123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- from queue import deque
- from itertools import zip_longest
- def transpose(l):
- return list(zip_longest(*l, fillvalue=None))
- def rot_left(grid):
- return list(reversed(transpose(grid)))
- def rot_right(grid):
- return list(transpose(reversed(grid)))
- lines = [line[:-1] for line in open("22.input")]
- board = [line for line in lines[:-2]]
- max_width = max(len(line) for line in board)
- board = [line + " " * max(0, max_width - len(line)) for line in board]
- def show(grid):
- for line in grid:
- for c in line:
- print(c, end='')
- print("")
- def walk(steps, board, pos):
- for _ in range(steps):
- x, y = pos
- new_x = pos[0] + 1
- if new_x >= len(board[y]) or board[y][new_x] == " ":
- new_x = [x for x, c in enumerate(board[y]) if c != " "][0]
- if board[y][new_x] == '#':
- break
- else:
- pos = (new_x, y)
- return (board, pos)
- def rotate(rot, board, pos, facing):
- if rot == "R":
- board = rot_left(board)
- x, y = pos
- pos = (y, len(board)-x-1)
- facing = (facing + 1) % 4
- elif rot == "L":
- x, y = pos
- new_x = len(board) - y - 1
- board = rot_right(board)
- pos = (new_x, x)
- facing = (facing - 1) % 4
- return (board, pos, facing)
- def part1(board, pw):
- pw = deque(pw)
- pos = ([x for x, c in enumerate(board[0]) if c == '.'][0], 0)
- facing = 0
- num = ""
- while pw:
- char = pw.popleft()
- if char == 'R' or char == 'L':
- steps = int(num)
- board, pos = walk(steps, board, pos)
- board, pos, facing = rotate(char, board, pos, facing)
- num = ""
- else:
- num += char
- board, pos = walk(int(num), board, pos)
- final_facing = facing
- while facing != 0:
- board, pos, facing = rotate("R", board, pos, facing)
- pos = (pos[0] + 1, pos[1] + 1)
- print(pos[1] * 1000 + pos[0] * 4 + final_facing)
- part1(board, lines[-1])
- grid_sz = 50
- def empty_side():
- return [" " * grid_sz] * grid_sz
- def extract_side(board, pos):
- return [line[pos[0] * grid_sz : (pos[0] + 1) * grid_sz] for line in board[pos[1] * grid_sz : (pos[1] + 1) * grid_sz]]
- dir_to_sym = [">", "v", "<", "^"]
- def build_cube(board):
- neighbours = {}
- sides = set()
- side = ([x for x, c in enumerate(board[0]) if c == '.'][0] // grid_sz, 0)
- q = deque()
- q.append(side)
- while q:
- side = q.popleft()
- x, y = side
- if side not in neighbours:
- neighbours[side] = [None, None, None, None]
- for i, (dx, dy) in enumerate([(1, 0), (0, 1), (-1, 0), (0, -1)]):
- new_x, new_y = x + dx, y + dy
- new = (new_x, new_y)
- if new_x < 0:
- continue
- if new_y < 0:
- continue
- try:
- if board[new_y * grid_sz][new_x * grid_sz] != " ":
- if new not in neighbours:
- neighbours[new] = [None, None, None, None]
- neighbours[side][i] = (new, (i + 2) % 4)
- neighbours[new][(i+2) % 4] = (side, i % 4)
- if (new_x, new_y) not in sides:
- sides.add((new_x, new_y))
- q.append((new_x, new_y))
- except IndexError:
- continue
- for _ in range(4):
- for pos, nbrs in neighbours.items():
- # Fold corners
- for i in range(4):
- left = nbrs[i]
- right = nbrs[(i+1) % 4]
- if left is not None and right is not None:
- if neighbours[left[0]][(i+1)%4] is not None:
- continue
- if left[0] in [a[0] for a in neighbours[right[0]] if a is not None]:
- continue
- rel_left = (i - left[1]) % 4
- rel_right = ((i + 1) - right[1]) % 4
- rel = (rel_left - rel_right) % 4
- print("Looking at", pos)
- print("Relative direction:", rel_left, rel_right, rel)
- if rel == 0:
- print(left[0], "has", right[0], "to the", dir_to_sym[(i + 1)%4])
- print(right[0], "has", left[0], "to the", dir_to_sym[(i + 0)%4])
- print("-" * 20)
- neighbours[left[0]][(i+1)%4] = (right[0], (i+0)%4)
- neighbours[right[0]][(i+0)%4] = (left[0], (i+1)%4)
- elif rel == 1:
- print(left[0], "has", right[0], "to the", dir_to_sym[(i + 1)%4])
- print(right[0], "has", left[0], "to the", dir_to_sym[(i + 1)%4])
- print("-" * 20)
- neighbours[left[0]][(i+1)%4] = (right[0], (i+1)%4)
- neighbours[right[0]][(i+1)%4] = (left[0], (i+1)%4)
- else:
- raise ValueError("Weird grid?")
- for pair in neighbours.items():
- print(pair)
- # Finish the fold :DDD
- for i in range(2):
- for start in [(pos, nbrs) for (pos, nbrs) in neighbours.items() if None in nbrs]:
- print("Start:", start)
- pos, nbrs = start
- for direction in [(2 + i) % 4 for i, n in enumerate(nbrs) if n is None]:
- start_dir = direction
- print("Direction:", direction)
- for i in range(3):
- if neighbours[pos][direction] is None:
- break
- pos, new_dir = neighbours[pos][direction]
- direction = (2 + new_dir) % 4
- print("pos:", pos, "Direction:", direction)
- else:
- neighbours[start[0]][(2 + start_dir) % 4] = (pos, direction)
- neighbours[pos][direction] = (start[0], (2 + start_dir) % 4)
- for pair in neighbours.items():
- print(pair)
- return neighbours
- def part2(board, pw):
- #neighbours = build_cube(board)
- #print(neighbours)
- neighbours = {
- (1, 0): [((2, 0), 2), ((1, 1), 3), ((0, 2), 2), ((0, 3), 2)],
- (2, 0): [((1, 2), 0), ((1, 1), 0), ((1, 0), 0), ((0, 3), 1)],
- (1, 1): [((2, 0), 1), ((1, 2), 3), ((0, 2), 3), ((1, 0), 1)],
- (1, 2): [((2, 0), 0), ((0, 3), 0), ((0, 2), 0), ((1, 1), 1)],
- (0, 2): [((1, 2), 2), ((0, 3), 3), ((1, 0), 2), ((1, 1), 2)],
- (0, 3): [((1, 2), 1), ((2, 0), 3), ((1, 0), 3), ((0, 2), 1)]}
- if False:
- edges = set()
- for pos, nbrs in neighbours.items():
- for i, (n, d) in enumerate(nbrs):
- if (n, d) in edges:
- print("Duplicate:", (n, d))
- break
- edges.add((n, d))
- print(pos, "has", n, "to the", dir_to_sym[i])
- for side in neighbours.keys():
- for direction in range(4):
- current = side
- #print("Start:", current, "Direction:", direction)
- for i in range(4):
- side, new_dir = neighbours[side][direction]
- direction = (2 + new_dir) % 4
- #print("side:", side, "Direction:", direction)
- print(side, current)
- assert current == side
- side = ([x for x, c in enumerate(board[0]) if c == '.'][0] // grid_sz, 0)
- grid = extract_side(board, side)
- pos = ([x for x, c in enumerate(grid[0]) if c == '.'][0], 0)
- facing = 0
- num = ""
- path = set()
- pw = deque(pw)
- pw.append('S')
- while pw:
- char = pw.popleft()
- if char == 'R' or char == 'L' or char == 'S':
- rot = char
- steps = int(num)
- #print("grid:", side, "pos:", pos, "face:", dir_to_sym[facing])
- #show(grid)
- for _ in range(steps):
- x, y = pos
- new_x = pos[0] + 1
- new_pos = (new_x, pos[1])
- new_grid = grid
- new_side = side
- new_facing = facing
- if new_x >= grid_sz:
- # Find next side
- new_side, entry_dir = neighbours[side][facing]
- rel_dir = entry_dir - facing
- new_facing = (entry_dir + 2) % 4
- new_grid = extract_side(board, new_side)
- for _ in range(new_facing):
- new_grid = rot_left(new_grid)
- new_x = 0
- new_pos = (0, y)
- if new_grid[y][new_x] == '#':
- break
- else:
- side = new_side
- pos = new_pos
- grid = new_grid
- facing = new_facing
- #print("grid:", side, "pos:", pos, "face:", dir_to_sym[facing])
- #show(grid)
- grid, pos, facing = rotate(rot, grid, pos, facing)
- num = ""
- else:
- num += char
- final_facing = facing
- while facing != 0:
- grid, pos, facing = rotate("R", grid, pos, facing)
- pos = (side[0] * grid_sz + pos[0] + 1, side[1] * grid_sz + pos[1] + 1)
- print(pos[1] * 1000 + pos[0] * 4 + final_facing)
- part2(board, lines[-1])
|