|
- # flake8: noqa
- import re
- import collections
- import itertools
- import sys
- import math
-
- import toolkit
-
-
- def apply(cell):
- key, *fns = cell
- string = tiles[key]
- for fn in fns:
- if fn is not None:
- string = fn(string)
- return string
-
-
- def apply2(string, fns):
- for fn in fns:
- if fn is not None:
- string = fn(string)
- return string
-
-
- def render(grid):
- return '\n'.join(
- '\n'.join(''.join([n[1:-1]
- for n in ln])
- for ln in zip(*[apply(cell).splitlines()[1:-1] for cell in row]))
- for row in grid
- )
-
-
- def get_borders(content):
- a, *_, b = content.splitlines()
- c, *_, d = [''.join(ln) for ln in zip(*content.splitlines())]
- return [a, b, c, d, a[::-1], b[::-1], c[::-1], d[::-1]]
-
-
- def flipV(string):
- return '\n'.join(ln for ln in string.splitlines()[::-1])
-
-
- def flipH(string):
- return '\n'.join(ln[::-1] for ln in string.splitlines())
-
-
- def rot90(n):
- def inner(string):
- return toolkit.render({k * 1j ** n: v for k, v in toolkit.read_image(string)[0].items()})
- return inner
-
-
- text = sys.stdin.read().strip()
- tiles = {}
- borders = {}
- for line in text.split('\n\n'):
- title, content = line.split(':\n')
- tid = int(title.split()[1])
- tiles[tid] = content
- borders[tid] = get_borders(content)
-
-
- size = int(len(borders) ** 0.5)
-
-
- adj = collections.defaultdict(set)
- for (aid, A), (bid, B) in itertools.combinations(borders.items(), 2):
- if set(A) & set(B):
- adj[aid].add(bid)
- adj[bid].add(aid)
-
- corn = [v for v, ks in adj.items() if len(ks) == 2]
-
- gids = [[None for _ in range(size)] for _ in range(size)]
-
- gids[0][0] = corn[0]
- gids[1][0], gids[0][1] = adj[gids[0][0]]
- gids[1][1], = adj[gids[1][0]] & adj[gids[0][1]] - {gids[0][0]}
-
- for x in range(2, size):
- gids[0][x], = adj[gids[0][x - 1]] - {gids[0][x - 2], gids[1][x - 1]}
- gids[1][x], = adj[gids[0][x]] & adj[gids[1][x - 1]] - {gids[0][x - 1]}
-
- for y in range(2, size):
- gids[y][0], = adj[gids[y - 1][0]] - {gids[y - 2][0], gids[y - 1][1]}
- for x in range(1, size):
- gids[y][x], = adj[gids[y][x - 1]] & adj[gids[y - 1][x]] - {gids[y - 1][x - 1]}
-
-
- grid = [[(gids[y][x],) for x in range(size)] for y in range(size)]
- grid[0][0] += (flipV,)
-
- options = list(itertools.product([None, flipH], [None, flipV], [None] + [rot90(n) for n in range(3)]))
-
- for x in range(1, size):
- goal = [ln[-1] for ln in apply(grid[0][x - 1]).splitlines()]
- for chain in options:
- if goal == [ln[0] for ln in apply(grid[0][x] + chain).splitlines()]:
- grid[0][x] += chain
- break
-
- for y in range(1, size):
- for x in range(size):
- goal = apply(grid[y - 1][x]).splitlines()[-1]
- for chain in options:
- if goal == apply(grid[y][x] + chain).splitlines()[0]:
- grid[y][x] += chain
- break
-
-
- monster = ''' #
- # ## ## ###
- # # # # # # '''
- kuk, W, H = toolkit.read_image(monster)
- goal = re.compile(toolkit.render(kuk).replace('\n', '').replace(' ', '.'))
-
- cnt = 0
- for chain in options:
- grody = apply2(render(grid), chain)
- grok = [list(line) for line in grody.splitlines()]
- for y in range(len(grok) - H):
- for x in range(len(grok[0]) - W):
- sample = ''.join(''.join(grok[y + dy][x + dx] for dx in range(W)) for dy in range(H))
- cnt += bool(goal.fullmatch(sample))
- if cnt:
- break
-
- print(grody)
- print(grody.count('#') - cnt * monster.count('#'))
|