From a83c7cb4414174354634721e3cf77a90ddf0fefb Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 18 Dec 2024 00:25:04 -0500 Subject: [PATCH] 2024: d18: ex2: add solution --- 2024/d18/ex2/ex2.py | 78 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100755 2024/d18/ex2/ex2.py diff --git a/2024/d18/ex2/ex2.py b/2024/d18/ex2/ex2.py new file mode 100755 index 0000000..046324b --- /dev/null +++ b/2024/d18/ex2/ex2.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +import heapq +import sys +from collections.abc import Iterator +from typing import NamedTuple + + +class Point(NamedTuple): + x: int + y: int + + def neighbours(self) -> Iterator["Point"]: + for dx, dy in ( + (-1, 0), + (1, 0), + (0, -1), + (0, 1), + ): + yield Point(self.x + dx, self.y + dy) + + +DIMS = Point(70, 70) + + +def solve(input: str) -> str: + def parse(input: list[str]) -> list[Point]: + return [Point(*map(int, line.split(","))) for line in input] + + def djikstra(start: Point, end: Point, blocks: set[Point]) -> int | None: + # Priority queue of (distance, point) + queue = [(0, start)] + seen: set[Point] = set() + + while len(queue) > 0: + cost, p = heapq.heappop(queue) + if p == end: + return cost + # We must have seen p with a smaller distance before + if p in seen: + continue + # First time encountering p, must be the smallest distance to it + seen.add(p) + # Add all neighbours to be visited + for n in p.neighbours(): + if p in blocks: + continue + if not 0 <= p.x <= DIMS.x: + continue + if not 0 <= p.y <= DIMS.y: + continue + heapq.heappush(queue, (cost + 1, n)) + + return None + + def bisect_cutoff(start: Point, end: Point, blocks: list[Point]) -> Point: + # Cutting off the path is monotonic: once cut-off, it's never uncut + low, high = 0, len(blocks) + while low < high: + mid = low + (high - low) // 2 + if djikstra(start, end, set(blocks[: mid + 1])) is None: + high = mid + else: + low = mid + 1 + return blocks[low] + + coords = parse(input.splitlines()) + byte = bisect_cutoff(Point(0, 0), DIMS, coords) + return f"{byte.x},{byte.y}" + + +def main() -> None: + input = sys.stdin.read() + print(solve(input)) + + +if __name__ == "__main__": + main()