Advent of Code 2020, now in the most terse and awful python possible
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
4.0 KiB

#!/usr/bin/env python3
from sys import argv
from math import sqrt
tiles = {}
for tile in map(lambda x: x.splitlines(), open(argv[1]).read().strip().split("\n\n")):
tiles[int(tile[0][5:-1])] = "".join(tile[1:])
size = int(sqrt(len(tiles)))
tsize = int(sqrt(len(next(iter(tiles.values())))))
sides = {}
sides_rev = {}
for tile in tiles:
data = tiles[tile]
up = data[0:tsize]
down = data[tsize ** 2 - tsize:tsize ** 2]
left = ""
right = ""
for x in range(tsize):
left += data[tsize * x]
right += data[tsize * x + tsize - 1]
up = up[::-1]
right = right[::-1]
sides[tile] = [
up, left, down, right,
up[::-1], left[::-1], down[::-1], right[::-1]
]
for i, side in enumerate(sides[tile]):
if side not in sides_rev:
sides_rev[side] = []
sides_rev[side].append((tile, i))
tiles_noconn = {}
for side in sides_rev:
if len(sides_rev[side]) == 1:
tile, orien = sides_rev[side][0]
if tile not in tiles_noconn:
tiles_noconn[tile] = []
tiles_noconn[tile].append(orien)
tiles_corner = [i for i, noconn in tiles_noconn.items() if len(noconn) == 4]
print(tiles_corner[0] * tiles_corner[1] * tiles_corner[2] * tiles_corner[3])
# Figure out the orientation of the top-left corner tile we pick
orien = tiles_noconn[tiles_corner[0]]
if 3 in orien and 0 in orien:
orien = 3
else:
orien = min(orien)
corner = (tiles_corner[0], orien)
def get_orien(base, add):
if base & 4:
add = 4 - add
return ((base + add) & 3) | (base & 4)
# Create a connection graph
conn = {}
for side in sides_rev.values():
if len(side) != 2:
continue
conn[side[0]] = side[1]
conn[side[1]] = side[0]
# Put the grid together, starting from the top-left corner, going right then down
grid = []
for y in range(size):
if y:
tile_top, orien_top = grid[y-1][0]
orien_top = get_orien(orien_top, 2)
tile, orien = conn[(tile_top, orien_top)]
orien ^= 4
row = [(tile, orien)]
else:
row = [corner]
for x in range(1, size):
tile_left, orien_left = row[x-1]
orien_left = get_orien(orien_left, 3)
tile, orien = conn[(tile_left, orien_left)]
orien = get_orien(orien, -1)
orien ^= 6
row.append((tile, orien))
grid.append(row)
def rotate(tile, orien):
# Flip the tile if necessary
if orien >= 4:
tile = [row[::-1] for row in tile]
orien = (4 - orien) % 4
# Rotate the tile
for x in range(orien):
tile = [[tile[len(iy) - x - 1][y] for x, ix in enumerate(iy)] for y, iy in enumerate(tile)]
return tile
# Create image
ssize = tsize - 2
image = [[0] * size * ssize for x in range(size * ssize)]
for y, row in enumerate(grid):
y *= ssize
for x, (tile, orien) in enumerate(row):
x *= ssize
# Convert tile data into grid, strip the edges
tile = [tiles[tile][x + 1:x + tsize - 1] for x in range(tsize, tsize ** 2 - tsize, tsize)]
tile = rotate(tile, orien)
# Store into image
for iy, irow in enumerate(tile):
for ix, c in enumerate(irow):
image[y + iy][x + ix] = c
# Find monsters
monster = """
#
# ## ## ###
# # # # # #
"""
monster = [(y,x) for y, line in enumerate(monster[1:-1].split("\n")) for x, char in enumerate(line) if char == "#"]
monster_h = 3
monster_w = 20
for orien in range(8):
cur_image = rotate(image, orien)
count = 0
for y in range(size * ssize - monster_h):
for x in range(size * ssize - monster_w):
for m in monster:
if cur_image[m[0] + y][m[1] + x] != "#":
break
else:
for m in monster:
cur_image[m[0] + y][m[1] + x] = "O"
count += 1
if count:
break
# Tally roughness
print(sum(1 for row in cur_image for char in row if char == "#"))
# for row in cur_image:
# print("".join(row))