from functools import reduce from math import prod, sqrt from pathlib import Path from typing import TypeAlias Point: TypeAlias = tuple[int, int, int] FILE = "input.txt" class Circuit: def __init__(self, points: set[Point]) -> None: self.points: set[Point] = points def __contains__(self, other: Circuit) -> bool: for p in other.points: if p in self.points: return True return False def merge(self, other: Circuit) -> Circuit: return Circuit(self.points.union(other.points)) def main(): lines = Path(FILE).read_text().splitlines() points: list[Point] = [parse_point(line) for line in lines] distances = sorted([ (distance(p1, p2), Circuit({p1, p2})) for i, p1 in enumerate(points) for p2 in points[i+1:] ], key=lambda x: x[0]) big_circuits: list[Circuit] = [] last = None for (_, small_circuit) in distances: last = small_circuit new_circuits: list[Circuit] = [] for big_circuit in big_circuits: if small_circuit in big_circuit: new_circuits.append(big_circuit) if len(new_circuits): for circuit in new_circuits: big_circuits.remove(circuit) new_circuits.append(small_circuit) new_circuit = reduce(lambda a, b: a.merge(b), new_circuits) big_circuits.append(new_circuit) else: big_circuits.append(small_circuit) if len(big_circuits) == 1 and len(big_circuits[0].points) == len(points): break assert last is not None return prod(p[0] for p in last.points) def distance(p1: tuple[int, int, int], p2: tuple[int, int, int]): x1, y1, z1 = p1 x2, y2, z2 = p2 return sqrt((x2-x1)**2+(y2-y1)**2+(z2-z1)**2) def parse_point(line: str) -> Point: x, y, z = line.split(",") return (int(x), int(y), int(z)) if __name__ == "__main__": print(main())