2024 d21 wip

This commit is contained in:
felixm 2024-12-30 22:28:06 -05:00
parent 2ee16eb1ba
commit 376a2eac09

View File

@ -1,161 +1,110 @@
import heapq import heapq
from lib import get_data from lib import get_data
from functools import cache
data = get_data(__file__) data = get_data(__file__)
def new_dir(current, move): def new_dir(current, move):
if current == "<": return {"<":
if move == ">": return "v" {">": "v"},
else: return None "^":
elif current == "^": {">": "A", "v": "v"},
if move == ">": return "A" "v":
elif move == "v": return "v" {"<": "<", ">": ">", "^": "^"},
else: return None ">":
elif current == "v": {"^": "A", "<": "v"},
if move == "<": return "<" "A":
elif move == "^": return "^" {"<": "^", "v": ">"}
elif move == ">": return ">" }[current].get(move, None)
else: return None
elif current == ">":
if move == "<": return "v"
elif move == "^": return "A"
else: return None
elif current == "A":
if move == "<": return "^"
elif move == "v": return ">"
else: return None
else:
assert False
def new_num(current, move): def new_num(current, move):
if current == "7": return {"7": {">": "8", "v": "4",},
if move == ">": return "8" "8": {"<": "7", ">": "9", "v": "5"},
elif move == "v": return "4" "9": {"<": "8", "v": "6"},
else: return None "4": {"^": "7", ">": "5", "v": "1"},
elif current == "8": "5": {"^": "8", ">": "6", "<": "4", "v": "2"},
if move == "<": return "7" "6": {"^": "9", "<": "5", "v": "3"},
elif move == ">": return "9" "1": {"^": "4", ">": "2"},
elif move == "v": return "5" "2": {"^": "5", ">": "3", "<": "1", "v": "0"},
else: return None "3": {"^": "6", "<": "2", "v": "A"},
elif current == "9": "0": {"^": "2", ">": "A"},
if move == "<": return "8" "A": {"^": "3", "<": "0"},
elif move == "v": return "6" }[current].get(move, None)
else: return None
elif current == "5":
if move == "^": return "8"
elif move == "<": return "4"
elif move == ">": return "6"
elif move == "v": return "2"
else: assert False
elif current == "4":
if move == "^": return "7"
elif move == ">": return "5"
elif move == "v": return "1"
else: return None
elif current == "6":
if move == "^": return "9"
elif move == "<": return "5"
elif move == "v": return "3"
else: return None
elif current == "1":
if move == "^": return "4"
elif move == ">": return "2"
else: return None
elif current == "2":
if move == "^": return "5"
elif move == "<": return "1"
elif move == ">": return "3"
elif move == "v": return "0"
else: assert False
elif current == "3":
if move == "^": return "6"
elif move == "<": return "2"
elif move == "v": return "A"
else: return None
elif current == "0":
if move == "^": return "2"
elif move == ">": return "A"
else: return None
elif current == "A":
if move == "^": return "3"
elif move == "<": return "0"
else: return None
else:
assert False
@cache def new_state(press, node, has_numpad):
def new_state(press, node): key_pads, num_pad, typed, pressed = node
cost, r, n, path = node key_pads = list(key_pads)
r = list(r) pressed = pressed + press
for i in range(len(r)): for i in range(len(key_pads)):
if press == "A": if press == "A":
press = r[i] press = key_pads[i]
else: else:
r[i] = new_dir(r[i], press) key_pads[i] = new_dir(key_pads[i], press)
if r[i] is None: if key_pads[i] is None:
return None return None
press = None press = None
break break
npath = str(path)
if press is not None: if press is not None:
if has_numpad:
if press == "A": if press == "A":
npath += n typed = typed + num_pad
else: else:
n = new_num(n, press) num_pad = new_num(num_pad, press)
if n is None: if num_pad is None:
return None return None
else:
nnode = cost + 1, tuple(r), n, npath typed = typed + press
nnode = tuple(key_pads), num_pad, typed, pressed
return nnode return nnode
if press == "A": # if press == "A":
if r[0] == "A": # if key_pads[0] == "A":
if r[1] == "A": # if key_pads[1] == "A":
path += n # entered += num_pad
else: # else:
n = new_num(n, r[1]) # num_pad = new_num(num_pad, key_pads[1])
else: # else:
r[1] = new_dir(r[1], r[0]) # key_pads[1] = new_dir(key_pads[1], key_pads[0])
else: # else:
r[0] = new_dir(r[0], press) # key_pads[0] = new_dir(key_pads[0], press)
#
# if key_pads[0] is None or key_pads[1] is None or num_pad is None:
# return None
# return tuple(key_pads), num_pad, typed
if r[0] is None or r[1] is None or n is None: def find(code, key_pads=2, has_numpad=False):
return None _, _, TYPED, ENTERED = 0, 1, 2, 3
npath = str(path)
return cost + 1, tuple(r), n, npath
def sequence(line):
def dist(n1, n2): def dist(n1, n2):
"""cost from node to node""" if n1 == 0: return 0
return 1 return 1
def h(node): def h(node):
"""heuristic function (never overestimate)""" return len(code) - len(node[TYPED])
return len(line) - len(node[3])
def is_goal(node): def is_goal(node):
return node[3] == line return node[TYPED] == code
def neighbors(node): def neighbors(node):
nbs = [] nbs = []
for move in '<^v>A': for move in '<^v>A':
nb = new_state(move, node) nb = new_state(move, node, has_numpad)
if nb is None: if nb is None:
continue continue
if not line.startswith(node[3]): if not code.startswith(nb[TYPED]):
continue continue
nbs.append(nb) nbs.append(nb)
return nbs return nbs
key_pads = tuple(['A' for _ in range(5)]) def hash(node):
start = (0, key_pads, 'A', '') return node[:-1]
key_pads = tuple(['A' for _ in range(key_pads)])
start = (key_pads, 'A', '', '')
starts = [start] starts = [start]
open_set = [] open_set = []
g_score = {} g_score = {}
@ -163,27 +112,32 @@ def sequence(line):
for start in starts: for start in starts:
heapq.heappush(open_set, (h(start), start)) heapq.heappush(open_set, (h(start), start))
g_score[start] = dist(0, start) g_score[hash(start)] = dist(0, start)
while open_set: while open_set:
current_f_score, current = heapq.heappop(open_set) current_f_score, current = heapq.heappop(open_set)
if is_goal(current): if is_goal(current):
assert current_f_score == g_score[current] assert current_f_score == g_score[hash(current)]
cost = g_score[current] # print(current)
cost = g_score[hash(current)]
cost = current[ENTERED]
break break
# print(current)
for neighbor in neighbors(current): for neighbor in neighbors(current):
tentative_g_score = g_score[current] + dist(current, neighbor) tentative_g_score = g_score[hash(current)] + dist(current, neighbor)
if neighbor not in g_score or tentative_g_score < g_score[neighbor]: if tentative_g_score < g_score.get(hash(neighbor), 10**15):
g_score[neighbor] = tentative_g_score # print(" ", neighbor, end="")
f_score = g_score[neighbor] + h(neighbor) # input()
g_score[hash(neighbor)] = tentative_g_score
f_score = g_score[hash(neighbor)] + h(neighbor)
heapq.heappush(open_set, (f_score, neighbor)) heapq.heappush(open_set, (f_score, neighbor))
return cost return cost
t = 0 t = 0
for line in data.splitlines(): for line in data.splitlines():
s = sequence(line) s = find(line, 2, True)
i = int("".join([c for c in line if ord('0') <= ord(c) <= ord('9')])) i = int("".join([c for c in line if c.isdigit()]))
print(s, i) s = len(s)
t += (s - 1) * i t += s * i
print(t) print(t)