11use  crate :: solutions:: Solution ; 
2+ use  crate :: utils:: deltoid_surface:: DeltoidSurface ; 
23use  crate :: utils:: grid:: Grid ; 
34use  crate :: utils:: point:: Point ; 
45use  itertools:: Itertools ; 
@@ -9,18 +10,46 @@ pub struct Day20;
910
1011impl  Solution  for  Day20  { 
1112    fn  part_one ( & self ,  input :  & str )  -> String  { 
12-         self . cheats_in_range ( input,  100 ..) . to_string ( ) 
13+         self . part_one_cheats_in_range ( input,  100 ..) . to_string ( ) 
1314    } 
1415
15-     fn  part_two ( & self ,  _input :  & str )  -> String  { 
16-         String :: from ( '0' ) 
16+     fn  part_two ( & self ,  input :  & str )  -> String  { 
17+         // extract surface trait and contains method 
18+         // grid has function get every position in area 
19+         // rename surface range as rectangular area 
20+         // 
21+         self . part_two_cheats_in_range ( input,  100 ..) . to_string ( ) 
1722    } 
1823} 
1924
2025impl  Day20  { 
21-     fn  cheats_in_range < R > ( & self ,  input :  & str ,  range :  R )  -> usize 
26+     fn  part_one_cheats_in_range ( & self ,  input :  & str ,  range :  impl  RangeBounds < usize > )  -> usize  { 
27+         let  cheat_range_from_current = |current :  Point | { 
28+             current
29+                 . adjacent_vectors ( ) 
30+                 . map ( |v| v. forward ( ) . position ( ) ) 
31+                 . into_iter ( ) 
32+         } ; 
33+ 
34+         self . cheats_in_range ( input,  range,  & cheat_range_from_current) 
35+     } 
36+ 
37+     fn  part_two_cheats_in_range ( & self ,  input :  & str ,  range :  impl  RangeBounds < usize > )  -> usize  { 
38+         let  cheat_range_from_current =
39+             |current :  Point | DeltoidSurface :: new ( current,  20 ) . points ( ) . into_iter ( ) ; 
40+ 
41+         self . cheats_in_range ( input,  range,  & cheat_range_from_current) 
42+     } 
43+ 
44+     fn  cheats_in_range < R ,  I > ( 
45+         & self , 
46+         input :  & str , 
47+         range :  R , 
48+         cheat_positions :  & dyn  Fn ( Point )  -> I , 
49+     )  -> usize 
2250    where 
2351        R :  RangeBounds < usize > , 
52+         I :  Iterator < Item  = Point > , 
2453    { 
2554        let  grid:  Grid < char >  = Grid :: from ( input) ; 
2655        let  start = grid. get_first_position ( & 'S' ) . unwrap ( ) ; 
@@ -32,30 +61,30 @@ impl Day20 {
3261            . path ( ) 
3362            . iter ( ) 
3463            . flat_map ( |( current_time,  current) | { 
35-                 current
36-                     . adjacent_vectors ( ) 
37-                     . iter ( ) 
38-                     . filter ( |v| grid. is_for_point ( & v. position ( ) ,  '#' ) ) 
39-                     . map ( |p| p. forward ( ) ) 
64+                 cheat_positions ( * current) 
4065                    . filter ( |v| { 
41-                         grid. get_for_point ( & v . position ( ) ) 
66+                         grid. get_for_point ( v ) 
4267                            . is_some_and ( |element| [ '.' ,  'E' ] . contains ( element) ) 
4368                    } ) 
44-                     . filter_map ( |v| { 
45-                         if  let  Some ( time_after_cheat)  =
46-                             path_without_cheats. picoseconds_from ( v. position ( ) ) 
47-                         { 
48-                             if  time_after_cheat > * current_time { 
49-                                 return  Some ( time_after_cheat - current_time - 2 ) ; 
50-                                 // why -2 
69+                     . filter_map ( |cheat_position| { 
70+                         let  time_after_cheat = path_without_cheats
71+                             . picoseconds_from ( cheat_position) 
72+                             . unwrap ( ) ; 
73+                         let  cheat_cost = current. manhattan_distance ( & cheat_position)  as  usize ; 
74+ 
75+                         if  time_after_cheat > * current_time + cheat_cost { 
76+                             let  time = time_after_cheat - current_time - cheat_cost; 
77+                             if  range. contains ( & time)  { 
78+                                 return  Some ( time) ; 
5179                            } 
52-                         } 
5380
54-                         None 
81+                             None 
82+                         }  else  { 
83+                             None 
84+                         } 
5585                    } ) 
5686                    . collect_vec ( ) 
5787            } ) 
58-             . filter ( |time| range. contains ( time) ) 
5988            . count ( ) 
6089    } 
6190
@@ -126,17 +155,28 @@ mod tests {
126155###############"# ; 
127156
128157    #[ test]  
129-     fn  test_solve ( )  { 
130-         assert_eq ! ( 14 ,  Day20 . cheats_in_range( EXAMPLE ,  2 ..=2 ) ) ; 
131-         assert_eq ! ( 14 ,  Day20 . cheats_in_range( EXAMPLE ,  4 ..=4 ) ) ; 
132-         assert_eq ! ( 2 ,  Day20 . cheats_in_range( EXAMPLE ,  6 ..=6 ) ) ; 
133-         assert_eq ! ( 4 ,  Day20 . cheats_in_range( EXAMPLE ,  8 ..=8 ) ) ; 
134-         assert_eq ! ( 2 ,  Day20 . cheats_in_range( EXAMPLE ,  10 ..=10 ) ) ; 
135-         assert_eq ! ( 3 ,  Day20 . cheats_in_range( EXAMPLE ,  12 ..=12 ) ) ; 
136-         assert_eq ! ( 1 ,  Day20 . cheats_in_range( EXAMPLE ,  20 ..=20 ) ) ; 
137-         assert_eq ! ( 1 ,  Day20 . cheats_in_range( EXAMPLE ,  36 ..=36 ) ) ; 
138-         assert_eq ! ( 1 ,  Day20 . cheats_in_range( EXAMPLE ,  38 ..=38 ) ) ; 
139-         assert_eq ! ( 1 ,  Day20 . cheats_in_range( EXAMPLE ,  40 ..=40 ) ) ; 
140-         assert_eq ! ( 1 ,  Day20 . cheats_in_range( EXAMPLE ,  64 ..=64 ) ) ; 
158+     fn  part_one_cheats_in_range ( )  { 
159+         assert_eq ! ( 14 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  2 ..=2 ) ) ; 
160+         assert_eq ! ( 14 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  4 ..=4 ) ) ; 
161+         assert_eq ! ( 2 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  6 ..=6 ) ) ; 
162+         assert_eq ! ( 4 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  8 ..=8 ) ) ; 
163+         assert_eq ! ( 2 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  10 ..=10 ) ) ; 
164+         assert_eq ! ( 3 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  12 ..=12 ) ) ; 
165+         assert_eq ! ( 1 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  20 ..=20 ) ) ; 
166+         assert_eq ! ( 1 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  36 ..=36 ) ) ; 
167+         assert_eq ! ( 1 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  38 ..=38 ) ) ; 
168+         assert_eq ! ( 1 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  40 ..=40 ) ) ; 
169+         assert_eq ! ( 1 ,  Day20 . part_one_cheats_in_range( EXAMPLE ,  64 ..=64 ) ) ; 
170+     } 
171+ 
172+     #[ test]  
173+     fn  part_two_cheats_in_range ( )  { 
174+         assert_eq ! ( 32 ,  Day20 . part_two_cheats_in_range( EXAMPLE ,  50 ..=50 ) ) ; 
175+         assert_eq ! ( 31 ,  Day20 . part_two_cheats_in_range( EXAMPLE ,  52 ..=52 ) ) ; 
176+         assert_eq ! ( 29 ,  Day20 . part_two_cheats_in_range( EXAMPLE ,  54 ..=54 ) ) ; 
177+         assert_eq ! ( 39 ,  Day20 . part_two_cheats_in_range( EXAMPLE ,  56 ..=56 ) ) ; 
178+         assert_eq ! ( 25 ,  Day20 . part_two_cheats_in_range( EXAMPLE ,  58 ..=58 ) ) ; 
179+         assert_eq ! ( 23 ,  Day20 . part_two_cheats_in_range( EXAMPLE ,  60 ..=60 ) ) ; 
180+         assert_eq ! ( 20 ,  Day20 . part_two_cheats_in_range( EXAMPLE ,  62 ..=62 ) ) ; 
141181    } 
142182} 
0 commit comments