diff --git a/2016/d13/ex1/ex1.py b/2016/d13/ex1/ex1.py new file mode 100755 index 0000000..688beb2 --- /dev/null +++ b/2016/d13/ex1/ex1.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +import functools +import heapq +import sys +from collections.abc import Callable, Iterator +from typing import NamedTuple + + +class Point(NamedTuple): + x: int + y: int + + +def solve(input: str) -> int: + def office_location_open(p: Point, number: int) -> bool: + x, y = p + return (x * x + 3 * x + 2 * x * y + y + y * y + number).bit_count() % 2 == 0 + + def office_neighbours(p: Point, number: int) -> Iterator[Point]: + for dx, dy in ( + (-1, 0), + (1, 0), + (0, -1), + (0, 1), + ): + n = Point(p.x + dx, p.y + dy) + if n.x < 0 or n.y < 0: + continue + if office_location_open(n, number): + yield n + + def dijkstra( + start: Point, + end: Point, + neighbours: Callable[[Point], Iterator[Point]], + ) -> int: + # Priority queue of (distance, point) + queue = [(0, start)] + seen: set[Point] = set() + + while len(queue) > 0: + dist, p = heapq.heappop(queue) + if p == end: + return dist + # 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 neighbours(p): + heapq.heappush(queue, (dist + 1, n)) + + assert False # Sanity check + + favorite_number = int(input.strip()) + neighbours = functools.partial(office_neighbours, number=favorite_number) + return dijkstra(Point(1, 1), Point(31, 39), neighbours) + + +def main() -> None: + input = sys.stdin.read() + print(solve(input)) + + +if __name__ == "__main__": + main()