22.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. from queue import deque
  2. from itertools import zip_longest
  3. def transpose(l):
  4. return list(zip_longest(*l, fillvalue=None))
  5. def rot_left(grid):
  6. return list(reversed(transpose(grid)))
  7. def rot_right(grid):
  8. return list(transpose(reversed(grid)))
  9. lines = [line[:-1] for line in open("22.input")]
  10. board = [line for line in lines[:-2]]
  11. max_width = max(len(line) for line in board)
  12. board = [line + " " * max(0, max_width - len(line)) for line in board]
  13. def show(grid):
  14. for line in grid:
  15. for c in line:
  16. print(c, end='')
  17. print("")
  18. def walk(steps, board, pos):
  19. for _ in range(steps):
  20. x, y = pos
  21. new_x = pos[0] + 1
  22. if new_x >= len(board[y]) or board[y][new_x] == " ":
  23. new_x = [x for x, c in enumerate(board[y]) if c != " "][0]
  24. if board[y][new_x] == '#':
  25. break
  26. else:
  27. pos = (new_x, y)
  28. return (board, pos)
  29. def rotate(rot, board, pos, facing):
  30. if rot == "R":
  31. board = rot_left(board)
  32. x, y = pos
  33. pos = (y, len(board)-x-1)
  34. facing = (facing + 1) % 4
  35. elif rot == "L":
  36. x, y = pos
  37. new_x = len(board) - y - 1
  38. board = rot_right(board)
  39. pos = (new_x, x)
  40. facing = (facing - 1) % 4
  41. return (board, pos, facing)
  42. def part1(board, pw):
  43. pw = deque(pw)
  44. pos = ([x for x, c in enumerate(board[0]) if c == '.'][0], 0)
  45. facing = 0
  46. num = ""
  47. while pw:
  48. char = pw.popleft()
  49. if char == 'R' or char == 'L':
  50. steps = int(num)
  51. board, pos = walk(steps, board, pos)
  52. board, pos, facing = rotate(char, board, pos, facing)
  53. num = ""
  54. else:
  55. num += char
  56. board, pos = walk(int(num), board, pos)
  57. final_facing = facing
  58. while facing != 0:
  59. board, pos, facing = rotate("R", board, pos, facing)
  60. pos = (pos[0] + 1, pos[1] + 1)
  61. print(pos[1] * 1000 + pos[0] * 4 + final_facing)
  62. part1(board, lines[-1])
  63. grid_sz = 50
  64. def empty_side():
  65. return [" " * grid_sz] * grid_sz
  66. def extract_side(board, pos):
  67. return [line[pos[0] * grid_sz : (pos[0] + 1) * grid_sz] for line in board[pos[1] * grid_sz : (pos[1] + 1) * grid_sz]]
  68. dir_to_sym = [">", "v", "<", "^"]
  69. def build_cube(board):
  70. neighbours = {}
  71. sides = set()
  72. side = ([x for x, c in enumerate(board[0]) if c == '.'][0] // grid_sz, 0)
  73. q = deque()
  74. q.append(side)
  75. while q:
  76. side = q.popleft()
  77. x, y = side
  78. if side not in neighbours:
  79. neighbours[side] = [None, None, None, None]
  80. for i, (dx, dy) in enumerate([(1, 0), (0, 1), (-1, 0), (0, -1)]):
  81. new_x, new_y = x + dx, y + dy
  82. new = (new_x, new_y)
  83. if new_x < 0:
  84. continue
  85. if new_y < 0:
  86. continue
  87. try:
  88. if board[new_y * grid_sz][new_x * grid_sz] != " ":
  89. if new not in neighbours:
  90. neighbours[new] = [None, None, None, None]
  91. neighbours[side][i] = (new, (i + 2) % 4)
  92. neighbours[new][(i+2) % 4] = (side, i % 4)
  93. if (new_x, new_y) not in sides:
  94. sides.add((new_x, new_y))
  95. q.append((new_x, new_y))
  96. except IndexError:
  97. continue
  98. for _ in range(4):
  99. for pos, nbrs in neighbours.items():
  100. # Fold corners
  101. for i in range(4):
  102. left = nbrs[i]
  103. right = nbrs[(i+1) % 4]
  104. if left is not None and right is not None:
  105. if neighbours[left[0]][(i+1)%4] is not None:
  106. continue
  107. if left[0] in [a[0] for a in neighbours[right[0]] if a is not None]:
  108. continue
  109. rel_left = (i - left[1]) % 4
  110. rel_right = ((i + 1) - right[1]) % 4
  111. rel = (rel_left - rel_right) % 4
  112. print("Looking at", pos)
  113. print("Relative direction:", rel_left, rel_right, rel)
  114. if rel == 0:
  115. print(left[0], "has", right[0], "to the", dir_to_sym[(i + 1)%4])
  116. print(right[0], "has", left[0], "to the", dir_to_sym[(i + 0)%4])
  117. print("-" * 20)
  118. neighbours[left[0]][(i+1)%4] = (right[0], (i+0)%4)
  119. neighbours[right[0]][(i+0)%4] = (left[0], (i+1)%4)
  120. elif rel == 1:
  121. print(left[0], "has", right[0], "to the", dir_to_sym[(i + 1)%4])
  122. print(right[0], "has", left[0], "to the", dir_to_sym[(i + 1)%4])
  123. print("-" * 20)
  124. neighbours[left[0]][(i+1)%4] = (right[0], (i+1)%4)
  125. neighbours[right[0]][(i+1)%4] = (left[0], (i+1)%4)
  126. else:
  127. raise ValueError("Weird grid?")
  128. for pair in neighbours.items():
  129. print(pair)
  130. # Finish the fold :DDD
  131. for i in range(2):
  132. for start in [(pos, nbrs) for (pos, nbrs) in neighbours.items() if None in nbrs]:
  133. print("Start:", start)
  134. pos, nbrs = start
  135. for direction in [(2 + i) % 4 for i, n in enumerate(nbrs) if n is None]:
  136. start_dir = direction
  137. print("Direction:", direction)
  138. for i in range(3):
  139. if neighbours[pos][direction] is None:
  140. break
  141. pos, new_dir = neighbours[pos][direction]
  142. direction = (2 + new_dir) % 4
  143. print("pos:", pos, "Direction:", direction)
  144. else:
  145. neighbours[start[0]][(2 + start_dir) % 4] = (pos, direction)
  146. neighbours[pos][direction] = (start[0], (2 + start_dir) % 4)
  147. for pair in neighbours.items():
  148. print(pair)
  149. return neighbours
  150. def part2(board, pw):
  151. #neighbours = build_cube(board)
  152. #print(neighbours)
  153. neighbours = {
  154. (1, 0): [((2, 0), 2), ((1, 1), 3), ((0, 2), 2), ((0, 3), 2)],
  155. (2, 0): [((1, 2), 0), ((1, 1), 0), ((1, 0), 0), ((0, 3), 1)],
  156. (1, 1): [((2, 0), 1), ((1, 2), 3), ((0, 2), 3), ((1, 0), 1)],
  157. (1, 2): [((2, 0), 0), ((0, 3), 0), ((0, 2), 0), ((1, 1), 1)],
  158. (0, 2): [((1, 2), 2), ((0, 3), 3), ((1, 0), 2), ((1, 1), 2)],
  159. (0, 3): [((1, 2), 1), ((2, 0), 3), ((1, 0), 3), ((0, 2), 1)]}
  160. if False:
  161. edges = set()
  162. for pos, nbrs in neighbours.items():
  163. for i, (n, d) in enumerate(nbrs):
  164. if (n, d) in edges:
  165. print("Duplicate:", (n, d))
  166. break
  167. edges.add((n, d))
  168. print(pos, "has", n, "to the", dir_to_sym[i])
  169. for side in neighbours.keys():
  170. for direction in range(4):
  171. current = side
  172. #print("Start:", current, "Direction:", direction)
  173. for i in range(4):
  174. side, new_dir = neighbours[side][direction]
  175. direction = (2 + new_dir) % 4
  176. #print("side:", side, "Direction:", direction)
  177. print(side, current)
  178. assert current == side
  179. side = ([x for x, c in enumerate(board[0]) if c == '.'][0] // grid_sz, 0)
  180. grid = extract_side(board, side)
  181. pos = ([x for x, c in enumerate(grid[0]) if c == '.'][0], 0)
  182. facing = 0
  183. num = ""
  184. path = set()
  185. pw = deque(pw)
  186. pw.append('S')
  187. while pw:
  188. char = pw.popleft()
  189. if char == 'R' or char == 'L' or char == 'S':
  190. rot = char
  191. steps = int(num)
  192. #print("grid:", side, "pos:", pos, "face:", dir_to_sym[facing])
  193. #show(grid)
  194. for _ in range(steps):
  195. x, y = pos
  196. new_x = pos[0] + 1
  197. new_pos = (new_x, pos[1])
  198. new_grid = grid
  199. new_side = side
  200. new_facing = facing
  201. if new_x >= grid_sz:
  202. # Find next side
  203. new_side, entry_dir = neighbours[side][facing]
  204. rel_dir = entry_dir - facing
  205. new_facing = (entry_dir + 2) % 4
  206. new_grid = extract_side(board, new_side)
  207. for _ in range(new_facing):
  208. new_grid = rot_left(new_grid)
  209. new_x = 0
  210. new_pos = (0, y)
  211. if new_grid[y][new_x] == '#':
  212. break
  213. else:
  214. side = new_side
  215. pos = new_pos
  216. grid = new_grid
  217. facing = new_facing
  218. #print("grid:", side, "pos:", pos, "face:", dir_to_sym[facing])
  219. #show(grid)
  220. grid, pos, facing = rotate(rot, grid, pos, facing)
  221. num = ""
  222. else:
  223. num += char
  224. final_facing = facing
  225. while facing != 0:
  226. grid, pos, facing = rotate("R", grid, pos, facing)
  227. pos = (side[0] * grid_sz + pos[0] + 1, side[1] * grid_sz + pos[1] + 1)
  228. print(pos[1] * 1000 + pos[0] * 4 + final_facing)
  229. part2(board, lines[-1])