343 lines
8.2 KiB
Python
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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|