@@ -74,6 +74,17 @@ pub fn part_one(input: &str) -> Option<u64> {
74
74
Some ( min_cost)
75
75
}
76
76
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
+
77
88
pub fn part_two ( input : & str ) -> Option < u64 > {
78
89
let ( grid, start) = Grid :: new_from_str_capture_start ( input, & |c| c, & |c| c == 'S' ) ;
79
90
let target_pos = grid
@@ -108,15 +119,8 @@ pub fn part_two(input: &str) -> Option<u64> {
108
119
let mut best_spots_grid = base_false_grid. clone ( ) ;
109
120
let mut best_target_cost = u64:: MAX ;
110
121
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
- } ;
118
122
119
- while let Some ( s) = q. pop_front ( ) {
123
+ while let Some ( mut s) = q. pop_front ( ) {
120
124
if s. pos == target_pos {
121
125
if best_target_cost < s. cost {
122
126
continue ;
@@ -139,36 +143,65 @@ pub fn part_two(input: &str) -> Option<u64> {
139
143
140
144
let right = s. dir . rotate_clockwise ( ) ;
141
145
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) | {
149
147
if grid[ pos] == '#' {
150
- continue ;
148
+ return false ;
151
149
}
152
150
let best_cost_to_next_pos = best_cost_grid[ pos] [ dir_to_index ( dir) ] ;
153
151
if best_cost_to_next_pos < cost {
154
- continue ;
152
+ return false ;
155
153
}
156
-
157
154
if cost > best_target_cost {
158
- continue ;
155
+ return false ;
159
156
}
157
+ true
158
+ } ;
160
159
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
+ }
172
205
}
173
206
}
174
207
0 commit comments