@@ -9,6 +9,7 @@ use crate::solution::{AocError, Solution};
99
1010type Coords = ( isize , isize ) ;
1111type Keyboard = HashMap < Coords , char > ;
12+ type Sequence = Vec < char > ;
1213
1314#[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
1415enum Direction {
@@ -36,12 +37,12 @@ impl Direction {
3637 }
3738}
3839
39- fn parse ( input : & str ) -> Result < Vec < ( Vec < char > , u32 ) > , AocError > {
40+ fn parse ( input : & str ) -> Result < Vec < ( Sequence , u64 ) > , AocError > {
4041 let codes = input
4142 . lines ( )
4243 . map ( |line| {
4344 let numeric_part = & line[ 0 ..3 ] ;
44- let numeric: u32 = numeric_part
45+ let numeric: u64 = numeric_part
4546 . parse ( )
4647 . map_err ( |err| AocError :: parse ( numeric_part, err) ) ?;
4748 let code = line. chars ( ) . collect ( ) ;
@@ -161,27 +162,7 @@ fn find_shortest(keys: &Keyboard, start: &Coords, end: &Coords) -> Vec<Vec<Direc
161162 shortest
162163}
163164
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 {
185166 shortest * numeric_part
186167}
187168
@@ -190,29 +171,85 @@ impl Robot {
190171 Self { keys, current }
191172 }
192173
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 > > {
194179 let mut parts = vec ! [ ] ;
195180 let mut current = self . current ;
196181
197182 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+ } ;
205194
206195 parts. push ( part) ;
207196
208197 current = * target;
209198 }
210199
211- combine_parts ( & parts)
200+ parts
212201 }
213202}
214203
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 {
216253 let mut input = vec ! [ ] ;
217254 for direction in shortest {
218255 input. push ( match direction {
@@ -226,51 +263,73 @@ fn convert_to_directional(shortest: &[Direction]) -> Vec<char> {
226263 input
227264}
228265
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 {
230272 let ( numpad, numpad_start) = create_numerical_keypad ( ) ;
231- let ( keypad, keypad_start ) = create_directional_keypad ( ) ;
273+ let ( keypad, _ ) = create_directional_keypad ( ) ;
232274
233275 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) ;
236277
237- let numpad_inputs = numpad_robot. find_inputs_to_produce ( sequence) ;
238-
239- let shortest: Vec < _ > = numpad_inputs
278+ let shortest = numpad_inputs
240279 . 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 ( ) ;
245288
246289 shortest
247290}
248291
249292pub struct Day21 ;
250293impl Solution for Day21 {
251- type A = u32 ;
252- type B = u32 ;
294+ type A = u64 ;
295+ type B = u64 ;
253296
254297 fn default_input ( & self ) -> & ' static str {
255298 include_str ! ( "../../../inputs/2024/day21.txt" )
256299 }
257300
258- fn part_1 ( & self , input : & str ) -> Result < u32 , AocError > {
301+ fn part_1 ( & self , input : & str ) -> Result < u64 , AocError > {
259302 let codes = parse ( input) ?;
260303
304+ let mut cache = HashMap :: new ( ) ;
305+ let mut shortest_cache: HashMap < ( char , char ) , Vec < Sequence > > = HashMap :: new ( ) ;
306+
261307 let complexities = codes
262308 . into_iter ( )
263309 . 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 ) ;
265311 complexity ( shortest, numeric_part)
266312 } )
267313 . sum ( ) ;
268314
269315 Ok ( complexities)
270316 }
271317
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)
274333 }
275334}
276335
@@ -291,44 +350,144 @@ mod tests {
291350 assert_eq ! ( Day21 . part_1( INPUT ) , Ok ( 126384 ) ) ;
292351 }
293352
353+ #[ test]
354+ fn it_solves_part2_real ( ) {
355+ assert_eq ! ( Day21 . part_2( Day21 . default_input( ) ) , Ok ( 167538833832712 ) ) ;
356+ }
357+
294358 #[ test]
295359 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+ }
297379
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 ( ) ;
298398 let robot = Robot :: new ( keypad, a) ;
399+ let mut cache: HashMap < ( char , char ) , Vec < Sequence > > = HashMap :: new ( ) ;
299400
300401 assert_eq ! (
301- robot. find_inputs_to_produce( & [ '0 ' , '2 ' , '9 ' , 'A' ] ) ,
402+ robot. find_inputs_to_produce( & [ '^ ' , '> ' , '^ ' , 'A' ] , & mut cache ) ,
302403 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
306425 ]
307426 ) ;
308427 }
309428
310429 #[ test]
311430 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+ ) ;
313440 }
314441
315442 #[ test]
316443 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+ ) ;
318453 }
319454
320455 #[ test]
321456 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+ ) ;
323466 }
324467
325468 #[ test]
326469 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+ ) ;
328479 }
329480
330481 #[ test]
331482 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+ ) ;
333492 }
334493}
0 commit comments