130 lines
3.2 KiB
Python
130 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import collections
|
|
import sys
|
|
|
|
input_file = sys.argv[1]
|
|
|
|
with open(input_file) as fd:
|
|
lines = [line.rstrip() for line in fd.readlines()]
|
|
|
|
height = len(lines)
|
|
width = len(lines[0])
|
|
|
|
vec = tuple[int, int]
|
|
directions = [
|
|
(-1, 0), # ^ North
|
|
(0, 1), # > East
|
|
(1, 0), # v South
|
|
(0, -1), # < West
|
|
]
|
|
|
|
Cheat = tuple[vec, int]
|
|
|
|
cheats: set[Cheat] = set()
|
|
for i, line in enumerate(lines):
|
|
if "S" in line:
|
|
j = line.index("S")
|
|
start = i, j
|
|
if i in range(1, height - 1):
|
|
for j in range(1, width - 1):
|
|
char = lines[i][j]
|
|
if char != "#":
|
|
continue
|
|
for d, direction in enumerate(directions):
|
|
ii, jj = i + direction[0], j + direction[1]
|
|
if ii not in range(1, height - 1) or jj not in range(1, width - 1):
|
|
continue
|
|
cchar = lines[ii][jj]
|
|
if cchar == "#":
|
|
continue
|
|
cheats.add(((i, j), d))
|
|
|
|
|
|
def print_grid(visited: list[list[int | None]], cheat: Cheat) -> None:
|
|
for i in range(height):
|
|
line = ""
|
|
for j in range(width):
|
|
char = lines[i][j]
|
|
if visited[i][j] is not None:
|
|
char = "O"
|
|
if cheat[0] == (i, j):
|
|
char = "X"
|
|
line += char
|
|
print(line)
|
|
print()
|
|
|
|
|
|
def time(cheat: Cheat) -> int | None:
|
|
visited: list[list[int | None]] = list()
|
|
for _ in range(height):
|
|
visited.append([None] * width)
|
|
stack: set[vec] = {start}
|
|
for s in range(1, 10000):
|
|
nstack: set[vec] = set()
|
|
for pos in stack:
|
|
i, j = pos
|
|
for d, direction in enumerate(directions):
|
|
if (i, j) == cheat[0]:
|
|
if d != cheat[1]:
|
|
continue
|
|
ii, jj = i + direction[0], j + direction[1]
|
|
cchar = lines[ii][jj]
|
|
if cchar == "#" and cheat != ((ii, jj), d):
|
|
continue
|
|
elif cchar == "E":
|
|
# if s == 84 - 8:
|
|
# print_grid(visited, cheat)
|
|
return s
|
|
previs = visited[ii][jj]
|
|
if previs is not None and previs < s:
|
|
continue
|
|
visited[ii][jj] = s
|
|
nstack.add((ii, jj))
|
|
stack = nstack
|
|
|
|
# print("Second", s)
|
|
# print_grid(visited)
|
|
else:
|
|
return None
|
|
|
|
|
|
canon_saves: collections.Counter[int] = collections.Counter()
|
|
for k, v in (
|
|
{
|
|
2: 14,
|
|
4: 14,
|
|
6: 2,
|
|
8: 4,
|
|
10: 2,
|
|
12: 3,
|
|
20: 1,
|
|
36: 1,
|
|
38: 1,
|
|
40: 1,
|
|
64: 1,
|
|
}
|
|
).items():
|
|
canon_saves[k] = v
|
|
|
|
normal = time(((0, 0), 0))
|
|
assert normal
|
|
saves: collections.Counter[int] = collections.Counter()
|
|
saves_mo100 = 0
|
|
print(f"{normal=}")
|
|
print(f"{len(cheats)=}")
|
|
for c, cheat in enumerate(cheats):
|
|
print("Cheat", c, "/", len(cheats))
|
|
ntime = time(cheat)
|
|
assert ntime
|
|
diff = normal - ntime
|
|
saves[diff] += 1
|
|
if diff >= 100:
|
|
saves_mo100 += 1
|
|
del saves[0]
|
|
print(f"{saves=}")
|
|
print(f"{canon_saves=}")
|
|
print(f"{(saves == canon_saves)=}")
|
|
print(saves_mo100)
|
|
# 1282: too low
|