Day 20; 2020 is done!
This commit is contained in:
parent
2a07c6458c
commit
1310c4314e
133
2020/day20.py
Normal file
133
2020/day20.py
Normal file
|
@ -0,0 +1,133 @@
|
|||
import math
|
||||
import sys
|
||||
|
||||
DIM = 10
|
||||
|
||||
def main():
|
||||
groups = sys.stdin.read().rstrip().split("\n\n")
|
||||
tiles = {}
|
||||
for group in groups:
|
||||
lines = [line.rstrip() for line in group.splitlines()]
|
||||
tile_id = int(lines[0][5:].rstrip(":"))
|
||||
tiles[tile_id] = list(map(list, lines[1:]))
|
||||
idim = int(math.sqrt(len(tiles)))
|
||||
|
||||
# Part 1
|
||||
# instructions are not very clean, but looks like we only have to check 3
|
||||
# rotations + optionally horizontal flip. vertical flip is not needed on
|
||||
# example, and a vflip is actually rot180 + hflip.
|
||||
perms = {}
|
||||
for tile_id, tile in tiles.items():
|
||||
perms[tile_id] = permutations(tile)
|
||||
image = search({}, 0, 0, [], perms, idim)
|
||||
tl = image[(0, 0)]
|
||||
tr = image[(0, idim - 1)]
|
||||
bl = image[(idim - 1, 0)]
|
||||
br = image[(idim - 1, idim - 1)]
|
||||
print("Multiplied corners:", tl[0] * tr[0] * bl[0] * br[0])
|
||||
|
||||
# Part 2
|
||||
pattern = [
|
||||
(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)
|
||||
]
|
||||
merged = merge_tiles(image, perms, idim)
|
||||
global DIM
|
||||
DIM = len(merged)
|
||||
perms = permutations(merged)
|
||||
max_monsters = max(find_monsters(perm, pattern) for perm in perms)
|
||||
num_dashes = sum(c == "#" for row in merged for c in row)
|
||||
monster_dashes = max_monsters * len(pattern)
|
||||
print("Roughness:", num_dashes - monster_dashes)
|
||||
|
||||
def permutations(tile):
|
||||
return [
|
||||
tile,
|
||||
hflip(tile),
|
||||
rot90(tile),
|
||||
rot90(hflip(tile)),
|
||||
rot90(rot90(tile)),
|
||||
rot90(rot90(hflip(tile))),
|
||||
rot90(rot90(rot90(tile))),
|
||||
rot90(rot90(rot90(hflip(tile)))),
|
||||
]
|
||||
|
||||
def rot90(tile):
|
||||
rtile = [[None for _ in range(DIM)] for _ in range(DIM)]
|
||||
for x in range(DIM):
|
||||
for y in range(DIM):
|
||||
rtile[x][y] = tile[DIM - y - 1][x]
|
||||
return rtile
|
||||
|
||||
def hflip(tile):
|
||||
return [row[::] for row in tile[::-1]]
|
||||
|
||||
def search(image, x, y, placed, perms, idim):
|
||||
fitting = find_fitting(image, x, y, placed, perms)
|
||||
if (x, y) == (idim - 1, idim - 1) and len(fitting) == 1:
|
||||
image[(idim - 1, idim - 1)] = fitting[0]
|
||||
return image
|
||||
for fit in fitting:
|
||||
nimage = image.copy()
|
||||
nimage[(x, y)] = fit
|
||||
nx = (x + 1) % idim
|
||||
ny = y if nx > 0 else y + 1
|
||||
nplaced = placed + [fit[0]]
|
||||
if (complete_image := search(nimage, nx, ny, nplaced, perms, idim)):
|
||||
return complete_image
|
||||
|
||||
def find_fitting(image, x, y, placed, perms):
|
||||
up_perm = image.get((x, y - 1))
|
||||
up = perms[up_perm[0]][up_perm[1]] if up_perm else None
|
||||
left_perm = image.get((x - 1, y))
|
||||
left = perms[left_perm[0]][left_perm[1]] if left_perm else None
|
||||
down_perm = image.get((x, y + 1))
|
||||
down = perms[down_perm[0]][down_perm[1]] if down_perm else None
|
||||
right_perm = image.get((x + 1, y))
|
||||
right = perms[right_perm[0]][right_perm[1]] if right_perm else None
|
||||
return [
|
||||
(tile_id, tile_perm_id)
|
||||
for tile_id, tile_perms in perms.items()
|
||||
if tile_id not in placed
|
||||
for tile_perm_id, tile_perm in enumerate(tile_perms)
|
||||
if fits(tile_perm, up, left, down, right)
|
||||
]
|
||||
|
||||
def fits(tile, up, left, down, right):
|
||||
return all((
|
||||
not up or tile[0] == up[-1],
|
||||
not down or tile[-1] == down[0],
|
||||
not left or [r[0] for r in tile] == [r[-1] for r in left],
|
||||
not right or [r[-1] for r in tile] == [r[0] for r in right],
|
||||
))
|
||||
|
||||
def merge_tiles(image, perms, idim):
|
||||
tdim = DIM - 2
|
||||
merged = [[] for _ in range(idim * tdim)]
|
||||
for ix in range(idim):
|
||||
for iy in range(idim):
|
||||
i_tile_id, i_perm_id = image[(ix, iy)]
|
||||
tile = perms[i_tile_id][i_perm_id]
|
||||
for y, row in enumerate(tile[1:-1]):
|
||||
merged[iy * tdim + y] += row[1:-1]
|
||||
return merged
|
||||
|
||||
def find_monsters(image, pattern):
|
||||
w, h = len(image[0]), len(image)
|
||||
num_monsters = 0
|
||||
for x in range(w):
|
||||
for y in range(h):
|
||||
for px, py in pattern:
|
||||
ix, iy = x + px, y + py
|
||||
if not 0 <= ix < w or not 0 <= iy < h:
|
||||
break
|
||||
if image[iy][ix] != "#":
|
||||
break
|
||||
else:
|
||||
num_monsters += 1
|
||||
return num_monsters
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue