Skip to content

Commit d6020f7

Browse files
committed
AoC 2024 Day 21 Part 1 [very slow]
1 parent 337f4cd commit d6020f7

File tree

1 file changed

+184
-0
lines changed

1 file changed

+184
-0
lines changed

src/main/python/AoC2024_21.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#! /usr/bin/env python3
2+
#
3+
# Advent of Code 2024 Day 21
4+
#
5+
6+
import itertools
7+
import sys
8+
9+
from aoc.common import InputData
10+
from aoc.common import SolutionBase
11+
from aoc.common import aoc_samples
12+
from aoc.common import log
13+
from aoc.graph import Dijkstra
14+
from aoc.grid import Cell
15+
16+
Input = InputData
17+
Output1 = int
18+
Output2 = int
19+
20+
21+
TEST = """\
22+
029A
23+
980A
24+
179A
25+
456A
26+
379A
27+
"""
28+
29+
DIR_KEYPAD = {
30+
"A": {
31+
"^": [["<"]],
32+
">": [["v"]],
33+
"v": [["<", "v"], ["v", "<"]],
34+
"<": [["v", "<", "<"], ["<", "v", "<"]],
35+
"A": [[""]],
36+
},
37+
"^": {
38+
"^": [[""]],
39+
">": [["v", ">"], [">", "v"]],
40+
"v": [["v"]],
41+
"<": [["v", "<"]],
42+
"A": [[">"]],
43+
},
44+
">": {
45+
"^": [["<", "^"], ["^", "<"]],
46+
">": [[""]],
47+
"v": [["<"]],
48+
"<": [["<", "<"]],
49+
"A": [["^"]],
50+
},
51+
"v": {
52+
"^": [["^"]],
53+
">": [[">"]],
54+
"v": [[""]],
55+
"<": [["<"]],
56+
"A": [[">", "^"], ["^", ">"]],
57+
},
58+
"<": {
59+
"^": [[">", "^"]],
60+
">": [[">", ">"]],
61+
"v": [[">"]],
62+
"<": [[""]],
63+
"A": [[">", ">", "^"], [">", "^", ">"]],
64+
},
65+
}
66+
67+
NUM_KEYPAD = {
68+
"7": Cell(0, 0),
69+
"8": Cell(0, 1),
70+
"9": Cell(0, 2),
71+
"4": Cell(1, 0),
72+
"5": Cell(1, 1),
73+
"6": Cell(1, 2),
74+
"1": Cell(2, 0),
75+
"2": Cell(2, 1),
76+
"3": Cell(2, 2),
77+
"0": Cell(3, 1),
78+
"A": Cell(3, 2),
79+
}
80+
81+
82+
class Solution(SolutionBase[Input, Output1, Output2]):
83+
def parse_input(self, input_data: InputData) -> Input:
84+
return input_data
85+
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
104+
105+
def do_num_keypad(self, seq: str) -> list[list[str]]:
106+
lst = [_ for _ in seq]
107+
ans = list[list[str]]()
108+
109+
all_moves = []
110+
for prev, nxt in zip(["A"] + lst, lst):
111+
start = NUM_KEYPAD[prev]
112+
end = NUM_KEYPAD[nxt]
113+
result = Dijkstra.all(
114+
start,
115+
lambda cell: cell == end,
116+
lambda cell: (
117+
n
118+
for n in cell.get_capital_neighbours()
119+
if n in NUM_KEYPAD.values()
120+
),
121+
lambda curr, nxt: 1,
122+
)
123+
# log((prev, nxt))
124+
paths = result.get_paths(end)
125+
moves = []
126+
for path in paths:
127+
move = []
128+
for c1, c2 in zip(path, path[1:]):
129+
move.append(c1.to(c2).arrow)
130+
moves.append(move)
131+
all_moves.append(moves)
132+
133+
# log(all_moves)
134+
tmp = [p for p in itertools.product(*all_moves)]
135+
# log(tmp)
136+
for t in tmp:
137+
a = []
138+
for tt in t:
139+
x = list(tt) + ["A"]
140+
a.extend(x)
141+
if a is not None:
142+
ans.append(a) # type:ignore
143+
log(seq)
144+
log(ans)
145+
return ans
146+
147+
def solve_1(self, input: str) -> int:
148+
best = sys.maxsize
149+
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)
158+
return best
159+
160+
def part_1(self, input: Input) -> Output1:
161+
return sum(self.solve_1(combo) * int(combo[:-1]) for combo in input)
162+
163+
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+
)
172+
def samples(self) -> None:
173+
pass
174+
175+
176+
solution = Solution(2024, 21)
177+
178+
179+
def main() -> None:
180+
solution.run(sys.argv)
181+
182+
183+
if __name__ == "__main__":
184+
main()

0 commit comments

Comments
 (0)