Files
python-aoc-2020/day20/part2.py
2020-12-29 15:42:56 +01:00

116 lines
3.2 KiB
Python

from collections import defaultdict
from functools import reduce
import numpy as np
from scipy.signal import convolve
lines = (x.strip() for x in open("input.txt"))
rotations = [(t, r) for t in (True, False) for r in range(4)]
tiles = {}
borders = defaultdict(set)
def edge_to_bin(edge):
edge = np.packbits(edge)
return edge[0] * 4 + edge[1] // 64
def add_tile(num, tile):
global tiles, borders
tiles[num] = tile
for transpose, rotate in rotations:
view = np.rot90(tile.T if transpose else tile, rotate)
edge = edge_to_bin(view[0])
borders[edge].add(num)
for line in lines:
if line.startswith("Tile"):
num = int(line[5:-1])
tile = np.zeros((10, 10), dtype=np.int8)
lnr = 0
elif line == "":
add_tile(num, tile)
else:
a = np.array([(0 if x == '.' else 1) for x in line], dtype=np.int8)
tile[lnr] = a
lnr += 1
add_tile(num, tile)
WIDTH = int(np.sqrt(len(tiles)))
# find top left corner
edges = defaultdict(int)
corner = None
for key, val in borders.items():
if len(val) == 1:
for item in val:
edges[item] += 1
for key, val in edges.items():
if val == 4:
corner = key
break
# rotate top left corner
corner_tile = tiles[corner]
joints = []
for x in range(4):
edge = edge_to_bin(corner_tile[0])
corner_tile = np.rot90(corner_tile)
if len(borders[edge]) == 2:
joints.append(edge)
if len(joints) == 2:
break
corner_tile = np.rot90(corner_tile)
# prepare plane
plane = np.zeros(((10-1)*WIDTH+1, (10-1)*WIDTH+1), dtype=np.int8)
plane[:10, :10] = corner_tile
used_tiles = {corner}
# orientate and add tiles to plane
for idx in range(1, len(tiles)):
x, y = (idx % WIDTH) * (10-1), (idx // WIDTH) * (10-1)
next_tile_top = None if y == 0 else borders[edge_to_bin(plane[y, x:x+10])]
next_tile_left = None if x == 0 else borders[edge_to_bin(plane[y:y+10, x].T)]
if next_tile_top is None:
next_tile = next_tile_left
elif next_tile_left is None:
next_tile = next_tile_top
else:
next_tile = next_tile_left.intersection(next_tile_top)
next_tile = next_tile.difference(used_tiles).pop()
used_tiles.add(next_tile)
tile = tiles[next_tile]
for transpose, rotate in rotations:
view = np.rot90(tile.T if transpose else tile, rotate)
if y != 0 and not np.all(np.equal(plane[y, x:x+10], view[0])):
continue
if x != 0 and not np.all(np.equal(plane[y:y+10, x], view[:, 0])):
continue
plane[y:y+10, x:x+10] = view
break
# remove all edges
plane = np.delete(plane, list(range(0, plane.shape[0], 10-1)), axis=0)
plane = np.delete(plane, list(range(0, plane.shape[1], 10-1)), axis=1)
# kernel
#
# ## ## ###
# # # # # #
kernel = np.array([
[0,0,0,0,0 ,0,0,0,0,0 ,0,0,0,0,0 ,0,0,0,1,0],
[1,0,0,0,0 ,1,1,0,0,0 ,0,1,1,0,0 ,0,0,1,1,1],
[0,1,0,0,1 ,0,0,1,0,0 ,1,0,0,1,0 ,0,1,0,0,0]
])
count = np.sum(kernel)
for transpose, rotate in rotations:
view = np.rot90(plane.T if transpose else plane, rotate)
neighbors = convolve(view, kernel, mode='valid')
if np.max(neighbors) == count:
print(len(np.where(view == 1)[0]) - len(np.where(neighbors == count)[0]) * count)