@@ -43,6 +43,12 @@ impl PartialOrd for Node {
4343 }
4444}
4545
46+ #[ derive( Clone , Copy , Debug , Hash , PartialEq , Eq ) ]
47+ enum CheatPolicy {
48+ Allowed ,
49+ Forbidden ,
50+ }
51+
4652impl Racetrack {
4753 fn height ( & self ) -> i32 {
4854 self . rows . len ( ) as i32
@@ -62,18 +68,24 @@ impl Racetrack {
6268 . find_map ( |( y, row) | row. iter ( ) . enumerate ( ) . find ( |( _, & cell) | cell == c) . map ( |( x, _) | Vec2 :: new ( x as i32 , y as i32 ) ) )
6369 }
6470
65- fn shortest_path ( & self , start : Vec2 < i32 > , end : Vec2 < i32 > ) -> Option < Node > {
71+ /// Finds paths through the racetrack, ordered ascendingly by total picoseconds.
72+ fn find_paths ( & self , start : Vec2 < i32 > , end : Vec2 < i32 > , cheat_policy : CheatPolicy , condition : impl Fn ( Node ) -> bool ) -> Vec < Node > {
6673 // Your run-of-the-mill Dijkstra implementation
6774
6875 let mut queue = BinaryHeap :: new ( ) ;
6976 let mut visited = HashSet :: new ( ) ;
77+ let mut paths = Vec :: new ( ) ;
7078
7179 queue. push ( Node { pos : start, picos : 0 , cheat : None } ) ;
7280 visited. insert ( ( start, None ) ) ;
7381
7482 while let Some ( node) = queue. pop ( ) {
7583 if node. pos == end {
76- return Some ( node) ;
84+ // dbg!(paths.len(), node.picos);
85+ paths. push ( node) ;
86+ if !condition ( node) {
87+ break ;
88+ }
7789 }
7890
7991 for dy in -1 ..=1 {
@@ -83,11 +95,16 @@ impl Racetrack {
8395 if self . in_bounds ( neigh) {
8496 let is_wall = self [ neigh] == '#' ;
8597
86- let can_cheat = node. cheat . map_or ( true , |c| c. picos_left > 0 ) ;
98+ let cheats_allowed = cheat_policy == CheatPolicy :: Allowed ;
99+ let can_cheat = cheats_allowed && node. cheat . map_or ( true , |c| c. picos_left > 0 ) ;
87100 let new_cheat = if let Some ( cheat) = node. cheat {
88- Some ( Cheat { start : cheat. start , end : if cheat. picos_left == 1 { Some ( neigh) } else { cheat. end } , picos_left : ( cheat. picos_left - 1 ) . max ( 0 ) } )
89- } else if is_wall {
90- Some ( Cheat { start : neigh, end : None , picos_left : 1 } )
101+ let is_ending = cheat. picos_left == 1 ;
102+ if is_ending && is_wall {
103+ continue ;
104+ }
105+ Some ( Cheat { start : cheat. start , end : if is_ending { Some ( neigh) } else { cheat. end } , picos_left : ( cheat. picos_left - 1 ) . max ( 0 ) } )
106+ } else if cheats_allowed && is_wall {
107+ Some ( Cheat { start : node. pos , end : None , picos_left : 1 } )
91108 } else {
92109 node. cheat
93110 } ;
@@ -102,7 +119,7 @@ impl Racetrack {
102119 }
103120 }
104121
105- None
122+ paths
106123 }
107124}
108125
@@ -127,5 +144,13 @@ fn main() {
127144 let start = track. locate ( 'S' ) . unwrap ( ) ;
128145 let end = track. locate ( 'E' ) . unwrap ( ) ;
129146
130- println ! ( "Shortest: {:?}" , track. shortest_path( start, end) . unwrap( ) ) ;
147+ let shortest_path = track. find_paths ( start, end, CheatPolicy :: Forbidden , |_| false ) [ 0 ] ;
148+ let base_picos = shortest_path. picos ;
149+
150+ let paths = track. find_paths ( start, end, CheatPolicy :: Allowed , |_| true ) ;
151+ // let part1 = paths.len();
152+
153+ for p in paths. iter ( ) . take ( 20 ) . map ( |n| ( base_picos - n. picos , n. cheat ) ) {
154+ println ! ( "{p:?}" ) ;
155+ }
131156}
0 commit comments