#!/usr/bin/env python3 from pathlib import Path def parse(input): scanners = [] s = [] for line in input: line = line.rstrip() if 'scanner' in line: s = [] continue if len(line): b = tuple([int(x) for x in line.split(',')]) s.append(b) else: scanners.append(s) if len(s): scanners.append(s) return scanners def calc_distances(s): d = [] for _ in s: d.append(set()) for i in range(len(s)): for j in range(i+1, len(s)): dist = 0 for k in range(len(s[i])): dist += (s[i][k] - s[j][k]) ** 2 d[i].add(dist) d[j].add(dist) return d def num_overlapping(s0, s1): num = 0 for b0 in s0: for b1 in s1: if 11 <= len(b0 & b1): num += 1 return num def get_transform_func(v0, v1): (x0, y0, z0) = v0 (x1, y1, z1) = v1 transform = '(' if abs(x0) == abs(x1): if 0 > x0 // x1: transform += '-' transform += 'x,' elif abs(x0) == abs(y1): if 0 > x0 // y1: transform += '-' transform += 'y,' elif abs(x0) == abs(z1): if 0 > x0 // z1: transform += '-' transform += 'z,' if abs(y0) == abs(x1): if 0 > y0 // x1: transform += '-' transform += 'x,' elif abs(y0) == abs(y1): if 0 > y0 // y1: transform += '-' transform += 'y,' elif abs(y0) == abs(z1): if 0 > y0 // z1: transform += '-' transform += 'z,' if abs(z0) == abs(x1): if 0 > z0 // x1: transform += '-' transform += 'x' elif abs(z0) == abs(y1): if 0 > z0 // y1: transform += '-' transform += 'y' elif abs(z0) == abs(z1): if 0 > z0 // z1: transform += '-' transform += 'z' transform += ')' return transform def transform(scanners, idx, done=[]): done.append(idx) for n in scanners[idx]['overlapping']: if n in done: continue overlapping = [] for i, b0 in enumerate(scanners[idx]['metrics']): for j, b1 in enumerate(scanners[n]['metrics']): if 11 <= len(b0 & b1): overlapping.append((i, j)) if 2 <= len(overlapping): break if 2 <= len(overlapping): break assert len(overlapping) == 2 (x0, y0, z0) = scanners[idx]['beacons'][overlapping[0][0]] (x1, y1, z1) = scanners[idx]['beacons'][overlapping[1][0]] v0 = (x0 - x1, y0 - y1, z0 - z1) (x0, y0, z0) = scanners[n]['beacons'][overlapping[0][1]] (x1, y1, z1) = scanners[n]['beacons'][overlapping[1][1]] v1 = (x0 - x1, y0 - y1, z0 - z1) transform_func = get_transform_func(v0, v1) for i in range(len(scanners[n]['beacons'])): (x, y, z) = scanners[n]['beacons'][i] scanners[n]['beacons'][i] = eval(transform_func) (xo, yo, zo) = scanners[idx]['origin'] (x0, y0, z0) = scanners[idx]['beacons'][overlapping[0][0]] (x1, y1, z1) = scanners[n]['beacons'][overlapping[0][1]] scanners[n]['origin'] = (xo + x0 - x1, yo + y0 - y1, zo + z0 - z1) (xo, yo, zo) = scanners[n]['origin'] transform(scanners, n, done) for i in range(len(scanners[n]['beacons'])): (x, y, z) = scanners[n]['beacons'][i] scanners[n]['beacons'][i] = (x + xo, y + yo, z + zo) def solve(input): result_p1 = 0 result_p2 = 0 beacons = parse(input) scanners = [] for b in beacons: s = {} s['beacons'] = b s['metrics'] = calc_distances(b) s['origin'] = (0, 0, 0) s['overlapping'] = set() scanners.append(s) for i in range(len(scanners)): for j in range(i + 1, len(scanners)): num = num_overlapping( scanners[i]['metrics'], scanners[j]['metrics']) if num: scanners[i]['overlapping'].add(j) scanners[j]['overlapping'].add(i) transform(scanners, 0) m = set() for s in scanners: for b in s['beacons']: m.add(b) result_p1 = len(m) for i in range(len(scanners)): (xi, yi, zi) = scanners[i]['origin'] for j in range(i + 1, len(scanners)): (xj, yj, zj) = scanners[j]['origin'] md = abs(xi - xj) + abs(yi - yj) + abs(zi - zj) result_p2 = max(result_p2, md) print("Part 1 result:", result_p1) print("Part 2 result:", result_p2) input = list() p = Path(__file__).with_name('input.txt') with open(p) as f: input = f.readlines() solve(input)