Finish 2021 thanks so much Eric
This commit is contained in:
121
2021/d19.py
Normal file
121
2021/d19.py
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
from lib import get_data
|
||||||
|
from lib import ints
|
||||||
|
from collections import defaultdict
|
||||||
|
from itertools import permutations, product
|
||||||
|
|
||||||
|
V3 = tuple[int, int, int]
|
||||||
|
|
||||||
|
data = get_data(__file__)
|
||||||
|
scanners = []
|
||||||
|
for block in data.split("\n\n"):
|
||||||
|
scanner = []
|
||||||
|
for line in block.splitlines()[1:]:
|
||||||
|
a, b, c = ints(line)
|
||||||
|
scanner.append((a, b, c))
|
||||||
|
scanners.append(scanner)
|
||||||
|
|
||||||
|
|
||||||
|
def rotate(vi: V3) -> list[V3]:
|
||||||
|
r = []
|
||||||
|
for p in permutations(vi):
|
||||||
|
for f in product([1, -1], repeat=3):
|
||||||
|
v = tuple([a * b for a, b in zip(p, f)])
|
||||||
|
r.append(v)
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
assert len(rotate((8, 0, 7))) == 48
|
||||||
|
|
||||||
|
|
||||||
|
def sub(a: V3, b: V3) -> V3:
|
||||||
|
return a[0] - b[0], a[1] - b[1], a[2] - b[2]
|
||||||
|
|
||||||
|
|
||||||
|
def add(a: V3, b: V3) -> V3:
|
||||||
|
return a[0] + b[0], a[1] + b[1], a[2] + b[2]
|
||||||
|
|
||||||
|
|
||||||
|
def relative(scanner: list[V3]):
|
||||||
|
d = defaultdict(list)
|
||||||
|
for i in range(len(scanner)):
|
||||||
|
for j in range(i + 1, len(scanner)):
|
||||||
|
for ii, jj in [(i, j), (j, i)]:
|
||||||
|
a, b = scanner[ii], scanner[jj]
|
||||||
|
delta = sub(a, b)
|
||||||
|
d[delta].append((a, b))
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def overlap(scanner_1, scanner_2):
|
||||||
|
expected_overlaps = 15
|
||||||
|
r1 = relative(scanner_1)
|
||||||
|
scanners_2 = []
|
||||||
|
for scanner in list(zip(*list(map(rotate, scanner_2)))):
|
||||||
|
os = set()
|
||||||
|
r2 = relative(scanner)
|
||||||
|
# number of bacon pairs that have the same offset
|
||||||
|
t = sum(1 for k1 in r1.keys() if k1 in r2)
|
||||||
|
if t >= expected_overlaps:
|
||||||
|
for k1, v1 in r1.items():
|
||||||
|
if k1 in r2:
|
||||||
|
((abs1, abs2),) = v1
|
||||||
|
((rel1, rel2),) = r2[k1]
|
||||||
|
os.add(sub(abs1, rel1))
|
||||||
|
os.add(sub(abs2, rel2))
|
||||||
|
if len(os) == 1:
|
||||||
|
# found the right orientation for scanner_2
|
||||||
|
scanners_2.append((os.pop(), scanner))
|
||||||
|
else:
|
||||||
|
r2 = None
|
||||||
|
else:
|
||||||
|
r2 = None
|
||||||
|
|
||||||
|
if len(scanners_2) == 0:
|
||||||
|
return None
|
||||||
|
((orig_2, scanner_2_rel),) = scanners_2
|
||||||
|
scanner_2_abs = [add(orig_2, b) for b in scanner_2_rel]
|
||||||
|
return orig_2, scanner_2_abs
|
||||||
|
|
||||||
|
|
||||||
|
origs = [(0, 0, 0)]
|
||||||
|
todo = set(range(len(scanners)))
|
||||||
|
done = set([0]) if 0 in todo else set([todo.pop()])
|
||||||
|
todo.discard(0)
|
||||||
|
while todo:
|
||||||
|
for i in range(len(scanners)):
|
||||||
|
for j in range(len(scanners)):
|
||||||
|
if i == j:
|
||||||
|
continue
|
||||||
|
if i not in done:
|
||||||
|
continue
|
||||||
|
if j in done:
|
||||||
|
continue
|
||||||
|
r = overlap(scanners[i], scanners[j])
|
||||||
|
|
||||||
|
if r is None:
|
||||||
|
continue
|
||||||
|
o, s2 = r
|
||||||
|
origs.append(o)
|
||||||
|
no = len(set(scanners[i]).intersection(s2))
|
||||||
|
if no >= 12:
|
||||||
|
scanners[j] = s2
|
||||||
|
done.add(j)
|
||||||
|
todo.discard(j)
|
||||||
|
# print(f"{i} < {no} > {j} at {o}")
|
||||||
|
|
||||||
|
|
||||||
|
all = []
|
||||||
|
for s in scanners:
|
||||||
|
all += s
|
||||||
|
print(len(set(all)))
|
||||||
|
|
||||||
|
|
||||||
|
def mdist(a, b):
|
||||||
|
return sum(abs(aa - bb) for aa, bb in zip(a, b))
|
||||||
|
|
||||||
|
|
||||||
|
m = 0
|
||||||
|
for i in range(len(origs)):
|
||||||
|
for j in range(i + 1, len(origs)):
|
||||||
|
m = max(m, mdist(origs[i], origs[j]))
|
||||||
|
print(m)
|
||||||
@@ -190,7 +190,7 @@ Solutions and utility script for Advent of Code challenges in Python.
|
|||||||
- Day 16: 50:01 (Way too slow. Was non-trivial but fun. Much better was feasible.)
|
- Day 16: 50:01 (Way too slow. Was non-trivial but fun. Much better was feasible.)
|
||||||
- Day 17: 21:59 (Not tricky again but struggling for no reason.)
|
- Day 17: 21:59 (Not tricky again but struggling for no reason.)
|
||||||
- Day 18: 162:00 (I couldn't figure out how to solve it as a tree so I did the basic way.)
|
- Day 18: 162:00 (I couldn't figure out how to solve it as a tree so I did the basic way.)
|
||||||
- Day 19:
|
- Day 19: days (Super hard for me but super fun ultimately once I had the right approach.)
|
||||||
- Day 20: 105:00 (That wasn't easy but was able to solve in one go.)
|
- Day 20: 105:00 (That wasn't easy but was able to solve in one go.)
|
||||||
- Day 21: 37:45 (Wasn't hard but I was just too slow.)
|
- Day 21: 37:45 (Wasn't hard but I was just too slow.)
|
||||||
- Day 22: 142:00 (Wonderful problem and hard for me but learned something new for sure.)
|
- Day 22: 142:00 (Wonderful problem and hard for me but learned something new for sure.)
|
||||||
|
|||||||
Reference in New Issue
Block a user