55
66import itertools
77import sys
8+ from functools import cache
89
910from aoc .common import InputData
1011from aoc .common import SolutionBase
1112from aoc .common import aoc_samples
12- from aoc .common import log
1313from aoc .graph import Dijkstra
1414from aoc .grid import Cell
1515
3232 ">" : [["v" ]],
3333 "v" : [["<" , "v" ], ["v" , "<" ]],
3434 "<" : [["v" , "<" , "<" ], ["<" , "v" , "<" ]],
35- "A" : [[ "" ] ],
35+ "A" : [],
3636 },
3737 "^" : {
38- "^" : [[ "" ] ],
38+ "^" : [],
3939 ">" : [["v" , ">" ], [">" , "v" ]],
4040 "v" : [["v" ]],
4141 "<" : [["v" , "<" ]],
4242 "A" : [[">" ]],
4343 },
4444 ">" : {
4545 "^" : [["<" , "^" ], ["^" , "<" ]],
46- ">" : [[ "" ] ],
46+ ">" : [],
4747 "v" : [["<" ]],
4848 "<" : [["<" , "<" ]],
4949 "A" : [["^" ]],
5050 },
5151 "v" : {
5252 "^" : [["^" ]],
5353 ">" : [[">" ]],
54- "v" : [[ "" ] ],
54+ "v" : [],
5555 "<" : [["<" ]],
5656 "A" : [[">" , "^" ], ["^" , ">" ]],
5757 },
5858 "<" : {
5959 "^" : [[">" , "^" ]],
6060 ">" : [[">" , ">" ]],
6161 "v" : [[">" ]],
62- "<" : [[ "" ] ],
62+ "<" : [],
6363 "A" : [[">" , ">" , "^" ], [">" , "^" , ">" ]],
6464 },
6565}
@@ -83,24 +83,29 @@ class Solution(SolutionBase[Input, Output1, Output2]):
8383 def parse_input (self , input_data : InputData ) -> Input :
8484 return input_data
8585
86- def do_dir_keypad (self , seq : list [str ]) -> list [list [str ]]:
87- ans = []
88-
89- def dfs (seq : list [str ], pos : int , path : list [str ]) -> None :
90- if pos == len (seq ):
91- ans .append (path )
92- return
93- prev , nxt = seq [pos - 1 ], seq [pos ]
94- for s in DIR_KEYPAD [prev ][nxt ]:
95- new_path = path [:]
96- if s != ["" ]:
97- for x in s :
98- new_path .append (x )
99- new_path .append ("A" )
100- dfs (seq , pos + 1 , new_path )
101-
102- dfs (["A" ] + seq , 1 , [])
103- return ans
86+ @cache
87+ def do_dir_keypad (self , seq : tuple [str , ...], level : int ) -> int :
88+ assert len (seq ) > 0
89+ if level == 1 :
90+ ans = 0
91+ for first , second in zip (("A" ,) + seq , seq ):
92+ moves = DIR_KEYPAD [first ][second ]
93+ ans += 1 if len (moves ) == 0 else len (moves [0 ] + ["A" ])
94+ return ans
95+ else :
96+ ans = 0
97+ for first , second in zip (("A" ,) + seq , seq ):
98+ moves = DIR_KEYPAD [first ][second ]
99+ if len (moves ) > 0 :
100+ ans += min (
101+ self .do_dir_keypad (
102+ tuple (_ for _ in move + ["A" ]), level - 1
103+ )
104+ for move in moves
105+ )
106+ else :
107+ ans += self .do_dir_keypad (("A" ,), level - 1 )
108+ return ans
104109
105110 def do_num_keypad (self , seq : str ) -> list [list [str ]]:
106111 lst = [_ for _ in seq ]
@@ -120,7 +125,6 @@ def do_num_keypad(self, seq: str) -> list[list[str]]:
120125 ),
121126 lambda curr , nxt : 1 ,
122127 )
123- # log((prev, nxt))
124128 paths = result .get_paths (end )
125129 moves = []
126130 for path in paths :
@@ -130,45 +134,30 @@ def do_num_keypad(self, seq: str) -> list[list[str]]:
130134 moves .append (move )
131135 all_moves .append (moves )
132136
133- # log(all_moves)
134137 tmp = [p for p in itertools .product (* all_moves )]
135- # log(tmp)
136138 for t in tmp :
137139 a = []
138140 for tt in t :
139141 x = list (tt ) + ["A" ]
140142 a .extend (x )
141143 if a is not None :
142144 ans .append (a ) # type:ignore
143- log (seq )
144- log (ans )
145145 return ans
146146
147- def solve_1 (self , input : str ) -> int :
147+ def solve (self , input : str , levels : int ) -> int :
148148 best = sys .maxsize
149149 seqs = self .do_num_keypad (input )
150- for seq1 in seqs :
151- seq2 = self .do_dir_keypad (seq1 )
152- seq3 = []
153- for seq in seq2 :
154- seq3 .extend (self .do_dir_keypad (seq ))
155- shortest = sorted (seq3 , key = len )[0 ]
156- if len (shortest ) < best :
157- best = len (shortest )
150+ for seq in seqs :
151+ best = min (best , self .do_dir_keypad (tuple (_ for _ in seq ), levels ))
158152 return best
159153
160154 def part_1 (self , input : Input ) -> Output1 :
161- return sum (self .solve_1 (combo ) * int (combo [:- 1 ]) for combo in input )
155+ return sum (self .solve (combo , 2 ) * int (combo [:- 1 ]) for combo in input )
162156
163157 def part_2 (self , input : Input ) -> Output2 :
164- return 0
165-
166- @aoc_samples (
167- (
168- ("part_1" , TEST , 126384 ),
169- # ("part_2", TEST, "TODO"),
170- )
171- )
158+ return sum (self .solve (combo , 25 ) * int (combo [:- 1 ]) for combo in input )
159+
160+ @aoc_samples ((("part_1" , TEST , 126384 ),))
172161 def samples (self ) -> None :
173162 pass
174163
0 commit comments