diff --git a/2022/day22.py b/2022/day22.py index d42b586..6a409ea 100644 --- a/2022/day22.py +++ b/2022/day22.py @@ -2,14 +2,15 @@ import re import sys +from typing import Callable import numpy as np -VOID = 0 -EMPTY = 1 -WALL = 2 +VOID, EMPTY, WALL = 0, 1, 2 TILE_FROM_CHAR = {" ": VOID, ".": EMPTY, "#": WALL} +SCORES = {"E": 0, "S": 1, "W": 2, "N": 3} + board_map_s, direction_s = sys.stdin.read().split("\n\n") @@ -27,23 +28,6 @@ directions = [ int(p1) if p2 else p1 for p1, p2 in re.findall(R"(([0-9])+|L|R)", direction_s) ] -invert = False - -y0 = 0 -x0 = np.where(board[0] == EMPTY)[0][0] -r0 = "E" - -if invert: - board = board[::-1, ::-1] - y0 = board.shape[0] - 1 - x0 = np.where(board[-1, :] == EMPTY)[0][-1] - r0 = "W" - # print( - # "\n".join( - # "".join(next(k for k, v in TILE_FROM_CHAR.items() if v == c) for c in row) - # for row in board - # ) - # ) # find on each row and column the first and last non-void row_first_non_void = np.argmax(board != VOID, axis=1) @@ -52,6 +36,75 @@ col_first_non_void = np.argmax(board != VOID, axis=0) col_last_non_void = board.shape[0] - np.argmax(board[::-1, :] != VOID, axis=0) - 1 +faces = np.zeros_like(board) +size = np.gcd(board.shape[0], board.shape[1]) +for row in range(0, board.shape[0], size): + for col in range(row_first_non_void[row], row_last_non_void[row], size): + faces[row : row + size, col : col + size] = faces.max() + 1 + +SIZE = np.gcd(*board.shape) + +# TODO: deduce this from the actual cube... +faces_wrap: dict[int, dict[str, Callable[[int, int], tuple[int, int, str]]]] +if board.shape == (12, 16): # example + faces_wrap = { + 1: { + "W": lambda y, x: (4, 4 + y, "S"), # 3N + "N": lambda y, x: (4, 11 - x, "S"), # 2N + "E": lambda y, x: (11 - y, 15, "W"), # 6E + }, + 2: { + "W": lambda y, x: (11, 19 - y, "N"), # 6S + "N": lambda y, x: (0, 11 - y, "S"), # 1N + "S": lambda y, x: (11, 11 - x, "N"), # 5S + }, + 3: { + "N": lambda y, x: (x - 4, 8, "E"), # 1W + "S": lambda y, x: (15 - x, 8, "E"), # 5W + }, + 4: {"E": lambda y, x: (8, 19 - y, "S")}, # 6N + 5: { + "W": lambda y, x: (7, 15 - y, "N"), # 3S + "S": lambda y, x: (7, 11 - x, "N"), # 2S + }, + 6: { + "N": lambda y, x: (19 - x, 11, "W"), # 4E + "E": lambda y, x: (11 - y, 11, "W"), # 1E + "S": lambda y, x: (19 - x, 0, "E"), # 2W + }, + } + +else: + faces_wrap = { + 1: { + "W": lambda y, x: (3 * SIZE - y - 1, 0, "E"), # 4W + "N": lambda y, x: (2 * SIZE + x, 0, "E"), # 6W + }, + 2: { + "N": lambda y, x: (4 * SIZE - 1, x - 2 * SIZE, "N"), # 6S + "E": lambda y, x: (3 * SIZE - y - 1, 2 * SIZE - 1, "W"), # 5E + "S": lambda y, x: (x - SIZE, 2 * SIZE - 1, "W"), # 3E + }, + 3: { + "W": lambda y, x: (2 * SIZE, y - SIZE, "S"), # 4N + "E": lambda y, x: (SIZE - 1, SIZE + y, "N"), # 2S + }, + 4: { + "W": lambda y, x: (3 * SIZE - y - 1, SIZE, "E"), # 1W + "N": lambda y, x: (SIZE + x, SIZE, "E"), # 3W + }, + 5: { + "E": lambda y, x: (3 * SIZE - y - 1, 3 * SIZE - 1, "W"), # 2E + "S": lambda y, x: (2 * SIZE + x, SIZE - 1, "W"), # 6E + }, + 6: { + "W": lambda y, x: (0, y - 2 * SIZE, "S"), # 1N + "E": lambda y, x: (3 * SIZE - 1, y - 2 * SIZE, "N"), # 5S + "S": lambda y, x: (0, x + 2 * SIZE, "S"), # 2N + }, + } + + def wrap_part_1(y0: int, x0: int, r0: str) -> tuple[int, int, str]: if r0 == "E": return y0, row_first_non_void[y0], r0 @@ -66,184 +119,107 @@ def wrap_part_1(y0: int, x0: int, r0: str) -> tuple[int, int, str]: def wrap_part_2(y0: int, x0: int, r0: str) -> tuple[int, int, str]: - if r0 == "E": - if y0 in range(0, 4): - y0 = board.shape[0] - y0 - 1 - return y0, row_last_non_void[y0], "W" - elif y0 in range(4, 8): - print(x0, row_last_non_void[8], row_last_non_void[8] - (y0 - 4)) - x0 = row_last_non_void[8] - (y0 - 4) - return col_first_non_void[x0], x0, "S" - else: - y0 = board.shape[0] - y0 - 1 - return y0, row_last_non_void[y0], "W" - elif r0 == "S": - if x0 in range(0, 4): - y0 = board.shape[0] - 1 - return y0, row_first_non_void[y0] + 3 - x0, "N" - elif x0 in range(4, 8): - y0 = col_last_non_void[x0] + 8 - x0 - return y0, row_first_non_void[y0], "E" - elif x0 in range(8, 12): - # 8 -> 3, 9 -> 2 - x0 = board.shape[0] - x0 - 1 - return col_last_non_void[x0], x0, "N" - else: - y0 = col_first_non_void[0] + board.shape[1] - x0 - 1 - return y0, row_first_non_void[y0], "W" - elif r0 == "W": - if y0 in range(0, 4): - x0 = 4 + y0 - return col_first_non_void[x0], x0, "S" - elif y0 in range(4, 8): - x0 = board.shape[1] - (y0 - 4) - 1 - return board.shape[0] - 1, x0, "N" - else: - x0 = 4 + board.shape[0] - y0 - 1 - return col_last_non_void[x0], x0, "N" - elif r0 == "N": - if x0 in range(0, 4): - y0 = 0 - return y0, row_first_non_void[y0] + 3 - x0, "S" - elif x0 in range(4, 8): - y0 = x0 - 4 - return y0, row_first_non_void[y0], "E" - elif x0 in range(8, 12): - x0 = 11 - x0 - return col_first_non_void[x0], x0, "S" - else: - y0 = 4 + board.shape[0] - x0 - 1 - return y0, row_last_non_void[y0], "W" - - assert False + cube = faces[y0, x0] + assert r0 in faces_wrap[cube] + return faces_wrap[cube][r0](y0, x0) -# for i in range(4): -# print(wrap_part_2(i, 8 + i - 1, "E")) +def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int, str]: -# exit() + y0 = 0 + x0 = np.where(board[0] == EMPTY)[0][0] + r0 = "E" - -wrap = wrap_part_2 - -# directions = directions[:5] - -print(y0, x0, r0) - -facing = np.zeros_like(board, dtype=str) -facing.fill(" ") -facing[board != VOID] = "." -facing[board == WALL] = "#" - -for direction in directions: - # print(f"{y0} {x0} {r0} ({direction})") - # r1 = {"N": "S", "S": "N", "W": "E", "E": "W"}[r0] - # print(f"{board.shape[0] - y0 - 1} {board.shape[1] - x0 - 1} {r1} ({direction})") - facing[y0, x0] = {"E": ">", "W": "<", "N": "^", "S": "v"}[r0] - - if isinstance(direction, int): - while direction > 0: - if r0 == "E": - xi = np.where(board[y0, x0 + 1 : x0 + direction + 1] == WALL)[0] - # if y0 == 12 and x0 == 81: - # print( - # xi, - # board[y0, x0 + 1 : x0 + direction], - # board[y0, x0 + direction], - # ) - if len(xi): - # print("E1") - x0 = x0 + xi[0] - direction = 0 - elif ( - x0 + direction < board.shape[1] - and board[y0, x0 + direction] == EMPTY - ): - # print("E2") - x0 = x0 + direction - direction = 0 - else: - y0_t, x0_t, r0_t = wrap(y0, x0, r0) - if board[y0_t, x0_t] == WALL: - x0 = row_last_non_void[y0] + for direction in directions: + if isinstance(direction, int): + while direction > 0: + if r0 == "E": + xi = np.where(board[y0, x0 + 1 : x0 + direction + 1] == WALL)[0] + if len(xi): + x0 = x0 + xi[0] + direction = 0 + elif ( + x0 + direction < board.shape[1] + and board[y0, x0 + direction] == EMPTY + ): + x0 = x0 + direction direction = 0 else: - direction = direction - (row_last_non_void[y0] - x0) - 1 - y0, x0, r0 = y0_t, x0_t, r0_t - elif r0 == "S": - yi = np.where(board[y0 + 1 : y0 + direction + 1, x0] == WALL)[0] - if len(yi): - y0 = y0 + yi[0] - direction = 0 - elif ( - y0 + direction < board.shape[0] - and board[y0 + direction, x0] == EMPTY - ): - y0 = y0 + direction - direction = 0 - else: - y0_t, x0_t, r0_t = wrap(y0, x0, r0) - if board[y0_t, x0_t] == WALL: - y0 = col_last_non_void[x0] + y0_t, x0_t, r0_t = wrap(y0, x0, r0) + if board[y0_t, x0_t] == WALL: + x0 = row_last_non_void[y0] + direction = 0 + else: + direction = direction - (row_last_non_void[y0] - x0) - 1 + y0, x0, r0 = y0_t, x0_t, r0_t + elif r0 == "S": + yi = np.where(board[y0 + 1 : y0 + direction + 1, x0] == WALL)[0] + if len(yi): + y0 = y0 + yi[0] + direction = 0 + elif ( + y0 + direction < board.shape[0] + and board[y0 + direction, x0] == EMPTY + ): + y0 = y0 + direction direction = 0 else: - direction = direction - (col_last_non_void[x0] - y0) - 1 - y0, x0, r0 = y0_t, x0_t, r0_t - elif r0 == "W": - left = max(x0 - direction - 1, 0) - xi = np.where(board[y0, left:x0] == WALL)[0] - if len(xi): - x0 = left + xi[-1] + 1 - direction = 0 - elif x0 - direction >= 0 and board[y0, x0 - direction] == EMPTY: - x0 = x0 - direction - direction = 0 - else: - y0_t, x0_t, r0_t = wrap(y0, x0, r0) - if board[y0_t, x0_t] == WALL: - x0 = row_first_non_void[y0] + y0_t, x0_t, r0_t = wrap(y0, x0, r0) + if board[y0_t, x0_t] == WALL: + y0 = col_last_non_void[x0] + direction = 0 + else: + direction = direction - (col_last_non_void[x0] - y0) - 1 + y0, x0, r0 = y0_t, x0_t, r0_t + elif r0 == "W": + left = max(x0 - direction - 1, 0) + xi = np.where(board[y0, left:x0] == WALL)[0] + if len(xi): + x0 = left + xi[-1] + 1 + direction = 0 + elif x0 - direction >= 0 and board[y0, x0 - direction] == EMPTY: + x0 = x0 - direction direction = 0 else: - direction = direction - (x0 - row_first_non_void[y0]) - 1 - y0, x0, r0 = y0_t, x0_t, r0_t - elif r0 == "N": - top = max(y0 - direction - 1, 0) - yi = np.where(board[top:y0, x0] == WALL)[0] - if len(yi): - y0 = top + yi[-1] + 1 - direction = 0 - elif y0 - direction >= 0 and board[y0 - direction, x0] == EMPTY: - y0 = y0 - direction - direction = 0 - else: - y0_t, x0_t, r0_t = wrap(y0, x0, r0) - if board[y0_t, x0_t] == WALL: - y0 = col_first_non_void[x0] + y0_t, x0_t, r0_t = wrap(y0, x0, r0) + if board[y0_t, x0_t] == WALL: + x0 = row_first_non_void[y0] + direction = 0 + else: + direction = direction - (x0 - row_first_non_void[y0]) - 1 + y0, x0, r0 = y0_t, x0_t, r0_t + elif r0 == "N": + top = max(y0 - direction - 1, 0) + yi = np.where(board[top:y0, x0] == WALL)[0] + if len(yi): + y0 = top + yi[-1] + 1 + direction = 0 + elif y0 - direction >= 0 and board[y0 - direction, x0] == EMPTY: + y0 = y0 - direction direction = 0 else: - direction = direction - (y0 - col_first_non_void[x0]) - 1 - y0, x0, r0 = y0_t, x0_t, r0_t - else: - r0 = { - "E": {"L": "N", "R": "S"}, - "N": {"L": "W", "R": "E"}, - "W": {"L": "S", "R": "N"}, - "S": {"L": "E", "R": "W"}, - }[r0][direction] + y0_t, x0_t, r0_t = wrap(y0, x0, r0) + if board[y0_t, x0_t] == WALL: + y0 = col_first_non_void[x0] + direction = 0 + else: + direction = direction - (y0 - col_first_non_void[x0]) - 1 + y0, x0, r0 = y0_t, x0_t, r0_t + else: + r0 = { + "E": {"L": "N", "R": "S"}, + "N": {"L": "W", "R": "E"}, + "W": {"L": "S", "R": "N"}, + "S": {"L": "E", "R": "W"}, + }[r0][direction] -facing[y0, x0] = {"E": ">", "W": "<", "N": "^", "S": "v"}[r0] + return y0, x0, r0 -print("\n".join(map("".join, facing))) -if invert: - print(y0, x0, r0, "->", end=" ") - x0, y0, r0 = ( - board.shape[1] - x0 - 1, - board.shape[0] - y0 - 1, - {"N": "S", "S": "N", "W": "E", "E": "W"}[r0], - ) -print(y0, x0, r0) - -score = {"E": 0, "S": 1, "W": 2, "N": 3} -answer_1 = 1000 * (1 + y0) + 4 * (1 + x0) + score[r0] +y1, x1, r1 = run(wrap_part_1) +answer_1 = 1000 * (1 + y1) + 4 * (1 + x1) + SCORES[r1] print(f"answer 1 is {answer_1}") + +y2, x2, r2 = run(wrap_part_2) +answer_2 = 1000 * (1 + y2) + 4 * (1 + x2) + SCORES[r2] +print(f"answer 2 is {answer_2}")