import math from collections import deque, defaultdict from tools.aoc import AOCDay from tools.coordinate import Coordinate, Line from tools.grid import Grid from tools.visualization import Window from typing import Any def walk(grid: Grid, start: Coordinate, steps: int) -> int: q = deque() q.append((start, 0)) s_mod = steps % 2 v = set() v2 = set() while q: coord, dist = q.popleft() # print("check ", coord, dist) if coord in v or coord in v2: # print(" -> in v") continue if dist % 2 == s_mod: # print(f" -> add to v because {dist} even or == {steps}") v.add(coord) else: v2.add(coord) if dist >= steps: # print(f" -> >= {steps}") continue for n in coord.getNeighbours(includeDiagonal=False): if grid.get(n % (grid.maxX + 1)): q.append((n, dist + 1)) # print([x for x in v]) return len(v) class Day(AOCDay): inputs = [ [ (16, "input21_test"), (3562, "input21"), ], [ # (16733044, "input21_test"), (None, "input21"), ], ] def parse_input(self) -> (Grid, Coordinate): grid = Grid().from_data(self.getInput(), translate={".": True, "#": False}) start = None for x in grid.find("S"): start = x grid.set(x, True) break # there is only one S return grid, start def part1(self) -> Any: needed_steps = 6 if self.is_test() else 64 grid, start = self.parse_input() return walk(grid, start, needed_steps) def part2(self) -> Any: grid, start = self.parse_input() print(grid.getOnCount()) for i in [Coordinate(0, 65), Coordinate(65, 0), Coordinate(130, 65), Coordinate(65, 130)]: t = walk(grid, i, 131) # this always yields 14746 ... coincidence? print(f"{i} = {t}") for i in [Coordinate(0, 0), Coordinate(130, 0), Coordinate(0, 130), Coordinate(130, 130)]: t = walk(grid, i, 65) print(f"{i} = {t}") needed_steps = 5000 if self.is_test() else 26501365 t = walk(grid, start, 64) k = walk(grid, start, len(self.getInput()) + 65) g = walk(grid, start, 2 * len(self.getInput()) + 65) # v = walk(grid, start, (3 * len(self.getInput())) - 1) print(f"65={t}, 196={k}, 327={g}, {math.log(k)=}, {math.log(g)=}") print(((k - t) / 14746) * 3) print((g - t) / 14746) print((g - k) / 14746) print(g % k) print(k / t) return "" if __name__ == "__main__": day = Day(2023, 21) day.run(verbose=True)