2025-12-01 14:23:49 -06:00

343 lines
8.2 KiB
Python

ROBOT_SPEED: int = 1
class Coord:
x: int
y: int
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __str__(self) -> str:
return f'({self.x}, {self.y})'
def __eq__(self, other) -> bool:
return other.x == self.x and other.y == self.y
def __add__(self, other):
return Coord(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Coord(self.x - other.x, self.y - other.y)
def __mul__(self, other):
return Coord(self.x * other, self.y * other)
def __rmul__(self, other):
return Coord(self.x * other, self.y * other)
def copy(self):
return Coord(self.x, self.y)
class Warehouse:
robot: Coord
walls: list[Coord] = []
boxes: list[Coord] = []
width: int
height: int
def __init__(self, warehouse_map: list[str]):
for y, line in enumerate(warehouse_map):
for x, c in enumerate(line):
match c:
case "#":
self.walls.append(Coord(x, y))
case "@":
self.robot = Coord(x, y)
case "O":
self.boxes.append(Coord(x, y))
self.width = len(warehouse_map[0])
self.height = len(warehouse_map)
def __str__(self) -> str:
w = [['.'] * self.width for _ in range(self.height)]
for box in self.boxes:
w[box.y][box.x] = 'O'
for wall in self.walls:
w[wall.y][wall.x] = '#'
w[self.robot.y][self.robot.x] = '@'
s = ''
for row in w:
for c in row:
s += c
s += '\n'
return s
def move_robot(self, command: str) -> None:
dx, dy = 0, 0
match command:
case '>':
dx += 1
case '<':
dx -= 1
case 'v':
dy += 1
case '^':
dy -= 1
movement = Coord(dx, dy)
new_pos = self.robot + movement
if new_pos in self.walls:
return None
if new_pos in self.boxes:
if self.push_box(new_pos, movement):
self.robot = new_pos
return None
self.robot = new_pos
def push_box(self, box: Coord, movement: Coord) -> bool:
new_pos: Coord = box + movement
if new_pos in self.walls:
return False
if new_pos in self.boxes:
if not self.push_box(new_pos, movement):
return False
self.boxes.remove(box)
self.boxes.append(new_pos)
return True
def score(self) -> int:
total = 0
for box in self.boxes:
total += 100 * box.y + box.x
return total
class BigBox:
right: Coord
left: Coord
def __init__(self, left: Coord):
self.left = left
self.right = Coord(left.x + 1, left.y)
def __add__(self, other: Coord):
return BigBox(self.left + other)
def __eq__(self, other) -> bool:
return self.left == other.left and self.right == other.right
def __str__(self) -> str:
return '[]'
class SecondWarehouse:
robot: Coord
walls: list[Coord] = []
boxes: list[BigBox] = []
width: int
height: int
def __init__(self, warehouse_map: list[str]):
for y, line in enumerate(warehouse_map):
offset = 0
for x, c in enumerate(line):
match c:
case "#":
self.walls.append(Coord(x + offset, y))
self.walls.append(Coord(x + 1 + offset, y))
case "@":
self.robot = Coord(x + offset, y)
case "O":
self.boxes.append(BigBox(Coord(x + offset, y)))
offset += 1
self.width = len(warehouse_map[0]) * 2
self.height = len(warehouse_map)
def __str__(self) -> str:
w = [['.'] * self.width for _ in range(self.height)]
for box in self.boxes:
w[box.left.y][box.left.x] = '['
w[box.right.y][box.right.x] = ']'
for wall in self.walls:
w[wall.y][wall.x] = '#'
w[self.robot.y][self.robot.x] = '@'
s = ''
for row in w:
for c in row:
s += c
s += '\n'
return s
def move_robot(self, command: str) -> None:
dx, dy = 0, 0
match command:
case '>':
dx += 1
case '<':
dx -= 1
case 'v':
dy += 1
case '^':
dy -= 1
movement = Coord(dx, dy)
new_pos = self.robot + movement
if new_pos in self.walls:
return
if box := self.is_box(new_pos):
if self.push_box(box, movement):
self.robot = new_pos
return
self.robot = new_pos
def is_box(self, pos: Coord) -> BigBox | None:
for box in self.boxes:
if pos == box.left or pos == box.right:
return box
return None
def push_box(self, box: BigBox, movement: Coord) -> bool:
new_box: BigBox = box + movement
# def push_box(self, box: Coord, movement: Coord) -> bool:
# if movement.y != 0:
# return self.vertical_push(box, movement)
# new_pos: Coord = box + movement
# new_pos2 = new_pos.copy()
# if movement.x > 0:
# new_pos2.x += 1
# else:
# new_pos.x -= 1
# if new_pos in self.walls:
# return False
# if new_pos2 in self.boxes:
# if not self.push_box(new_pos2, movement):
# return False
# self.boxes.remove(box)
# self.boxes.append(new_pos)
# return True
def vertical_push(self, box: Coord, movement: Coord) -> bool:
pass
new_pos: Coord = box + movement
# other_new_pos: Coord = new_pos
# if box in self.left_boxes:
# other_new_pos.x += 1
# else:
# other_new_pos.x -= 1
# if new_pos in self.walls or other_new_pos in self.walls:
# return False
# if new_pos in self.left_boxes or new_pos in self.right_boxes:
# if not self.vertical_push(new_pos, movement):
# return False
# if other_new_pos in self.left_boxes or other_new_pos in self.right_boxes:
# if not self.vertical_push(other_new_pos, movement):
# return False
# if box in self.left_boxes:
# self.left_boxes.remove(box)
# self.left_boxes.append(new_pos)
# self.right_boxes.remove(Coord(box.x + 1, box.y))
# self.right_boxes.append(other_new_pos)
# else:
# self.right_boxes.remove(box)
# self.right_boxes.append(new_pos)
# self.left_boxes.remove(Coord(box.x - 1, box.y))
# self.left_boxes.append(other_new_pos)
# return True
# def can_push(self, box: Coord, movement: Coord) -> bool:
# new_pos: Coord = box + movement
# if new_pos in self.right_boxes or new_pos in self.left_boxes:
# return self.can_push(new_pos, movement)
# return new_pos in self.walls
# def score(self) -> int:
# total = 0
# for box in self.left_boxes:
# total += 100 * box.y + box.x
# return total
def main() -> None:
with open('test.txt', 'r') as file:
data: str = file.read()
warehouse_map, movements = [s.split("\n") for s in data.split("\n\n")]
movements = ''.join(movements)
# Part 1
print('<', '=' * 25, ' PART 1 ', '=' * 25, '>')
warehouse = Warehouse(warehouse_map)
print(warehouse)
for command in movements:
warehouse.move_robot(command)
print(warehouse)
print('Score: ', warehouse.score())
# Part 2
print('<', '=' * 25, ' PART 2 ', '=' * 25, '>')
warehouse = SecondWarehouse(warehouse_map)
print(warehouse)
# for command in movements:
# warehouse.move_robot(command)
# print('Movement: ', command)
# print(warehouse)
# input()
if __name__ == '__main__':
main()