| from itertools import combinations, product | |||||
| def manhattan(p1, p2): | |||||
| return tuple(abs(a - b) for a, b in zip(p1, p2)) | |||||
| def find_shared_transpose(pts): | |||||
| new = {manhattan(a, b): a for a, b in combinations(pts, 2)} | |||||
| deltas = [ | |||||
| [(a - b) for a, b in zip(constellations[k], new[k])] | |||||
| for k in set(constellations) & set(new) | |||||
| ] | |||||
| if deltas: | |||||
| return [max(t, key=t.count) for t in zip(*deltas)] | |||||
| def overlap(pts): | |||||
| for fx, fy, fz in product([1, -1], [1, -1], [1, -1]): | |||||
| flipped = {(x * fx, y * fy, z * fz) for x, y, z in pts} | |||||
| if deltas := find_shared_transpose(flipped): | |||||
| dx, dy, dz = deltas | |||||
| transposed = {(x + dx, y + dy, z + dz) for x, y, z in flipped} | |||||
| if len(all_beacons & transposed) >= 12: | |||||
| update_state((-dx, -dy, -dz), transposed) | |||||
| return True | |||||
| def update_state(scanner, beacons): | |||||
| all_scanners.add(scanner) | |||||
| all_beacons.update(beacons) | |||||
| new = {manhattan(a, b): a for a, b in combinations(all_beacons, 2)} | |||||
| constellations.update(new) | |||||
| def transform(step, pts): | |||||
| if step % 3 == 0: | |||||
| pts = [pt[::-1] for pt in pts] | |||||
| return [pt[1:] + pt[:1] for pt in pts] | |||||
| text = open(0).read() | |||||
| reference, *readings = [ | |||||
| [tuple(int(n) for n in ln.split(',')) for ln in scnr.splitlines()[1:]] | |||||
| for scnr in text.split('\n\n') | |||||
| ] | |||||
| all_scanners = set() | |||||
| all_beacons = set() | |||||
| constellations = {} | |||||
| update_state((0, 0, 0), reference) | |||||
| for step in range(30): | |||||
| readings = [transform(step, pts) for pts in readings if not overlap(pts)] | |||||
| print(len(all_beacons)) | |||||
| print(max(sum(manhattan(a, b)) for a, b in combinations(all_scanners, 2))) |