Advertisement
Guest User

Advent of Code Day 20 - tilemakerpro.py

a guest
Dec 27th, 2020
295
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.73 KB | None | 0 0
  1. from collections import defaultdict, deque
  2. from math import sqrt
  3. from tile import Tile
  4.  
  5.  
  6. AdjacencyMap = dict[int, list[int]]
  7. ID = int
  8.  
  9.  
  10. class TileMakerPro:
  11.     def __init__(self, tiles: dict[ID, Tile]):
  12.         self.tiles: dict[ID, Tile] = tiles
  13.         self.dims: int = int(sqrt(len(self.tiles)))
  14.         self.adjacency_map: AdjacencyMap = self.make_adjacency_map()
  15.         self.corners: tuple[ID, ...] = self.find_corners()
  16.         self.start: ID = self.corners[0]
  17.         self.arranged: set[ID] = set()
  18.         self.unlinked: set[ID] = set([tile for tile in self.tiles])
  19.         self.map_is_arranged: bool = False
  20.         self.image: list[list[str]] = []
  21.  
  22.     def make_adjacency_map(self) -> AdjacencyMap:
  23.         tile_ids = [tile for tile in self.tiles.keys()]
  24.         adjacency_map: AdjacencyMap = defaultdict(list)
  25.         for id1 in tile_ids:
  26.             for id2 in tile_ids:
  27.                 if id1 != id2 and self.tiles[id1].is_adjacent(self.tiles[id2]):
  28.                     adjacency_map[id1].append(id2)
  29.         return adjacency_map
  30.  
  31.     def find_corners(self) -> tuple[ID, ...]:
  32.         return tuple(
  33.             [id for id in self.adjacency_map if len(self.adjacency_map[id]) == 2]
  34.         )
  35.  
  36.     def make_connection(self, t1: ID, t2: ID) -> bool:
  37.         n_rotations = 0
  38.         while True:
  39.             if self.tiles[t1].right_edge == self.tiles[t2].left_edge:
  40.                 self.tiles[t1].adjacent["r"] = t2
  41.                 self.tiles[t2].adjacent["l"] = t1
  42.                 return True
  43.             elif self.tiles[t1].left_edge == self.tiles[t2].right_edge:
  44.                 self.tiles[t1].adjacent["l"] = t2
  45.                 self.tiles[t2].adjacent["r"] = t1
  46.                 return True
  47.             elif self.tiles[t1].top_edge == self.tiles[t2].bottom_edge:
  48.                 self.tiles[t1].adjacent["t"] = t2
  49.                 self.tiles[t2].adjacent["b"] = t1
  50.                 return True
  51.             elif self.tiles[t1].bottom_edge == self.tiles[t2].top_edge:
  52.                 self.tiles[t1].adjacent["b"] = t2
  53.                 self.tiles[t2].adjacent["t"] = t1
  54.                 return True
  55.             self.tiles[t2].rotate()
  56.             n_rotations += 1
  57.             if n_rotations % 4 == 0:
  58.                 self.tiles[t2].flip()
  59.             if n_rotations % 8 == 0:
  60.                 return False
  61.  
  62.     def make_first_connection(self, t1: ID, t2: ID):
  63.         n_rotations = 0
  64.         while True:
  65.             if self.make_connection(t1, t2):
  66.                 return True
  67.             self.tiles[t1].rotate()
  68.             n_rotations += 1
  69.             if n_rotations % 4 == 0:
  70.                 self.tiles[t1].flip()
  71.  
  72.     def arrange_map(self):
  73.         queue: deque[ID] = deque([self.start])
  74.         while queue:
  75.             current: ID = queue.popleft()
  76.             if self.arranged:
  77.                 for tile in self.adjacency_map[current]:
  78.                     self.make_connection(current, tile)
  79.                     if tile not in self.arranged:
  80.                         queue.append(tile)
  81.                     self.arranged.add(current)
  82.             else:
  83.                 # set first connections
  84.                 self.arranged.add(current)
  85.                 self.make_first_connection(current, self.adjacency_map[current][0])
  86.                 queue.append(self.adjacency_map[current][0])
  87.                 self.make_connection(current, self.adjacency_map[current][1])
  88.                 queue.append(self.adjacency_map[current][1])
  89.         self.map_is_arranged = True
  90.  
  91.     def find_top_left_tile(self) -> ID:
  92.         for tile in self.tiles:
  93.             if all(
  94.                 [
  95.                     self.tiles[tile].adjacent["b"] is not None,
  96.                     self.tiles[tile].adjacent["t"] is None,
  97.                     self.tiles[tile].adjacent["r"] is not None,
  98.                     self.tiles[tile].adjacent["l"] is None,
  99.                 ]
  100.             ):
  101.                 return tile
  102.  
  103.     def consolidate_tiles(self) -> Tile:
  104.         if self.map_is_arranged is False:
  105.             raise ValueError(
  106.                 "Incomplete map arrangement.  Call 'self.arrange_map() first."
  107.             )
  108.         image_map = []
  109.         top_left = self.find_top_left_tile()
  110.         current_row, current_col = top_left, top_left
  111.         while current_row or current_col:
  112.             row = [[] for _ in range(len(self.tiles[top_left].inner_tile))]
  113.             while current_col:  # add rows
  114.                 for i in range(len(row)):
  115.                     row[i].extend(self.tiles[current_col].inner_tile[i])
  116.                 current_col = self.tiles[current_col].adjacent["r"]
  117.             image_map.extend(row)
  118.             current_row = self.tiles[current_row].adjacent["b"]
  119.             current_col = current_row
  120.         return Tile(0, image_map)
  121.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement