1
+ import networkx as nx
2
+ from itertools import product
3
+ from functools import cache
4
+
5
+ codes = """286A
6
+ 480A
7
+ 140A
8
+ 413A
9
+ 964A""" .split ("\n " )
10
+
11
+ kp_num = nx .grid_2d_graph (3 , 4 )
12
+ kp_num .remove_node ((0 , 0 ))
13
+
14
+ mapping = {
15
+ (0 , 1 ): "1" ,
16
+ (0 , 2 ): "4" ,
17
+ (0 , 3 ): "7" ,
18
+ (1 , 0 ): "0" ,
19
+ (1 , 1 ): "2" ,
20
+ (1 , 2 ): "5" ,
21
+ (1 , 3 ): "8" ,
22
+ (2 , 0 ): "A" ,
23
+ (2 , 1 ): "3" ,
24
+ (2 , 2 ): "6" ,
25
+ (2 , 3 ): "9"
26
+ }
27
+
28
+ kp_num = nx .relabel_nodes (kp_num , mapping )
29
+
30
+ kp_num_moves = {
31
+ ("A" , "0" ): "<" ,
32
+ ("0" , "A" ): ">" ,
33
+ ("A" , "3" ): "^" ,
34
+ ("3" , "A" ): "v" ,
35
+ ("0" , "2" ): "^" ,
36
+ ("2" , "0" ): "v" ,
37
+ ("3" , "2" ): "<" ,
38
+ ("2" , "3" ): ">" ,
39
+ ("3" , "6" ): "^" ,
40
+ ("6" , "3" ): "v" ,
41
+ ("2" , "1" ): "<" ,
42
+ ("1" , "2" ): ">" ,
43
+ ("2" , "5" ): "^" ,
44
+ ("5" , "2" ): "v" ,
45
+ ("1" , "4" ): "^" ,
46
+ ("4" , "1" ): "v" ,
47
+ ("6" , "5" ): "<" ,
48
+ ("5" , "6" ): ">" ,
49
+ ("6" , "9" ): "^" ,
50
+ ("9" , "6" ): "v" ,
51
+ ("5" , "4" ): "<" ,
52
+ ("4" , "5" ): ">" ,
53
+ ("5" , "8" ): "^" ,
54
+ ("8" , "5" ): "v" ,
55
+ ("4" , "7" ): "^" ,
56
+ ("7" , "4" ): "v" ,
57
+ ("9" , "8" ): "<" ,
58
+ ("8" , "9" ): ">" ,
59
+ ("8" , "7" ): "<" ,
60
+ ("7" , "8" ): ">"
61
+ }
62
+
63
+ kp_dir = nx .grid_2d_graph (3 , 2 )
64
+ kp_dir .remove_node ((0 , 1 ))
65
+
66
+ mapping = {
67
+ (0 , 0 ): "<" ,
68
+ (1 , 0 ): "v" ,
69
+ (2 , 0 ): ">" ,
70
+ (1 , 1 ): "^" ,
71
+ (2 , 1 ): "A"
72
+ }
73
+
74
+ kp_dir = nx .relabel_nodes (kp_dir , mapping )
75
+
76
+ kp_dir_moves = {
77
+ (">" , "v" ): "<" ,
78
+ ("v" , ">" ): ">" ,
79
+ (">" , "A" ): "^" ,
80
+ ("A" , ">" ): "v" ,
81
+ ("v" , "<" ): "<" ,
82
+ ("<" , "v" ): ">" ,
83
+ ("v" , "^" ): "^" ,
84
+ ("^" , "v" ): "v" ,
85
+ ("A" , "^" ): "<" ,
86
+ ("^" , "A" ): ">"
87
+ }
88
+
89
+
90
+ def get_num_inputs (output ):
91
+ inputs = []
92
+ for x , y in zip ("A" + output , output ):
93
+ sub_inputs = []
94
+ paths = nx .all_shortest_paths (kp_num , x , y )
95
+ for path in paths :
96
+ sub_inputs .append ("" .join (kp_num_moves [(u , v )] for u , v in zip (path , path [1 :])) + "A" )
97
+ inputs .append (sub_inputs )
98
+
99
+ return ['' .join (s ) for s in product (* inputs )]
100
+
101
+
102
+ def get_dir_inputs (output ):
103
+ inputs = []
104
+ for x , y in zip ("A" + output , output ):
105
+ sub_inputs = []
106
+ paths = nx .all_shortest_paths (kp_dir , x , y )
107
+ for path in paths :
108
+ sub_inputs .append ("" .join (kp_dir_moves [(u , v )] for u , v in zip (path , path [1 :])) + "A" )
109
+ inputs .append (sub_inputs )
110
+ return ['' .join (s ) for s in product (* inputs )]
111
+
112
+
113
+ @cache
114
+ def min_length (buttons , n ):
115
+ if n == 0 :
116
+ return len (buttons )
117
+ assert buttons [- 1 ] == "A"
118
+ if buttons .count ("A" ) > 1 :
119
+ ls = [x + "A" for x in buttons .split ("A" )][:- 1 ]
120
+ return sum (min_length (pt , n ) for pt in ls )
121
+ else :
122
+ return min (min_length (x , n - 1 ) for x in get_dir_inputs (buttons ))
123
+
124
+
125
+ def complexity (code , n ):
126
+ seqs = get_num_inputs (code )
127
+ min_len = min (min_length (seq , n ) for seq in seqs )
128
+ return min_len * int (code [:- 1 ])
129
+
130
+
131
+ # Part 1
132
+ print (sum (complexity (code , 2 ) for code in codes ))
133
+
134
+ # Part 2
135
+ print (sum (complexity (code , 25 ) for code in codes ))
0 commit comments