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
146 lines
4.0 KiB
4 years ago
|
#!/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))
|