from __future__ import annotations from collections import deque from tools.aoc import AOCDay from typing import Any class Component: def __init__(self, name: str): self.name = name self.connections = set() def __hash__(self) -> int: return hash(self.name) def separate(component1: Component, component2: Component) -> (int, int, int): """returns len(group1), len(group2), len(wires_to_disconnect)""" group1, group2, wires_to_disconnect = set(), set(), set() q1, q2 = deque([component1]), deque([component2]) while q1 or q2: if q1 and len(q1) != 3: c1 = q1.popleft() if c1 in group1 or c1 in group2: continue group1.add(c1) for c in c1.connections: if c in group2: wires_to_disconnect.add((c1, c) if c1.name < c.name else (c, c1)) else: q1.append(c) if q2 and len(q2) != 3: c2 = q2.popleft() if c2 in group1 or c2 in group2: continue group2.add(c2) for c in c2.connections: if c in group1: wires_to_disconnect.add(((c2, c) if c2.name < c.name else (c, c2))) else: q2.append(c) if len(q1) == 3 and len(q2) == 3: return len(group1), len(group2), len(wires_to_disconnect) return len(group1), len(group2), len(wires_to_disconnect) class Wiring: def __init__(self): self.components = {} def get_component(self, name: str): if name not in self.components: self.components[name] = Component(name) return self.components[name] def get_connections(self) -> set: connections = set() for component in self.components.values(): for connection in component.connections: connections.add( (component.name, connection.name) if component.name < connection.name else (connection.name, component.name) ) return connections @classmethod def from_input(cls, data: list[str]) -> Wiring: wiring = cls() for line in data: left, rights = line.split(": ") rights = rights.split() left = wiring.get_component(left) for r in rights: right = wiring.get_component(r) left.connections.add(right) right.connections.add(left) return wiring class Day(AOCDay): inputs = [ [ # (54, "input25_test"), # Test breaks sometimes. Live always works?! (545528, "input25_dennis"), (544523, "input25"), ], [ (None, "input25"), ], ] def part1(self) -> Any: wiring = Wiring.from_input(self.getInput()) the_first = wiring.get_component(next(iter(wiring.components))) for component in wiring.components.values(): if component == the_first: continue g1, g2, c = separate(the_first, component) if c == 3: return g1 * g2 g1, g2, c = separate(component, the_first) if c == 3: return g1 * g2 print("Missed, try again.") return "" def part2(self) -> Any: return "" if __name__ == "__main__": day = Day(2023, 25) day.run(verbose=True)