2024 d21 wip
This commit is contained in:
parent
2ee16eb1ba
commit
376a2eac09
206
2024/d21.py
206
2024/d21.py
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user