aoc2021/day19.py
2021-12-19 09:12:45 +01:00

91 lines
3.2 KiB
Python

from collections import defaultdict
from itertools import combinations
from tools.aoc import AOCDay
from tools.coordinate import Coordinate, DistanceAlgorithm
from tools.grid import Grid, GridTransformation
from typing import Any, List, Union
ROTATION_PATH = [
GridTransformation.ROTATE_X,
GridTransformation.ROTATE_X,
GridTransformation.ROTATE_Z,
GridTransformation.ROTATE_X,
GridTransformation.ROTATE_X,
GridTransformation.ROTATE_Z
]
def overlap(beacon1: Grid, beacon2: Grid) -> Union[Coordinate, None]:
diffs = defaultdict(int)
for c1 in beacon1.getActiveCells():
for c2 in beacon2.getActiveCells():
# Interestingly adding an if statement here to break out early when already 12 matches are found
# slows the loop down so much, that the end result is slower compared to just counting everything
# and checking the results afterwards.
diffs[c1 - c2] += 1
for d in diffs:
if diffs[d] >= 12:
return d
def converge(scanner_grids: List[Grid]) -> (Grid, List[Coordinate]):
main_grid = scanner_grids.pop()
diff_list = [Coordinate(0, 0, 0)]
while scanner_grids:
found = False
for i, scanner in enumerate(scanner_grids):
for rotation in ROTATION_PATH:
for y_rotation in range(4):
if diff := overlap(main_grid, scanner):
#print("Grid overlap:", main_grid.name, scanner.name, "=>", len(scanner_grids) - 1, "to go.")
scanner_grids.pop(i)
diff_list.append(diff)
for c in scanner.getActiveCells():
main_grid.set(c + diff)
found = True
break
scanner.transform(GridTransformation.ROTATE_Y)
scanner.transform(rotation)
if found:
break
if found:
break
assert found, "No overlapping Grids found"
return main_grid, diff_list
class Day(AOCDay):
test_solutions_p1 = [79, 465]
test_solutions_p2 = [3621, 12149]
def part1(self) -> Any:
scanner_grids = []
for scanner_input in self.getMultiLineInputAsArray():
scanner_grid = Grid()
scanner_grid.name = scanner_input[0]
for l in scanner_input[1:]:
scanner_grid.set(Coordinate(*map(int, l.split(","))))
scanner_grids.append(scanner_grid)
teh_grid, _ = converge(scanner_grids)
return len(teh_grid.getActiveCells())
def part2(self) -> Any:
scanner_grids = []
for scanner_input in self.getMultiLineInputAsArray():
scanner_grid = Grid()
scanner_grid.name = scanner_input[0]
for l in scanner_input[1:]:
scanner_grid.set(Coordinate(*map(int, l.split(","))))
scanner_grids.append(scanner_grid)
_, diff_list = converge(scanner_grids)
max_diff = 0
for s1, s2 in combinations(diff_list, 2):
max_diff = max(max_diff, s1.getDistanceTo(s2, includeDiagonals=False, algorithm=DistanceAlgorithm.MANHATTAN))
return max_diff