| import itertools | import itertools | ||||
| import sys | import sys | ||||
| import math | import math | ||||
| from functools import reduce | |||||
| import toolkit | import toolkit | ||||
| def apply(cell): | def apply(cell): | ||||
| key, *fns = cell | |||||
| string = tiles[key] | |||||
| string, *fns = cell | |||||
| for fn in fns: | for fn in fns: | ||||
| if fn is not None: | if fn is not None: | ||||
| string = fn(string) | string = fn(string) | ||||
| return '\n'.join( | return '\n'.join( | ||||
| '\n'.join(''.join([n[1:-1] | '\n'.join(''.join([n[1:-1] | ||||
| for n in ln]) | for n in ln]) | ||||
| for ln in zip(*[apply(cell).splitlines()[1:-1] for cell in row])) | |||||
| for row in grid | |||||
| for ln in zip(*[apply(grid[y, x]).splitlines()[1:-1] for x in range(size)])) | |||||
| for y in range(size) | |||||
| ) | ) | ||||
| return inner | return inner | ||||
| def noop(string): | |||||
| return string | |||||
| def variants(string): | |||||
| alts = [noop, flipH], [noop, flipV], [noop, rot90(1), rot90(2), rot90(3)] | |||||
| for ops in itertools.product(*alts): | |||||
| yield reduce(lambda s, f: f(s), ops, string), ops | |||||
| def search_in(source, target): | |||||
| source_lns = source.splitlines() | |||||
| target_lns = target.splitlines() | |||||
| h, w = len(target_lns), len(target_lns[0]) | |||||
| out = [] | |||||
| for y in range(len(source_lns)): | |||||
| for x in range(len(source_lns[0])): | |||||
| window = '\n'.join(ln[x:x + w] for ln in source_lns[y:y + h]) | |||||
| if re.fullmatch(target, window): | |||||
| out.append((x, y)) | |||||
| return out | |||||
| text = sys.stdin.read().strip() | text = sys.stdin.read().strip() | ||||
| tiles = {} | tiles = {} | ||||
| borders = {} | borders = {} | ||||
| tid = int(title.split()[1]) | tid = int(title.split()[1]) | ||||
| tiles[tid] = content | tiles[tid] = content | ||||
| borders[tid] = get_borders(content) | borders[tid] = get_borders(content) | ||||
| size = int(len(borders) ** 0.5) | |||||
| size = int(len(tiles) ** 0.5) | |||||
| adj = collections.defaultdict(set) | adj = collections.defaultdict(set) | ||||
| for (aid, A), (bid, B) in itertools.combinations(borders.items(), 2): | for (aid, A), (bid, B) in itertools.combinations(borders.items(), 2): | ||||
| if set(A) & set(B): | if set(A) & set(B): | ||||
| adj[aid].add(bid) | adj[aid].add(bid) | ||||
| adj[bid].add(aid) | adj[bid].add(aid) | ||||
| corn = [v for v, ks in adj.items() if len(ks) == 2] | corn = [v for v, ks in adj.items() if len(ks) == 2] | ||||
| print(math.prod(corn)) | |||||
| gids = [[None for _ in range(size)] for _ in range(size)] | gids = [[None for _ in range(size)] for _ in range(size)] | ||||
| gids[y][x], = adj[gids[y][x - 1]] & adj[gids[y - 1][x]] - {gids[y - 1][x - 1]} | 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)])) | options = list(itertools.product([None, flipH], [None, flipV], [None] + [rot90(n) for n in range(3)])) | ||||
| grid = {(y, x): (tiles[gids[y][x]],) for x in range(size) for y in range(size)} | |||||
| grid[0, 0] = (flipV(grid[0, 0][0]),) | |||||
| for x in range(1, size): | for x in range(1, size): | ||||
| goal = [ln[-1] for ln in apply(grid[0][x - 1]).splitlines()] | |||||
| goal = [ln[-1] for ln in apply(grid[0, x - 1]).splitlines()] | |||||
| for chain in options: | for chain in options: | ||||
| if goal == [ln[0] for ln in apply(grid[0][x] + chain).splitlines()]: | |||||
| grid[0][x] += chain | |||||
| if goal == [ln[0] for ln in apply(grid[0, x] + chain).splitlines()]: | |||||
| grid[0, x] += chain | |||||
| break | break | ||||
| for y in range(1, size): | for y in range(1, size): | ||||
| for x in range(size): | for x in range(size): | ||||
| goal = apply(grid[y - 1][x]).splitlines()[-1] | |||||
| goal = apply(grid[y - 1, x]).splitlines()[-1] | |||||
| for chain in options: | for chain in options: | ||||
| if goal == apply(grid[y][x] + chain).splitlines()[0]: | |||||
| grid[y][x] += chain | |||||
| if goal == apply(grid[y, x] + chain).splitlines()[0]: | |||||
| grid[y, x] += chain | |||||
| break | 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('#')) | |||||
| monster = ''' | |||||
| ..................#. | |||||
| #....##....##....### | |||||
| .#..#..#..#..#..#... | |||||
| '''[1:-1] | |||||
| src = render(grid) | |||||
| found = max([search_in(pic, monster) for pic, _ in variants(src)], key=len) | |||||
| print(src.count('#') - len(found) * monster.count('#')) |