@@ -17,15 +17,23 @@ struct Racetrack {
1717 rows : Vec < Vec < char > > ,
1818}
1919
20+ #[ derive( Clone , Copy , Debug , Hash , PartialEq , Eq ) ]
21+ struct Cheat {
22+ start : Vec2 < i32 > ,
23+ end : Option < Vec2 < i32 > > ,
24+ picos_left : i32 ,
25+ }
26+
2027#[ derive( Clone , Copy , Debug , Hash , PartialEq , Eq ) ]
2128struct Node {
2229 pos : Vec2 < i32 > ,
2330 picos : i32 ,
31+ cheat : Option < Cheat > ,
2432}
2533
2634impl Ord for Node {
2735 fn cmp ( & self , other : & Self ) -> Ordering {
28- self . picos . cmp ( & other . picos )
36+ other . picos . cmp ( & self . picos ) // Intentionally reversed to make BinaryHeap behave like a min-heap
2937 }
3038}
3139
@@ -54,27 +62,40 @@ impl Racetrack {
5462 . find_map ( |( y, row) | row. iter ( ) . enumerate ( ) . find ( |( _, & cell) | cell == c) . map ( |( x, _) | Vec2 :: new ( x as i32 , y as i32 ) ) )
5563 }
5664
57- fn shortest_path ( & self , start : Vec2 < i32 > , end : Vec2 < i32 > ) -> Option < i32 > {
65+ fn shortest_path ( & self , start : Vec2 < i32 > , end : Vec2 < i32 > ) -> Option < Node > {
5866 // Your run-of-the-mill Dijkstra implementation
5967
6068 let mut queue = BinaryHeap :: new ( ) ;
6169 let mut visited = HashSet :: new ( ) ;
6270
63- queue. push ( Node { pos : start, picos : 0 } ) ;
64- visited. insert ( start) ;
71+ queue. push ( Node { pos : start, picos : 0 , cheat : None } ) ;
72+ visited. insert ( ( start, None ) ) ;
6573
6674 while let Some ( node) = queue. pop ( ) {
6775 if node. pos == end {
68- return Some ( node. picos ) ;
76+ return Some ( node) ;
6977 }
7078
7179 for dy in -1 ..=1 {
7280 for dx in -1 ..=1 {
7381 if ( dx != 0 ) ^ ( dy != 0 ) {
7482 let neigh = Vec2 :: new ( node. pos . x + dx, node. pos . y + dy) ;
75- if !visited. contains ( & neigh) && self . in_bounds ( neigh) && self [ neigh] != '#' {
76- visited. insert ( neigh) ;
77- queue. push ( Node { pos : neigh, picos : node. picos + 1 } ) ;
83+ if self . in_bounds ( neigh) {
84+ let is_wall = self [ neigh] == '#' ;
85+
86+ let can_cheat = node. cheat . map_or ( true , |c| c. picos_left > 0 ) ;
87+ 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 } )
91+ } else {
92+ node. cheat
93+ } ;
94+
95+ if !visited. contains ( & ( neigh, new_cheat) ) && ( !is_wall || can_cheat) {
96+ visited. insert ( ( neigh, new_cheat) ) ;
97+ queue. push ( Node { pos : neigh, picos : node. picos + 1 , cheat : new_cheat } ) ;
98+ }
7899 }
79100 }
80101 }
@@ -106,5 +127,5 @@ fn main() {
106127 let start = track. locate ( 'S' ) . unwrap ( ) ;
107128 let end = track. locate ( 'E' ) . unwrap ( ) ;
108129
109- println ! ( "Shortest: {}" , track. shortest_path( start, end) . unwrap( ) ) ;
130+ println ! ( "Shortest: {:? }" , track. shortest_path( start, end) . unwrap( ) ) ;
110131}
0 commit comments