aoc2022/day22.py

219 lines
8.2 KiB
Python

import re
from tools.aoc import AOCDay
from tools.coordinate import Coordinate
from tools.grid import Grid, GridTransformation
from typing import Any
FACING = [
Coordinate(1, 0, 0),
Coordinate(0, 1, 0),
Coordinate(-1, 0, 0),
Coordinate(0, -1, 0)
]
def get_password_from_coord(c: Coordinate, face: int) -> int:
return (c.y + 1) * 1000 + (c.x + 1) * 4 + face
def walk(board: Grid, pos: Coordinate, directions: list) -> (Coordinate, int):
face = 0
for direction in directions:
steps, turn = direction
for _ in range(steps):
next_pos = pos + FACING[face]
next_face = face
if board.get(next_pos) is None or not board.isWithinBoundaries(next_pos):
match face:
case 0:
next_pos = Coordinate(0, next_pos.y, 0)
case 1:
next_pos = Coordinate(next_pos.x, 0, 0)
case 2:
next_pos = Coordinate(board.maxX, next_pos.y, 0)
case 3:
next_pos = Coordinate(next_pos.x, board.maxY, 0)
while board.get(next_pos) is None:
next_pos += FACING[face]
next_val = board.get(next_pos)
if not next_val:
break
else:
pos = next_pos
face = next_face
if turn != 'S':
face = (face + (1 if turn == 'R' else -1)) % 4
return pos, face
def cube_walk(board: Grid, pos: Coordinate, directions: list) -> (Coordinate, int):
face = 0
for direction in directions:
steps, turn = direction
for _ in range(steps):
next_pos = pos + FACING[face]
if board.get(next_pos) is False:
break
elif board.get(next_pos) is None:
match face:
case 0: # right
if board.get(Coordinate(pos.x + 1, pos.y, 1)) is True:
board.transform(GridTransformation.COUNTER_ROTATE_Y)
next_pos = Coordinate(1, pos.y, 0)
else:
break
case 1: # down
if board.get(Coordinate(pos.x, pos.y + 1, 1)) is True:
board.transform(GridTransformation.ROTATE_X)
next_pos = Coordinate(pos.x, 1, 0)
else:
break
case 2: # left
if board.get(Coordinate(0, pos.y, 1)) is True:
board.transform(GridTransformation.ROTATE_Y)
next_pos = Coordinate(board.maxX - 1, pos.y, 0)
else:
break
case 3: # up
if board.get(Coordinate(pos.x, 0, 1)) is True:
board.transform(GridTransformation.COUNTER_ROTATE_X)
next_pos = Coordinate(pos.x, board.maxY - 1, 0)
else:
break
pos = next_pos
if turn != 'S':
face = (face + (1 if turn == 'R' else -1)) % 4
return pos, face
def fold(board: Grid, start_pos: Coordinate):
c_size = 4 if board.maxX < 40 else 50 # account for test case
if board.get(start_pos + Coordinate(-1, 0, 0)) is not None:
for y in board.rangeY():
for x in range(start_pos.x - 1, board.minX - 1, -1):
from_c = Coordinate(x, y, 0)
to_c = Coordinate(start_pos.x - 1, y, start_pos.x - x)
board.set(to_c, board.get(from_c))
board.set(from_c, None)
board.recalcBoundaries()
board.transform(GridTransformation.ROTATE_Y)
board.shift(shift_z=-board.minZ)
fold(board, Coordinate(board.maxX - c_size, 1, 0))
board.transform(GridTransformation.COUNTER_ROTATE_Y)
board.shift_zero()
start_pos = Coordinate(1, 1, 0)
if board.get(start_pos + Coordinate(c_size, 0, 0)) is not None:
for y in range(board.maxY + 1):
for x in range(start_pos.x + c_size, board.maxX + 1):
from_c = Coordinate(x, y, 0)
to_c = Coordinate(start_pos.x + c_size, y, x - start_pos.x - c_size + 1)
board.set(to_c, board.get(from_c))
board.set(from_c, None)
board.recalcBoundaries()
board.transform(GridTransformation.COUNTER_ROTATE_Y)
board.shift(shift_z=-board.minZ)
fold(board, Coordinate(1, 1, 0))
board.transform(GridTransformation.ROTATE_Y)
board.shift_zero()
if board.get(Coordinate(start_pos.x, 0, 0)) is not None:
board.shift(shift_y=1)
if list(board.getActiveCells(x=0, z=0)):
board.shift(shift_x=1)
if board.get(start_pos + Coordinate(0, c_size, 0)) is not None:
for y in range(start_pos.y + c_size, board.maxY + 1):
for x in board.rangeX():
from_c = Coordinate(x, y, 0)
to_c = Coordinate(x, start_pos.y + c_size, y - start_pos.y - c_size + 1)
board.set(to_c, board.get(from_c))
board.set(from_c, None)
board.recalcBoundaries()
board.transform(GridTransformation.ROTATE_X)
fold(board, start_pos)
board.transform(GridTransformation.COUNTER_ROTATE_X)
class Day(AOCDay):
inputs = [
[
(6032, "input22_test"),
(191010, "input22_dennis"),
(1428, "input22"),
],
[
(5031, "input22_test"),
(55364, "input22_dennis"),
(142380, "input22"),
]
]
def get_map_and_directions(self) -> (Grid, list, Coordinate):
start_pos = None
map_lines, dir_line = self.getMultiLineInputAsArray()
board = Grid.from_str("/".join(map_lines), default=None, translate={' ': None, '#': False, '.': True}, mode3d=True)
for x in board.rangeX():
if board.get(Coordinate(x, 0, 0)) is not None:
start_pos = Coordinate(x, 0, 0)
break
if dir_line[0][-1] not in ['R', 'L']:
dir_line[0] += 'S'
directions = []
for d in re.findall(r'\d+[RLS]', dir_line[0]):
directions.append((int(d[:-1]), d[-1]))
return board, directions, start_pos
def part1(self) -> Any:
board, directions, start_position = self.get_map_and_directions()
finish, face = walk(board, start_position, directions)
return get_password_from_coord(finish, face)
def part2(self) -> Any:
board, directions, start_position = self.get_map_and_directions()
board.shift(shift_x=1, shift_y=1)
start_position += Coordinate(1, 1, 0)
fold(board, start_position)
finish, face = cube_walk(board, Coordinate(1, 1, 0), directions)
orig_board, _, _ = self.get_map_and_directions()
c_size = 4 if orig_board.maxX < 50 else 50 # account for test case
for x in range(0, orig_board.maxX + 1, c_size):
for y in range(0, orig_board.maxY + 1, c_size):
check_pos = finish - Coordinate(1, 1, 0)
check_face = face
sub_board = orig_board.sub_grid(x, y, x + c_size - 1, y + c_size - 1, 0, 0)
if len(sub_board.getActiveCells()) == 0:
continue
check_board = board.sub_grid(1, 1, c_size, c_size, 0, 0)
for _ in range(2):
for _ in range(4):
check_board.transform(GridTransformation.ROTATE_Z)
check_pos = Coordinate(-check_pos.y + sub_board.maxY, check_pos.x, 0)
check_face = (check_face + 1) % 4
if sub_board == check_board:
check_pos += Coordinate(x, y, 0)
return get_password_from_coord(check_pos, check_face)
check_board.transform(GridTransformation.FLIP_X)
check_pos = Coordinate(abs(check_pos.x - c_size + 1), check_pos.y, 0)
check_face = (check_face + 2) % 4
return ""
if __name__ == '__main__':
day = Day(2022, 22)
day.run(verbose=True)