#!/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