#!/usr/bin/env python3 import sys import numpy as np #import matplotlib.pyplot as plt def toBits(l): return sum(int(b=='#') * (2**i) for i,b in enumerate(l)) def allSides(tile): a,b,c,d = tile[0, : ], tile[-1, : ], tile[:, 0], tile[:, -1] e,f,g,h = tile[0, ::-1], tile[-1, ::-1], tile[::-1, 0], tile[::-1, -1] return [a,b,c,d,e,f,g,h] def comb(tile): return [tile, np.fliplr(tile), np.rot90(tile), np.rot90(np.rot90(tile)), np.rot90(np.rot90(np.rot90(tile))), np.fliplr(np.rot90(tile)), np.fliplr(np.rot90(np.rot90(tile))), np.fliplr(np.rot90(np.rot90(np.rot90(tile))))] def getRight(tile): return toBits(tile[:,-1]) def getLeft(tile): return toBits(tile[:,0]) def getBot(tile): return toBits(tile[-1,:]) def getTop(tile): return toBits(tile[0,:]) def matchRight(tile1, tile2): for t in comb(tile2): if getRight(tile1) == getLeft(t): return t def matchBot(tile1, tile2): for t in comb(tile2): if getBot(tile1) == getTop(t): return t def parse_data(strings): tiles = {} for tile in strings: tid, *data = tile.split('\n') try: tid = tid.rstrip(':').split(' ')[1] except IndexError: continue grid = [[c for c in line] for line in data if len(line) >= 1] tiles[int(tid)] = np.array(grid) return tiles def get_sides(tiles): sides = {} for tid, tile in tiles.items(): for bits in map(lambda x: toBits(x), allSides(tile)): if bits not in sides: sides[bits] = [] sides[bits].append(tid) return sides def get_corners(tiles, sides): singles = {} for bits, xs in sides.items(): if len(xs) == 1: singles[xs[0]] = [] for sid in singles.keys(): matches = [x for side in allSides(tiles[sid]) for x in sides[toBits(side)]] singles[sid] = list(dict.fromkeys(matches)) corners = [] for sid, single in singles.items(): if len(single) < 4: corners.append(sid) return corners def build_picture(tiles, sides, start): picture = [[None for x in range(12)] for x in range(12)] matches = [x for side in allSides(tiles[corner]) for x in sides[toBits(side)]] matches = list(dict.fromkeys(matches)) matches.remove(corner) for t in comb(tiles[corner]): x, y = map(lambda v: tiles[v], matches) if matchRight(t,x) is not None and matchBot(t,y) is not None: break if matchRight(t,y) is not None and matchBot(t,x) is not None: break else: tiles[corner] = t picture[0][0] = corner for i, p in enumerate(picture[0]): s = sides[getRight(tiles[p])] s.remove(p) if len(s) > 0: tiles[s[0]] = matchRight(tiles[p], tiles[s[0]]) picture[0][i+1] = s[0] for j in range(1, len(picture)): z = sides[getBot(tiles[p])] z.remove(p) if len(z) > 0: tiles[z[0]] = matchBot(tiles[p], tiles[z[0]]) picture[j][i] = z[0] p = z[0] return picture def countSM(pic, sm): count = 0 y, x = pic.shape ys, xs = sm.shape for i in range(0, y-ys): for j in range(0, x-xs): if np.all(pic[i:i+ys,j:j+xs] | sm): count += 1 return count if __name__ == '__main__': tiles = parse_data(open(sys.argv[1]).read().split('\n\n')) sides = get_sides(tiles) corner = get_corners(tiles, sides)[0] p = build_picture(tiles, sides, corner) print(p[0][0] * p[0][-1] * p[-1][0] * p[-1][-1]) pic = np.concatenate(tuple(np.concatenate(tuple(tiles[x][1:-1,1:-1] for x in r), axis=1) for r in p), axis=0) grayscale = np.vectorize(lambda x: np.int8(x == "#"))(pic) print(grayscale) snake = \ [ " # " , "# ## ## ###" , " # # # # # # " ] sneasnake = np.array([[c for c in line] for line in snake]) sneasnake = np.vectorize(lambda x: np.int8(x != "#"))(sneasnake) for p in comb(grayscale): print(countSM(p, sneasnake)) print(np.count_nonzero(grayscale) - 26 * 15) # plt.show() # plt.imshow(grayscale) # plt.savefig("test.png")