advent-of-code/2024/20/one.py
2024-12-25 12:59:49 +01:00

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