import fileinput from pprint import pprint import itertools from copy import deepcopy N = 10 # # Parse the input # current_tile = None tiles = [] for line in fileinput.input(): line = line.strip() if not line: continue if line.startswith("Tile "): if current_tile: assert len(current_tile) == N tiles.append((current_id, current_tile)) current_id = int(line[5:-1]) current_tile = [] else: assert len(line) == N current_tile.append(list(line)) assert len(current_tile) == N tiles.append((current_id, current_tile)) def aligns_right(left, right): return all(left[y][-1] == right[y][0] for y in range(len(left))) def aligns_bottom(top, bottom): return all(top[-1][x] == bottom[0][x] for x in range(len(top))) def aligns(apos, b): ax, ay = apos _, a = kek[(ax, ay)] if aligns_right(a, b): return 1, 0 if aligns_right(b, a): return -1, 0 if aligns_bottom(a, b): return 0, 1 if aligns_bottom(b, a): return 0, -1 return None def rotate(a): output = deepcopy(a) for y in range(len(a)): for x in range(len(a)): output[-1 - x][y] = a[y][x] return output def flip(a): output = [None] * len(a) for y in range(len(a)): output[y] = list(reversed(a[y])) return output def rotate_align(a_pos, b): """ Rotates and flips a and checks if it aligns for every possible orientation. """ if d := aligns(a_pos, b): return b, d bf = flip(b) if d := aligns(a_pos, bf): return bf, d b = rotate(b) if d := aligns(a_pos, b): return b, d bf = flip(b) if d := aligns(a_pos, bf): return bf, d b = rotate(b) if d := aligns(a_pos, b): return b, d bf = flip(b) if d := aligns(a_pos, bf): return bf, d b = rotate(b) if d := aligns(a_pos, b): return b, d bf = flip(b) if d := aligns(a_pos, bf): return bf, d return None, None positions = {tiles[0][0]: ((0, 0), tiles[0][1])} kek = {(0, 0): tiles[0]} while len(positions) != len(tiles): for a_id, _ in tiles: ((a_x, a_y), a_tile) = positions.get(a_id, ((None, None), None)) if not a_tile: continue for b_id, b_tile in tiles: if b_id in positions or a_id == b_id: continue transformed, dpos = rotate_align((a_x, a_y), b_tile) if dpos is not None: dx, dy = dpos b_x = a_x + dx b_y = a_y + dy positions[b_id] = (b_x, b_y), transformed kek[(b_x, b_y)] = b_id, transformed min_y = min(y for ((_, y), _) in positions.values()) max_y = max(y for ((_, y), _) in positions.values()) min_x = min(x for ((x, _), _) in positions.values()) max_x = max(x for ((x, _), _) in positions.values()) tilemap = {pos: (id, tile) for id, (pos, tile) in positions.items()} bl, _ = tilemap[(min_x, min_y)] br, _ = tilemap[(max_x, min_y)] tl, _ = tilemap[(min_x, max_y)] tr, _ = tilemap[(max_x, max_y)] print("Part 1:", tl * tr * bl * br) def is_snek(b, x, y): for (x_, y_) in ( (0, 0), (1, 1), (4, 1), (5, 0), (6, 0), (7, 1), (10, 1), (11, 0), (12, 0), (13, 1), (16, 1), (17, 0), (18, 0), (18, -1), (19, 0), ): try: if b[y_ + y][x_ + x] == ".": return False except: return False return True def find_sneks(b): sneks = [] for (x, y) in itertools.product(range(8 * 12), repeat=2): if is_snek(b, x, y): print(x, y, "is snek") sneks.append((x, y)) return sneks def rotate_find_sneks(b): if d := find_sneks(b): return b, d bf = flip(b) if d := find_sneks(bf): return bf, d b = rotate(b) if d := find_sneks(b): return b, d bf = flip(b) if d := find_sneks(bf): return bf, d b = rotate(b) if d := find_sneks(b): return b, d bf = flip(b) if d := find_sneks(bf): return bf, d b = rotate(b) if d := find_sneks(b): return b, d bf = flip(b) if d := find_sneks(bf): return bf, d def remove_snek(snek): # :( (x, y) = snek for (x_, y_) in ( (0, 0), (1, 1), (4, 1), (5, 0), (6, 0), (7, 1), (10, 1), (11, 0), (12, 0), (13, 1), (16, 1), (17, 0), (18, 0), (18, -1), (19, 0), ): big_chungus[y + y_][x + x_] = '.' chungus = [["."] * 8 * (max_x - min_x + 1) for _ in range(8 * (max_y - min_y + 1))] for (x, y), (id, tile) in tilemap.items(): x_ = (x - min_x) * 8 y_ = (y - min_y) * 8 for i, row in enumerate(tile[1:-1]): for j, c in enumerate(row[1:-1]): chungus[i + y_][j + x_] = c # for y, row in enumerate(chungus): # print("".join(row)) big_chungus, sneks = rotate_find_sneks(chungus) for snek in sneks: remove_snek(snek) part2 = 0 for row in big_chungus: for c in row: if c == "#": part2 += 1 print("Part 2:", part2)