Advertisement
hhoppe

Advent of code 2020 day 20

Dec 20th, 2020 (edited)
154
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.67 KB | None | 0 0
  1. def process(s, part2=False):
  2.  
  3.   def to_grid(s):
  4.     return np.array(list(map(list, s.strip('\n').splitlines())))
  5.  
  6.   tiles = {int(t[5:9]): to_grid(t[11:]) for t in s.strip('\n').split('\n\n')}
  7.   n = int(len(tiles)**0.5)
  8.   rotations = tuple(range(8))  # 4 proper rotations * 2 flips
  9.  
  10.   def rotate(tile, rotation):
  11.     return np.rot90(tile if rotation < 4 else tile[::-1], rotation % 4)
  12.  
  13.   edge_count = collections.Counter(
  14.       tuple(rotate(tile, rotation)[0])  # extract top edge
  15.       for tile in tiles.values() for rotation in rotations
  16.   )
  17.  
  18.   def is_corner(tile):
  19.     # A corner tile has 2 unique edges, which appear 4 times after flips.
  20.     return sum(edge_count[tuple(rotate(tile, rotation)[0])] == 1
  21.                for rotation in rotations) == 4
  22.  
  23.   corners = [index for index, tile in tiles.items() if is_corner(tile)]
  24.   assert len(corners) == 4
  25.   if not part2:
  26.     return np.prod(corners)
  27.  
  28.   index = corners[0]
  29.   tile = tiles[index]
  30.   rotation, _ = (rotation for rotation in rotations  # 2 solutions due to flip
  31.                  if (edge_count[tuple(rotate(tile, rotation)[0])] == 1 and
  32.                      edge_count[tuple(rotate(tile, rotation)[:, 0])] == 1))
  33.   layout = np.empty((n, n), dtype=object)
  34.   layout[0, 0] = (index, rotation)
  35.  
  36.   def find(not_index, subview, desired):
  37.     "Returns a tile index and rotation such that its subview matches desired."
  38.     for index in tiles:
  39.       if index != not_index:
  40.         for rotation in rotations:
  41.           if np.all(rotate(tiles[index], rotation)[subview] == desired):
  42.             return index, rotation
  43.     raise AssertionError
  44.  
  45.   for y, x in np.ndindex(n, n):
  46.     if x > 0:
  47.       left_index, left_rotation = layout[y, x - 1]
  48.       desired_left = rotate(tiles[left_index], left_rotation)[:, -1]
  49.       layout[y, x] = find(left_index, np.s_[:, 0], desired_left)
  50.     elif y > 0:
  51.       top_index, top_rotation = layout[y - 1, x]
  52.       desired_top = rotate(tiles[top_index], top_rotation)[-1, :]
  53.       layout[y, x] = find(top_index, np.s_[0, :], desired_top)
  54.  
  55.   def block(y, x):
  56.     index, rotation = layout[y, x]
  57.     return rotate(tiles[index], rotation)[1:-1, 1:-1]
  58.  
  59.   grid = np.block([[block(y, x) for x in range(n)] for y in range(n)])
  60.  
  61.   pattern = '                  # #    ##    ##    ### #  #  #  #  #  #   '
  62.   pattern = np.array(list(pattern)).reshape(3, -1) == '#'
  63.  
  64.   for rotation in rotations:
  65.     gridview = rotate(grid, rotation)
  66.     for y, x in np.ndindex(*np.array(gridview.shape) - pattern.shape):
  67.       subgrid = gridview[y:y + pattern.shape[0], x:x + pattern.shape[1]]
  68.       if np.all(~pattern | (subgrid != '.')):
  69.         subgrid[pattern] = 'O'
  70.  
  71.   return np.count_nonzero(grid == '#')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement