@@ -74,6 +74,17 @@ pub fn part_one(input: &str) -> Option<u64> {
7474 Some ( min_cost)
7575}
7676
77+ #[ inline]
78+ fn dir_to_index ( dir : Direction ) -> usize {
79+ match dir {
80+ RIGHT => 0 ,
81+ DOWN => 1 ,
82+ LEFT => 2 ,
83+ UP => 3 ,
84+ _ => unreachable ! ( ) ,
85+ }
86+ }
87+
7788pub fn part_two ( input : & str ) -> Option < u64 > {
7889 let ( grid, start) = Grid :: new_from_str_capture_start ( input, & |c| c, & |c| c == 'S' ) ;
7990 let target_pos = grid
@@ -108,15 +119,8 @@ pub fn part_two(input: &str) -> Option<u64> {
108119 let mut best_spots_grid = base_false_grid. clone ( ) ;
109120 let mut best_target_cost = u64:: MAX ;
110121 let mut best_cost_grid = Grid :: new ( grid. width , grid. height , [ u64:: MAX ; 4 ] ) ;
111- let dir_to_index = |d : Direction | match d {
112- RIGHT => 0 ,
113- DOWN => 1 ,
114- LEFT => 2 ,
115- UP => 3 ,
116- _ => unreachable ! ( ) ,
117- } ;
118122
119- while let Some ( s) = q. pop_front ( ) {
123+ while let Some ( mut s) = q. pop_front ( ) {
120124 if s. pos == target_pos {
121125 if best_target_cost < s. cost {
122126 continue ;
@@ -139,36 +143,65 @@ pub fn part_two(input: &str) -> Option<u64> {
139143
140144 let right = s. dir . rotate_clockwise ( ) ;
141145 let left = s. dir . rotate_counterclockwise ( ) ;
142- for & ( pos, dir, cost) in [
143- ( s. pos + s. dir , s. dir , s. cost + 1 ) ,
144- ( s. pos + left, left, s. cost + 1000 + 1 ) ,
145- ( s. pos + right, right, s. cost + 1000 + 1 ) ,
146- ]
147- . iter ( )
148- {
146+ let is_viable = |& ( pos, dir, cost) | {
149147 if grid[ pos] == '#' {
150- continue ;
148+ return false ;
151149 }
152150 let best_cost_to_next_pos = best_cost_grid[ pos] [ dir_to_index ( dir) ] ;
153151 if best_cost_to_next_pos < cost {
154- continue ;
152+ return false ;
155153 }
156-
157154 if cost > best_target_cost {
158- continue ;
155+ return false ;
159156 }
157+ true
158+ } ;
160159
161- let mut new_path = path_pool. pull ( || Vec :: with_capacity ( 1024 ) ) ;
162- new_path. clear ( ) ;
163- new_path. extend ( s. path . iter ( ) ) ;
164- new_path. push ( pos) ;
165- let new_state = State {
166- path : new_path,
167- pos,
168- dir,
169- cost,
170- } ;
171- q. push_back ( new_state) ;
160+ let all_options = [
161+ ( s. pos + s. dir , s. dir , s. cost + 1 ) ,
162+ ( s. pos + left, left, s. cost + 1000 + 1 ) ,
163+ ( s. pos + right, right, s. cost + 1000 + 1 ) ,
164+ ] ;
165+
166+ let viable_options = [
167+ is_viable ( & all_options[ 0 ] ) ,
168+ is_viable ( & all_options[ 1 ] ) ,
169+ is_viable ( & all_options[ 2 ] ) ,
170+ ] ;
171+
172+ // fast path when there is only one option to avoid copying the path
173+ let viable_count = viable_options. iter ( ) . filter ( |& & b| b) . count ( ) ;
174+ match viable_count {
175+ 0 => continue ,
176+ 1 => {
177+ let idx = viable_options. iter ( ) . position ( |& b| b) . unwrap ( ) ;
178+ let ( pos, dir, cost) = all_options[ idx] ;
179+ s. path . push ( pos) ;
180+ s. cost = cost;
181+ s. pos = pos;
182+ s. dir = dir;
183+ q. push_back ( s) ;
184+ }
185+ _ => {
186+ for ( idx, & is_viable) in viable_options. iter ( ) . enumerate ( ) {
187+ if !is_viable {
188+ continue ;
189+ }
190+
191+ let ( pos, dir, cost) = all_options[ idx] ;
192+ let mut new_path = path_pool. pull ( || Vec :: with_capacity ( 1024 ) ) ;
193+ new_path. clear ( ) ;
194+ new_path. extend ( s. path . iter ( ) ) ;
195+ new_path. push ( pos) ;
196+ let new_state = State {
197+ path : new_path,
198+ pos,
199+ dir,
200+ cost,
201+ } ;
202+ q. push_back ( new_state) ;
203+ }
204+ }
172205 }
173206 }
174207
0 commit comments