@@ -4,81 +4,89 @@ namespace AdventOfCode.Y2024.Day21;
4
4
using System . Collections . Generic ;
5
5
using System . Diagnostics ;
6
6
using System . Linq ;
7
- using System . Numerics ;
8
- using AngleSharp . Common ;
9
- using Cache = System . Collections . Concurrent . ConcurrentDictionary < ( char , char , int ) , long > ;
10
- using Keypad = System . Collections . Generic . Dictionary < System . Numerics . Complex , char > ;
11
- record struct Vec2 ( long x , long y ) ;
12
7
8
+ using Cache = System . Collections . Concurrent . ConcurrentDictionary < ( char currentKey , char nextKey , int depth ) , long > ;
9
+ using Keypad = System . Collections . Generic . Dictionary < Vec2 , char > ;
10
+ record struct Vec2 ( int x , int y ) ;
13
11
14
12
[ ProblemName ( "Keypad Conundrum" ) ]
15
13
class Solution : Solver {
16
14
17
- public object PartOne ( string input ) {
18
- return input . Split ( "\n " ) . Sum ( line => Solve2 ( line , 2 ) ) ;
19
- }
20
- public object PartTwo ( string input ) {
21
- return input . Split ( "\n " ) . Sum ( line => Solve2 ( line , 25 ) ) ;
22
- }
23
-
24
- static readonly Complex Right = 1 ;
25
- static readonly Complex Up = Complex . ImaginaryOne ;
26
- static readonly Complex Down = - Complex . ImaginaryOne ;
27
-
28
- long Solve2 ( string line , int depth ) {
15
+ public object PartOne ( string input ) => Solve ( input , 2 ) ;
16
+ public object PartTwo ( string input ) => Solve ( input , 25 ) ;
17
+ long Solve ( string input , int depth ) {
29
18
var keypad1 = ParseKeypad ( "789\n 456\n 123\n 0A" ) ;
30
19
var keypad2 = ParseKeypad ( " ^A\n <v>" ) ;
31
20
var keypads = Enumerable . Repeat ( keypad2 , depth ) . Prepend ( keypad1 ) . ToArray ( ) ;
32
- var res = EncodeString ( line , keypads , new Cache ( ) ) ;
33
- return res * int . Parse ( line . Substring ( 0 , line . Length - 1 ) ) ;
21
+
22
+ var cache = new Cache ( ) ;
23
+ var res = 0L ;
24
+
25
+ foreach ( var line in input . Split ( "\n " ) ) {
26
+ var num = int . Parse ( line [ ..^ 1 ] ) ;
27
+ res += num * EncodeKeys ( line , keypads , cache ) ;
28
+ }
29
+ return res ;
34
30
}
35
31
36
- long EncodeString ( string st , Keypad [ ] keypads , Cache cache ) {
32
+ // Determines the length of the shortest sequence that is needed to enter the given
33
+ // keys. An empty keypad array means that the sequence is simply entered by a human
34
+ // and no further encoding is needed. Otherwise the sequence is entered by a robot
35
+ // which needs to be programmed. In practice this means that the keys are encoded
36
+ // using the robots keypad (the first keypad), generating an other sequence of keys.
37
+ // This other sequence is then recursively encoded using the rest of the keypads.
38
+ long EncodeKeys ( string keys , Keypad [ ] keypads , Cache cache ) {
37
39
if ( keypads . Length == 0 ) {
38
- return st . Length ;
40
+ return keys . Length ;
39
41
} else {
40
-
41
- // the robot starts and finishes by pointing to 'A' key
42
+ // invariant: the robot starts and finishes by pointing at the 'A' key
42
43
var currentKey = 'A' ;
43
44
var length = 0L ;
44
- foreach ( var nextKey in st ) {
45
+
46
+ foreach ( var nextKey in keys ) {
45
47
length += EncodeKey ( currentKey , nextKey , keypads , cache ) ;
48
+ // while the sequence is entered the current key changes accordingly
46
49
currentKey = nextKey ;
47
50
}
48
- Debug . Assert ( st . Last ( ) == 'A' , "The robot should point to the 'A' key" ) ;
51
+
52
+ // at the end the current key should be reset to 'A'
53
+ Debug . Assert ( currentKey == 'A' , "The robot should point at the 'A' key" ) ;
49
54
return length ;
50
55
}
51
56
}
52
- long EncodeKey ( char currentKey , char nextKey , Keypad [ ] keypads , Cache cache ) {
53
- return cache . GetOrAdd ( ( currentKey , nextKey , keypads . Length ) , _ => {
54
- var currentPos = keypads [ 0 ] . Single ( kvp => kvp . Value == currentKey ) . Key ;
55
- var nextPos = keypads [ 0 ] . Single ( kvp => kvp . Value == nextKey ) . Key ;
57
+ long EncodeKey ( char currentKey , char nextKey , Keypad [ ] keypads , Cache cache ) =>
58
+ cache . GetOrAdd ( ( currentKey , nextKey , keypads . Length ) , _ => {
59
+ var keypad = keypads [ 0 ] ;
56
60
57
- var dy = ( int ) ( nextPos . Imaginary - currentPos . Imaginary ) ;
58
- var dx = ( int ) ( nextPos . Real - currentPos . Real ) ;
61
+ var currentPos = keypad . Single ( kvp => kvp . Value == currentKey ) . Key ;
62
+ var nextPos = keypad . Single ( kvp => kvp . Value == nextKey ) . Key ;
59
63
60
- var vert = new string ( dy < 0 ? 'v' : '^' , Math . Abs ( dy ) ) ;
61
- var horiz = new string ( dx < 0 ? '< ' : '> ' , Math . Abs ( dx ) ) ;
64
+ var dy = nextPos . y - currentPos . y ;
65
+ var vert = new string ( dy < 0 ? 'v ' : '^ ' , Math . Abs ( dy ) ) ;
62
66
63
- var cost = long . MaxValue ;
67
+ var dx = nextPos . x - currentPos . x ;
68
+ var horiz = new string ( dx < 0 ? '<' : '>' , Math . Abs ( dx ) ) ;
64
69
65
- if ( keypads [ 0 ] [ currentPos + dy * Up ] != ' ' ) {
66
- cost = Math . Min ( cost , EncodeString ( $ "{ vert } { horiz } A", keypads [ 1 ..] , cache ) ) ;
67
- }
68
-
69
- if ( keypads [ 0 ] [ currentPos + dx * Right ] != ' ' ) {
70
- cost = Math . Min ( cost , EncodeString ( $ "{ horiz } { vert } A", keypads [ 1 ..] , cache ) ) ;
71
- }
72
- return cost ;
73
- } ) ;
74
- }
70
+ var cost = long . MaxValue ;
71
+ // we can usually go vertical first then horizontal or vica versa,
72
+ // but we should check for the extra condition and don't position
73
+ // the robot over the ' ' key:
74
+ if ( keypad [ new Vec2 ( currentPos . x , nextPos . y ) ] != ' ' ) {
75
+ cost = Math . Min ( cost , EncodeKeys ( $ "{ vert } { horiz } A", keypads [ 1 ..] , cache ) ) ;
76
+ }
77
+
78
+ if ( keypad [ new Vec2 ( nextPos . x , currentPos . y ) ] != ' ' ) {
79
+ cost = Math . Min ( cost , EncodeKeys ( $ "{ horiz } { vert } A", keypads [ 1 ..] , cache ) ) ;
80
+ }
81
+ return cost ;
82
+ } ) ;
75
83
76
84
Keypad ParseKeypad ( string keypad ) {
77
85
var lines = keypad . Split ( "\n " ) ;
78
86
return (
79
87
from y in Enumerable . Range ( 0 , lines . Length )
80
88
from x in Enumerable . Range ( 0 , lines [ 0 ] . Length )
81
- select new KeyValuePair < Complex , char > ( x + y * Down , lines [ y ] [ x ] )
89
+ select new KeyValuePair < Vec2 , char > ( new Vec2 ( x , - y ) , lines [ y ] [ x ] )
82
90
) . ToDictionary ( ) ;
83
91
}
84
92
}
0 commit comments