Skip to content

Commit 35cda9d

Browse files
committed
added 2024/day21
1 parent 81f844f commit 35cda9d

File tree

4 files changed

+146
-1
lines changed

4 files changed

+146
-1
lines changed

2024/day21/answers.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
162740
2+
203640915832208

2024/day21/input.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
593A
2+
283A
3+
670A
4+
459A
5+
279A

2024/day21/run.py

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#! /usr/bin/env python3
2+
3+
def load_data(filename):
4+
with open(filename, 'r') as f:
5+
for line in f:
6+
line = line.rstrip('\n')
7+
yield line
8+
9+
codes = list(load_data('input.txt'))
10+
11+
# Part One
12+
13+
from functools import cache
14+
from collections import defaultdict, Counter
15+
16+
class Keypad:
17+
18+
@classmethod
19+
@cache
20+
def xy(cls, key):
21+
for y in range(len(cls.keypad)):
22+
if key in cls.keypad[y]:
23+
return cls.keypad[y].index(key), y
24+
raise Exception(f"Key {key} not found in {cls}")
25+
26+
@classmethod
27+
@cache
28+
def paths(cls, from_, to):
29+
def is_valid(x, y, path):
30+
for step in path:
31+
dx, dy = {'<': (-1, 0), '>': (1, 0), '^': (0, -1), 'v': (0, 1)}[step]
32+
x += dx
33+
y += dy
34+
if cls.keypad[y][x] is None:
35+
return False
36+
return True
37+
fx, fy = cls.xy(from_)
38+
tx, ty = cls.xy(to)
39+
dx = '<' * (fx-tx) if tx < fx else '>' * (tx-fx)
40+
dy = '^' * (fy-ty) if ty < fy else 'v' * (ty-fy)
41+
return set([dx+dy+'A'] * is_valid(fx, fy, dx+dy) + [dy+dx+'A'] * is_valid(fx, fy, dy+dx))
42+
43+
@classmethod
44+
@cache
45+
def shortest_from_to(cls, from_, to, backed):
46+
paths = cls.paths(from_, to)
47+
if not backed:
48+
return next(iter(paths))
49+
min_path = min( [DirKeypad.shortest_seq(path, backed-1) for path in paths], key=len )
50+
return min_path
51+
52+
@classmethod
53+
@cache
54+
def shortest_seq(cls, code, backed):
55+
ret = ''
56+
current = 'A'
57+
for c in code:
58+
ret += cls.shortest_from_to(current, c, backed)
59+
current = c
60+
return ret
61+
62+
63+
64+
65+
66+
67+
68+
69+
@classmethod
70+
@cache
71+
def shortest_path(cls, from_, to):
72+
paths = cls.paths(from_, to)
73+
min_path = min( [(DirKeypad.shortest_seq(path, 5), path) for path in paths], key=lambda item: len(item[0]) )[1]
74+
return min_path
75+
76+
@staticmethod
77+
def str2tokens(s):
78+
assert s[-1] == 'A'
79+
return [ t + 'A' for t in s.split('A')[:-1] ]
80+
81+
@classmethod
82+
def str2cnt(cls, s):
83+
return Counter(cls.str2tokens(s))
84+
85+
@staticmethod
86+
def length(code):
87+
return sum([ len(k) * v for k, v in code.items() ])
88+
89+
@classmethod
90+
@cache
91+
def move(cls, key):
92+
current = 'A'
93+
ret = []
94+
for c in key:
95+
ret.append(cls.shortest_path(current, c))
96+
current = c
97+
return ret
98+
99+
@classmethod
100+
def shortest_path_quick(cls, code, backed):
101+
if not backed:
102+
return cls.length(code)
103+
ret = defaultdict(int)
104+
for k, v in code.items():
105+
for m in DirKeypad.move(k):
106+
ret[m] += v
107+
return DirKeypad.shortest_path_quick(ret, backed-1)
108+
109+
@classmethod
110+
def shortest_seq_quick(cls, code, backed):
111+
code = ''.join(cls.move(code))
112+
code = cls.str2cnt(code)
113+
return cls.shortest_path_quick(code, backed)
114+
115+
class NumKeypad(Keypad):
116+
keypad = [['7', '8', '9'], ['4', '5', '6'], ['1', '2', '3'], [None, '0', 'A']]
117+
118+
class DirKeypad(Keypad):
119+
keypad = [[None, '^', 'A'], ['<', 'v', '>']]
120+
121+
def length(code, robots):
122+
return len(NumKeypad.shortest_seq(code, robots))
123+
124+
def score(code, robots):
125+
return length(code, robots) * int(code[:-1])
126+
127+
result = sum( score(code, 2) for code in codes )
128+
129+
print(result)
130+
131+
# Part Two
132+
133+
def score2(code, robots):
134+
return NumKeypad.shortest_seq_quick(code, robots) * int(code[:-1])
135+
136+
result = sum( score2(code, 25) for code in codes )
137+
138+
print(result)

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
2021 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +
1010
2022 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ -- ++ ++ -- ++ ++ ++ ++ ++ +
1111
2023 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ -- ++ ++ ++ ++ ++ ++ ++ +
12-
2024 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ -- ++ ++ ++ +
12+
2024 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +
1313
```

0 commit comments

Comments
 (0)