@@ -9,6 +9,7 @@ use crate::solution::{AocError, Solution};
9
9
10
10
type Coords = ( isize , isize ) ;
11
11
type Keyboard = HashMap < Coords , char > ;
12
+ type Sequence = Vec < char > ;
12
13
13
14
#[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
14
15
enum Direction {
@@ -36,12 +37,12 @@ impl Direction {
36
37
}
37
38
}
38
39
39
- fn parse ( input : & str ) -> Result < Vec < ( Vec < char > , u32 ) > , AocError > {
40
+ fn parse ( input : & str ) -> Result < Vec < ( Sequence , u64 ) > , AocError > {
40
41
let codes = input
41
42
. lines ( )
42
43
. map ( |line| {
43
44
let numeric_part = & line[ 0 ..3 ] ;
44
- let numeric: u32 = numeric_part
45
+ let numeric: u64 = numeric_part
45
46
. parse ( )
46
47
. map_err ( |err| AocError :: parse ( numeric_part, err) ) ?;
47
48
let code = line. chars ( ) . collect ( ) ;
@@ -161,27 +162,7 @@ fn find_shortest(keys: &Keyboard, start: &Coords, end: &Coords) -> Vec<Vec<Direc
161
162
shortest
162
163
}
163
164
164
- fn combine_parts ( parts : & [ Vec < Vec < char > > ] ) -> Vec < Vec < char > > {
165
- let mut combinations = vec ! [ vec![ ] ] ;
166
-
167
- for group in parts {
168
- let mut new_combinations = Vec :: new ( ) ;
169
-
170
- for combo_so_far in & combinations {
171
- for option in group {
172
- let mut combined = combo_so_far. clone ( ) ;
173
- combined. extend_from_slice ( option) ;
174
- new_combinations. push ( combined) ;
175
- }
176
- }
177
-
178
- combinations = new_combinations;
179
- }
180
-
181
- combinations
182
- }
183
-
184
- fn complexity ( shortest : u32 , numeric_part : u32 ) -> u32 {
165
+ fn complexity ( shortest : u64 , numeric_part : u64 ) -> u64 {
185
166
shortest * numeric_part
186
167
}
187
168
@@ -190,29 +171,85 @@ impl Robot {
190
171
Self { keys, current }
191
172
}
192
173
193
- fn find_inputs_to_produce ( & self , sequence : & [ char ] ) -> Vec < Vec < char > > {
174
+ fn find_inputs_to_produce (
175
+ & self ,
176
+ sequence : & [ char ] ,
177
+ cache : & mut HashMap < ( char , char ) , Vec < Sequence > > ,
178
+ ) -> Vec < Vec < Sequence > > {
194
179
let mut parts = vec ! [ ] ;
195
180
let mut current = self . current ;
196
181
197
182
for button in sequence {
198
- let ( target, _key) = self . keys . iter ( ) . find ( |( _, key) | * key == button) . unwrap ( ) ;
199
- let shortest_paths = find_shortest ( & self . keys , & current, target) ;
200
-
201
- let part = shortest_paths
202
- . iter ( )
203
- . map ( |shortest| convert_to_directional ( shortest) )
204
- . collect ( ) ;
183
+ let ( target, target_key) = self . keys . iter ( ) . find ( |( _, key) | * key == button) . unwrap ( ) ;
184
+ let part = if let Some ( cached) = cache. get ( & ( self . keys [ & current] , * target_key) ) {
185
+ cached. clone ( )
186
+ } else {
187
+ let shortest_paths = find_shortest ( & self . keys , & current, target) ;
188
+
189
+ shortest_paths
190
+ . iter ( )
191
+ . map ( |shortest| convert_to_directional ( shortest) )
192
+ . collect ( )
193
+ } ;
205
194
206
195
parts. push ( part) ;
207
196
208
197
current = * target;
209
198
}
210
199
211
- combine_parts ( & parts)
200
+ parts
212
201
}
213
202
}
214
203
215
- fn convert_to_directional ( shortest : & [ Direction ] ) -> Vec < char > {
204
+ fn recursive (
205
+ keys : & HashMap < Coords , char > ,
206
+ sequence : & [ char ] ,
207
+ remaining_robots : u32 ,
208
+ cache : & mut HashMap < ( String , u32 ) , u64 > ,
209
+ shortest_cache : & mut HashMap < ( char , char ) , Vec < Sequence > > ,
210
+ ) -> u64 {
211
+ let sequence_key: String = sequence. iter ( ) . collect ( ) ;
212
+ if let Some ( cached) = cache. get ( & ( sequence_key. clone ( ) , remaining_robots) ) {
213
+ return * cached;
214
+ }
215
+
216
+ let mut current = ( 2 , 0 ) ;
217
+ let mut count = 0 ;
218
+
219
+ for button in sequence {
220
+ let ( target, target_key) = keys. iter ( ) . find ( |( _, key) | * key == button) . unwrap ( ) ;
221
+ let possible = if let Some ( cached) = shortest_cache. get ( & ( keys[ & current] , * target_key) ) {
222
+ cached. clone ( )
223
+ } else {
224
+ let shortest_paths = find_shortest ( keys, & current, target) ;
225
+
226
+ shortest_paths
227
+ . iter ( )
228
+ . map ( |shortest| convert_to_directional ( shortest) )
229
+ . collect ( )
230
+ } ;
231
+
232
+ count += possible
233
+ . iter ( )
234
+ . map ( |part| {
235
+ if remaining_robots > 1 {
236
+ recursive ( keys, part, remaining_robots - 1 , cache, shortest_cache)
237
+ } else {
238
+ part. len ( ) as u64
239
+ }
240
+ } )
241
+ . min ( )
242
+ . unwrap ( ) ;
243
+
244
+ current = * target;
245
+ }
246
+
247
+ cache. insert ( ( sequence_key, remaining_robots) , count) ;
248
+
249
+ count
250
+ }
251
+
252
+ fn convert_to_directional ( shortest : & [ Direction ] ) -> Sequence {
216
253
let mut input = vec ! [ ] ;
217
254
for direction in shortest {
218
255
input. push ( match direction {
@@ -226,51 +263,73 @@ fn convert_to_directional(shortest: &[Direction]) -> Vec<char> {
226
263
input
227
264
}
228
265
229
- fn find_shortest_inputs ( sequence : & [ char ] ) -> Vec < char > {
266
+ fn find_shortest_inputs (
267
+ sequence : & [ char ] ,
268
+ robots : u32 ,
269
+ cache : & mut HashMap < ( String , u32 ) , u64 > ,
270
+ shortest_cache : & mut HashMap < ( char , char ) , Vec < Sequence > > ,
271
+ ) -> u64 {
230
272
let ( numpad, numpad_start) = create_numerical_keypad ( ) ;
231
- let ( keypad, keypad_start ) = create_directional_keypad ( ) ;
273
+ let ( keypad, _ ) = create_directional_keypad ( ) ;
232
274
233
275
let numpad_robot = Robot :: new ( numpad, numpad_start) ;
234
- let keypad_robot_1 = Robot :: new ( keypad. clone ( ) , keypad_start) ;
235
- let keypad_robot_2 = Robot :: new ( keypad, keypad_start) ;
276
+ let numpad_inputs = numpad_robot. find_inputs_to_produce ( sequence, shortest_cache) ;
236
277
237
- let numpad_inputs = numpad_robot. find_inputs_to_produce ( sequence) ;
238
-
239
- let shortest: Vec < _ > = numpad_inputs
278
+ let shortest = numpad_inputs
240
279
. iter ( )
241
- . flat_map ( |numpad_input| keypad_robot_1. find_inputs_to_produce ( numpad_input) )
242
- . flat_map ( |keypad_input| keypad_robot_2. find_inputs_to_produce ( & keypad_input) )
243
- . min_by ( |a, b| a. len ( ) . cmp ( & b. len ( ) ) )
244
- . unwrap_or ( Vec :: new ( ) ) ;
280
+ . map ( |inputs| {
281
+ inputs
282
+ . iter ( )
283
+ . map ( |input| recursive ( & keypad, input, robots, cache, shortest_cache) )
284
+ . min ( )
285
+ . unwrap ( )
286
+ } )
287
+ . sum ( ) ;
245
288
246
289
shortest
247
290
}
248
291
249
292
pub struct Day21 ;
250
293
impl Solution for Day21 {
251
- type A = u32 ;
252
- type B = u32 ;
294
+ type A = u64 ;
295
+ type B = u64 ;
253
296
254
297
fn default_input ( & self ) -> & ' static str {
255
298
include_str ! ( "../../../inputs/2024/day21.txt" )
256
299
}
257
300
258
- fn part_1 ( & self , input : & str ) -> Result < u32 , AocError > {
301
+ fn part_1 ( & self , input : & str ) -> Result < u64 , AocError > {
259
302
let codes = parse ( input) ?;
260
303
304
+ let mut cache = HashMap :: new ( ) ;
305
+ let mut shortest_cache: HashMap < ( char , char ) , Vec < Sequence > > = HashMap :: new ( ) ;
306
+
261
307
let complexities = codes
262
308
. into_iter ( )
263
309
. map ( |( code, numeric_part) | {
264
- let shortest = find_shortest_inputs ( & code) . len ( ) as u32 ;
310
+ let shortest = find_shortest_inputs ( & code, 2 , & mut cache , & mut shortest_cache ) ;
265
311
complexity ( shortest, numeric_part)
266
312
} )
267
313
. sum ( ) ;
268
314
269
315
Ok ( complexities)
270
316
}
271
317
272
- fn part_2 ( & self , input : & str ) -> Result < u32 , AocError > {
273
- unimplemented ! ( )
318
+ fn part_2 ( & self , input : & str ) -> Result < u64 , AocError > {
319
+ let codes = parse ( input) ?;
320
+
321
+ let mut cache = HashMap :: new ( ) ;
322
+ let mut shortest_cache: HashMap < ( char , char ) , Vec < Sequence > > = HashMap :: new ( ) ;
323
+
324
+ let complexities = codes
325
+ . into_iter ( )
326
+ . map ( |( code, numeric_part) | {
327
+ let shortest = find_shortest_inputs ( & code, 25 , & mut cache, & mut shortest_cache) ;
328
+ complexity ( shortest, numeric_part)
329
+ } )
330
+ . sum ( ) ;
331
+
332
+ Ok ( complexities)
274
333
}
275
334
}
276
335
@@ -291,44 +350,144 @@ mod tests {
291
350
assert_eq ! ( Day21 . part_1( INPUT ) , Ok ( 126384 ) ) ;
292
351
}
293
352
353
+ #[ test]
354
+ fn it_solves_part2_real ( ) {
355
+ assert_eq ! ( Day21 . part_2( Day21 . default_input( ) ) , Ok ( 167538833832712 ) ) ;
356
+ }
357
+
294
358
#[ test]
295
359
fn it_finds_shortest_numpad ( ) {
296
- let ( keypad, a) = create_numerical_keypad ( ) ;
360
+ let ( numpad, a) = create_numerical_keypad ( ) ;
361
+ let robot = Robot :: new ( numpad, a) ;
362
+ let mut cache: HashMap < ( char , char ) , Vec < Sequence > > = HashMap :: new ( ) ;
363
+
364
+ assert_eq ! (
365
+ robot. find_inputs_to_produce( & [ '0' , '2' , '9' , 'A' ] , & mut cache) ,
366
+ vec![
367
+ vec![ vec![ '<' , 'A' ] ] , // A - 0
368
+ vec![ vec![ '^' , 'A' ] ] , // 0 - 2
369
+ vec![
370
+ // 2 - 9
371
+ vec![ '^' , '>' , '^' , 'A' ] ,
372
+ vec![ '^' , '^' , '>' , 'A' ] ,
373
+ vec![ '>' , '^' , '^' , 'A' ]
374
+ ] ,
375
+ vec![ vec![ 'v' , 'v' , 'v' , 'A' ] ] // 9 - A
376
+ ]
377
+ ) ;
378
+ }
297
379
380
+ #[ test]
381
+ fn it_finds_shortest_keypad ( ) {
382
+ let ( keypad, a) = create_directional_keypad ( ) ;
383
+ let robot = Robot :: new ( keypad, a) ;
384
+ let mut cache: HashMap < ( char , char ) , Vec < Sequence > > = HashMap :: new ( ) ;
385
+
386
+ assert_eq ! (
387
+ robot. find_inputs_to_produce( & [ '<' , 'A' ] , & mut cache) ,
388
+ vec![
389
+ vec![ vec![ 'v' , '<' , '<' , 'A' ] , vec![ '<' , 'v' , '<' , 'A' ] ] , // A - <
390
+ vec![ vec![ '>' , '^' , '>' , 'A' ] , vec![ '>' , '>' , '^' , 'A' ] ] // < - A
391
+ ]
392
+ ) ;
393
+ }
394
+
395
+ #[ test]
396
+ fn it_finds_shortest_keypad_2 ( ) {
397
+ let ( keypad, a) = create_directional_keypad ( ) ;
298
398
let robot = Robot :: new ( keypad, a) ;
399
+ let mut cache: HashMap < ( char , char ) , Vec < Sequence > > = HashMap :: new ( ) ;
299
400
300
401
assert_eq ! (
301
- robot. find_inputs_to_produce( & [ '0 ' , '2 ' , '9 ' , 'A' ] ) ,
402
+ robot. find_inputs_to_produce( & [ '^ ' , '> ' , '^ ' , 'A' ] , & mut cache ) ,
302
403
vec![
303
- "<A^A^>^AvvvA" . chars( ) . collect:: <Vec <_>>( ) ,
304
- "<A^A^^>AvvvA" . chars( ) . collect:: <Vec <_>>( ) ,
305
- "<A^A>^^AvvvA" . chars( ) . collect:: <Vec <_>>( ) ,
404
+ vec![ vec![ '<' , 'A' ] ] , // A - ^
405
+ vec![ vec![ '>' , 'v' , 'A' ] , vec![ 'v' , '>' , 'A' ] ] , // ^ - >
406
+ vec![ vec![ '^' , '<' , 'A' ] , vec![ '<' , '^' , 'A' ] ] , // > - ^
407
+ vec![ vec![ '>' , 'A' ] ] // ^ - A
408
+ ]
409
+ ) ;
410
+ }
411
+
412
+ #[ test]
413
+ fn it_finds_shortest_keypad_3 ( ) {
414
+ let ( keypad, a) = create_directional_keypad ( ) ;
415
+ let robot = Robot :: new ( keypad, a) ;
416
+ let mut cache: HashMap < ( char , char ) , Vec < Sequence > > = HashMap :: new ( ) ;
417
+
418
+ assert_eq ! (
419
+ robot. find_inputs_to_produce( & [ '^' , '^' , '>' , 'A' ] , & mut cache) ,
420
+ vec![
421
+ vec![ vec![ '<' , 'A' ] ] , // A - ^
422
+ vec![ vec![ 'A' ] ] , // ^ - ^
423
+ vec![ vec![ '>' , 'v' , 'A' ] , vec![ 'v' , '>' , 'A' ] ] , // ^ - >
424
+ vec![ vec![ '^' , 'A' ] ] // > - A
306
425
]
307
426
) ;
308
427
}
309
428
310
429
#[ test]
311
430
fn it_finds_min_inputs_length_1 ( ) {
312
- assert_eq ! ( find_shortest_inputs( & [ '0' , '2' , '9' , 'A' ] ) . len( ) , 68 ) ;
431
+ assert_eq ! (
432
+ find_shortest_inputs(
433
+ & [ '0' , '2' , '9' , 'A' ] ,
434
+ 2 ,
435
+ & mut HashMap :: new( ) ,
436
+ & mut HashMap :: new( )
437
+ ) ,
438
+ 68
439
+ ) ;
313
440
}
314
441
315
442
#[ test]
316
443
fn it_finds_min_inputs_length_2 ( ) {
317
- assert_eq ! ( find_shortest_inputs( & [ '9' , '8' , '0' , 'A' ] ) . len( ) , 60 ) ;
444
+ assert_eq ! (
445
+ find_shortest_inputs(
446
+ & [ '9' , '8' , '0' , 'A' ] ,
447
+ 2 ,
448
+ & mut HashMap :: new( ) ,
449
+ & mut HashMap :: new( )
450
+ ) ,
451
+ 60
452
+ ) ;
318
453
}
319
454
320
455
#[ test]
321
456
fn it_finds_min_inputs_length_3 ( ) {
322
- assert_eq ! ( find_shortest_inputs( & [ '1' , '7' , '9' , 'A' ] ) . len( ) , 68 ) ;
457
+ assert_eq ! (
458
+ find_shortest_inputs(
459
+ & [ '1' , '7' , '9' , 'A' ] ,
460
+ 2 ,
461
+ & mut HashMap :: new( ) ,
462
+ & mut HashMap :: new( )
463
+ ) ,
464
+ 68
465
+ ) ;
323
466
}
324
467
325
468
#[ test]
326
469
fn it_finds_min_inputs_length_4 ( ) {
327
- assert_eq ! ( find_shortest_inputs( & [ '4' , '5' , '6' , 'A' ] ) . len( ) , 64 ) ;
470
+ assert_eq ! (
471
+ find_shortest_inputs(
472
+ & [ '4' , '5' , '6' , 'A' ] ,
473
+ 2 ,
474
+ & mut HashMap :: new( ) ,
475
+ & mut HashMap :: new( )
476
+ ) ,
477
+ 64
478
+ ) ;
328
479
}
329
480
330
481
#[ test]
331
482
fn it_finds_min_inputs_length_5 ( ) {
332
- assert_eq ! ( find_shortest_inputs( & [ '3' , '7' , '9' , 'A' ] ) . len( ) , 64 ) ;
483
+ assert_eq ! (
484
+ find_shortest_inputs(
485
+ & [ '3' , '7' , '9' , 'A' ] ,
486
+ 2 ,
487
+ & mut HashMap :: new( ) ,
488
+ & mut HashMap :: new( )
489
+ ) ,
490
+ 64
491
+ ) ;
333
492
}
334
493
}
0 commit comments