From f3d0899dbfd0aa3e6aebf5d19ec89d58ead418b2 Mon Sep 17 00:00:00 2001 From: Tom van der Lee Date: Mon, 19 Dec 2022 09:30:43 +0100 Subject: Day 18 --- aoc/datastructures.py | 68 ++++++++++++++++++++++++++++++++++++++++ aoc/mixins.py | 20 ++++++++++++ aoc/tests/__init__.py | 0 aoc/tests/test_datastructures.py | 16 ++++++++++ 4 files changed, 104 insertions(+) create mode 100644 aoc/tests/__init__.py create mode 100644 aoc/tests/test_datastructures.py (limited to 'aoc') diff --git a/aoc/datastructures.py b/aoc/datastructures.py index 81a68e4..244aa1e 100644 --- a/aoc/datastructures.py +++ b/aoc/datastructures.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from collections import namedtuple +from typing import Iterator class Coordinate(namedtuple("Coordinate", ["x", "y"])): @@ -28,3 +29,70 @@ class Coordinate(namedtuple("Coordinate", ["x", "y"])): px, py, ) + + def neighbours(self, no_diagonal: bool = False) -> Iterator["Coordinate"]: + if no_diagonal: + yield self + Coordinate(-1, 0), + yield self + Coordinate(1, 0), + yield self + Coordinate(0, -1), + yield self + Coordinate(0, 1), + else: + for dy in range(self.y - 1, self.y + 2): + for dx in range(self.x - 1, self.x + 2): + if dy == self.y and dx == self.x: + continue + + yield Coordinate(dx, dy) + + +class Coordinate3(namedtuple("Coordinate3", ["x", "y", "z"])): + def __sub__(self, other: "Coordinate3") -> "Coordinate3": + return Coordinate3(self.x - other.x, self.y - other.y, self.z - other.z) + + def __add__(self, other: "Coordinate3") -> "Coordinate3": + return Coordinate3(self.x + other.x, self.y + other.y, self.z + other.z) + + def manhattan_distance(self, other: "Coordinate3") -> int: + return abs(self.x - other.x) + abs(self.y - other.y) + abs(self.z - other.z) + + @property + def polarity(self) -> "Coordinate3": + try: + px = abs(self.x) / self.x + except ZeroDivisionError: + px = 0 + + try: + py = abs(self.y) / self.y + except ZeroDivisionError: + py = 0 + + try: + pz = abs(self.z) / self.z + except ZeroDivisionError: + pz = 0 + + return Coordinate3( + px, + py, + pz, + ) + + def neighbours(self, no_diagonal: bool = False) -> Iterator["Coordinate3"]: + if no_diagonal: + yield self + Coordinate3(-1, 0, 0) + yield self + Coordinate3(1, 0, 0) + yield self + Coordinate3(0, -1, 0) + yield self + Coordinate3(0, 1, 0) + yield self + Coordinate3(0, 0, -1) + yield self + Coordinate3(0, 0, 1) + else: + for dz in range(self.z - 1, self.z + 2): + for dy in range(self.y - 1, self.y + 2): + for dx in range(self.x - 1, self.x + 2): + coordinate = Coordinate3(dx, dy, dz) + + if dy == self.y and dx == self.x: + continue + + yield coordinate diff --git a/aoc/mixins.py b/aoc/mixins.py index faeb3eb..5986b6e 100644 --- a/aoc/mixins.py +++ b/aoc/mixins.py @@ -55,3 +55,23 @@ class AStarMixin(Generic[Node]): if neighbour not in open_nodes: open_nodes.add(neighbour) + + +class BreathFirstSearchMixin(Generic[Node]): + @staticmethod + def bfs( + start: Node, neighbours: Callable[[Node], Iterator[Node]] + ) -> Iterator[Node]: + queue = [start] + searched = set() + + while len(queue): + item = queue.pop(0) + + for neighbour in neighbours(item): + if neighbour not in searched and neighbour not in set(queue): + queue.append(neighbour) + + searched.add(item) + + yield item diff --git a/aoc/tests/__init__.py b/aoc/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/aoc/tests/test_datastructures.py b/aoc/tests/test_datastructures.py new file mode 100644 index 0000000..a7965f5 --- /dev/null +++ b/aoc/tests/test_datastructures.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +from aoc.datastructures import Coordinate3 + + +class TestCoordinate3: + def test_neighbours_nodiagonal(self): + coordinate = Coordinate3(0, 0, 0) + + assert set(coordinate.neighbours(True)) == { + Coordinate3(-1, 0, 0), + Coordinate3(1, 0, 0), + Coordinate3(0, -1, 0), + Coordinate3(0, 1, 0), + Coordinate3(0, 0, -1), + Coordinate3(0, 0, 1), + } -- cgit v1.2.3