Solve 2024:21 pt2 "Keypad Conundrum"
Turns out the initial trial of using Manhattan distance was required to solve part 2, since the BFS implementation is impossible to scale up from 2 robots to 25. Recursion and memoization makes the execution time acceptable. Line 53 makes all the difference here. The order of `<|v|^|>` matters when constructing a sequence. Many hours was spent trying to find the correct priority. For the example input, especially 456A and 379A were volatile.
This commit is contained in:
parent
3a9a7d4994
commit
d7d5312786
2 changed files with 21 additions and 83 deletions
|
|
@ -29,7 +29,7 @@ from collections import deque, Counter, defaultdict
|
||||||
from heapq import heappop, heappush
|
from heapq import heappop, heappush
|
||||||
from itertools import compress, combinations, chain, permutations
|
from itertools import compress, combinations, chain, permutations
|
||||||
|
|
||||||
from output import matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg, cw, ccw
|
from output import matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg, cw, ccw, bk
|
||||||
|
|
||||||
|
|
||||||
def solve(data):
|
def solve(data):
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
import re
|
|
||||||
from collections import deque, Counter, defaultdict
|
|
||||||
from heapq import heappop, heappush
|
|
||||||
from itertools import compress, combinations, chain, permutations
|
|
||||||
from functools import cache
|
from functools import cache
|
||||||
|
|
||||||
from output import matrix, D, DD, DDa, ADJ, ints, mhd, mdbg, vdbg, cw, ccw
|
|
||||||
|
|
||||||
DKP = {
|
DKP = {
|
||||||
"^": (0, 1),
|
"^": (0, 1),
|
||||||
"A": (0, 2),
|
"A": (0, 2),
|
||||||
|
|
@ -32,98 +26,42 @@ NKP = {
|
||||||
def solve(data):
|
def solve(data):
|
||||||
codes = data.split()
|
codes = data.split()
|
||||||
p1 = 0
|
p1 = 0
|
||||||
|
p2 = 0
|
||||||
for code in codes:
|
for code in codes:
|
||||||
seqlen = unwrap(code)
|
|
||||||
num = int(code[:-1])
|
num = int(code[:-1])
|
||||||
p1 += num * seqlen
|
p1 += num * unfold(code, 1 + 2, num=True)
|
||||||
p2 = None
|
p2 += num * unfold(code, 1 + 25, num=True)
|
||||||
return p1, p2
|
return p1, p2
|
||||||
|
|
||||||
|
|
||||||
def unwrap(dests):
|
@cache
|
||||||
seqlen = float("inf")
|
def unfold(sequence, iterations, num=False):
|
||||||
for num_path in press(dests, NKP):
|
if iterations == 0:
|
||||||
for d0_path in press(num_path, DKP):
|
return len(sequence)
|
||||||
for d1_path in press(d0_path, DKP):
|
|
||||||
seqlen = min(seqlen, len(d1_path))
|
|
||||||
return seqlen
|
|
||||||
|
|
||||||
|
|
||||||
def press(dests, pad):
|
|
||||||
s = "A"
|
s = "A"
|
||||||
P = [""]
|
total = 0
|
||||||
for d in dests:
|
pad, invalid = (NKP, (3, 0)) if num else (DKP, (0, 0))
|
||||||
paths = bfs(tuple(pad.values()), pad[s], pad[d])
|
for d in sequence:
|
||||||
ml = len(sorted(paths, key=lambda x: len(x))[0])
|
total += unfold(seq(pad[s], pad[d], invalid), iterations - 1)
|
||||||
sp = [p for p in paths if len(p) == ml]
|
|
||||||
nP = []
|
|
||||||
for a in P:
|
|
||||||
for b in sp:
|
|
||||||
nP.append(a + b)
|
|
||||||
P = nP
|
|
||||||
s = d
|
s = d
|
||||||
P = sorted(P, key=lambda x: len(x))
|
return total
|
||||||
psl = len(P[0])
|
|
||||||
return [p for p in P if len(p) == psl]
|
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def bfs(grid, S, E):
|
def seq(S, E, invalid):
|
||||||
seen = set()
|
y1, x1 = S
|
||||||
Q = deque([(S, "<", "")])
|
y2, x2 = E
|
||||||
paths = []
|
seq = "<" * (x1 - x2) + "v" * (y2 - y1) + "^" * (y1 - y2) + ">" * (x2 - x1)
|
||||||
while Q:
|
if (y2, x1) == invalid or (y1, x2) == invalid:
|
||||||
pos, dn, path = Q.popleft()
|
seq = seq[::-1]
|
||||||
if (pos, dn) in seen:
|
return seq + "A"
|
||||||
continue
|
|
||||||
if pos == E:
|
|
||||||
paths.append(path + "A")
|
|
||||||
continue
|
|
||||||
seen.add((pos, dn))
|
|
||||||
y, x = pos
|
|
||||||
for dn, delta in DDa.items():
|
|
||||||
dy, dx = delta
|
|
||||||
if (y + dy, x + dx) in grid:
|
|
||||||
Q.append(((y + dy, x + dx), dn, path + dn))
|
|
||||||
return paths
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import os
|
|
||||||
|
|
||||||
# use dummy data
|
|
||||||
inp = """
|
|
||||||
029A
|
|
||||||
980A
|
|
||||||
179A
|
|
||||||
456A
|
|
||||||
379A
|
|
||||||
""".strip()
|
|
||||||
|
|
||||||
"""
|
|
||||||
68 * 29
|
|
||||||
60 * 980
|
|
||||||
68 * 179
|
|
||||||
64 * 456
|
|
||||||
64 * 379
|
|
||||||
"""
|
|
||||||
|
|
||||||
# uncomment to instead use stdin
|
|
||||||
# import sys; inp = sys.stdin.read().strip()
|
|
||||||
|
|
||||||
# uncomment to use AoC provided puzzle input
|
|
||||||
with open("./input/21.txt", "r") as f:
|
with open("./input/21.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
# uncomment to do initial data processing shared by part 1-2
|
|
||||||
p1, p2 = solve(inp)
|
p1, p2 = solve(inp)
|
||||||
|
|
||||||
print(p1)
|
print(p1)
|
||||||
os.system(f"echo {p1} | wl-copy")
|
print(p2)
|
||||||
# print(p2)
|
|
||||||
# os.system(f"echo {p2} | wl-copy")
|
|
||||||
|
|
||||||
# uncomment and replace 0 with actual output to refactor code
|
|
||||||
# and ensure nonbreaking changes
|
|
||||||
# assert p1 == 0
|
|
||||||
# assert p2 == 0
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue