# -*- coding: utf-8 -*- from abc import ABC from typing import Iterator, Set from aoc import BaseAssignment from aoc.datastructures import Coordinate3 from aoc.mixins import BreathFirstSearchMixin class Assignment(BaseAssignment[int, Coordinate3], ABC): def parse_item(self, item: str) -> Coordinate3: x, y, z = item.split(",") return Coordinate3(int(x), int(y), int(z)) @staticmethod def open_sides(blocks: Set[Coordinate3]) -> int: return len( [ neighbour for block in blocks for neighbour in block.neighbours(True) if neighbour not in blocks ] ) class AssignmentOne(Assignment): example_result = 64 def run(self, input: Iterator[Coordinate3]) -> int: blocks = set(input) return self.open_sides(blocks) class AssignmentTwo(Assignment, BreathFirstSearchMixin[Coordinate3]): example_result = 58 def run(self, input: Iterator[Coordinate3]) -> int: blocks = set(input) lower_x = min([block.x for block in blocks]) - 1 higher_x = max([block.x for block in blocks]) + 1 lower_y = min([block.y for block in blocks]) - 1 higher_y = max([block.y for block in blocks]) + 1 lower_z = min([block.z for block in blocks]) - 1 higher_z = max([block.z for block in blocks]) + 1 all_coordinates = set( Coordinate3(x, y, z) for x in range(lower_x, higher_x + 1) for y in range(lower_y, higher_y + 1) for z in range(lower_z, higher_z + 1) ) def nb(c): for n in c.neighbours(True): if n in all_coordinates and n not in blocks: yield n outer_coordinates = { coordinate for coordinate in self.bfs( Coordinate3(lower_x, lower_y, lower_z), neighbours=nb, ) } return len( [ neighbour for block in blocks for neighbour in block.neighbours(True) if neighbour in outer_coordinates ] )