All checks were successful
continuous-integration/drone/push Build is passing
166 lines
4.7 KiB
Python
166 lines
4.7 KiB
Python
#!/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)
|