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)