Spaces:
Running
Running
File size: 4,158 Bytes
a4da721 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
def parse_input(filename):
with open(filename, 'r') as f:
lines = f.read().strip().split('\n')
# Find empty line that separates map and moves
split_idx = lines.index('')
map_lines = lines[:split_idx]
moves = ''.join(lines[split_idx+1:]).strip()
return map_lines, moves
def find_robot_and_boxes(grid):
robot = None
boxes = set()
for y in range(len(grid)):
for x in range(len(grid[y])):
if grid[y][x] == '@':
robot = (x, y)
elif grid[y][x] == 'O':
boxes.add((x, y))
return robot, boxes
def find_robot_and_boxes_wide(grid):
robot = None
boxes = set()
for y in range(len(grid)):
for x in range(len(grid[y])-1):
if grid[y][x:x+2] == '[]':
boxes.add((x, y))
elif grid[y][x] == '@':
robot = (x, y)
return robot, boxes
def move_direction(direction):
if direction == '^': return (0, -1)
if direction == 'v': return (0, 1)
if direction == '<': return (-1, 0)
if direction == '>': return (1, 0)
return (0, 0)
def is_wall(grid, x, y):
return grid[y][x] == '#'
def simulate_move(grid, robot, boxes, dx, dy):
new_x, new_y = robot[0] + dx, robot[1] + dy
# Check if moving into wall
if is_wall(grid, new_x, new_y):
return robot, boxes
# Check if moving into box
if (new_x, new_y) in boxes:
box_new_x, box_new_y = new_x + dx, new_y + dy
# Check if box would move into wall or another box
if is_wall(grid, box_new_x, box_new_y) or (box_new_x, box_new_y) in boxes:
return robot, boxes
# Move box
boxes.remove((new_x, new_y))
boxes.add((box_new_x, box_new_y))
return (new_x, new_y), boxes
def simulate_move_wide(grid, robot, boxes, dx, dy):
new_x, new_y = robot[0] + dx, robot[1] + dy
# Check if moving into wall
if is_wall(grid, new_x, new_y):
return robot, boxes
# Check if moving into box
box_pos = None
for box in boxes:
if new_x in (box[0], box[0] + 1) and new_y == box[1]:
box_pos = box
break
if box_pos:
box_new_x, box_new_y = box_pos[0] + dx, box_pos[1] + dy
# Check if box would move into wall or another box
if (is_wall(grid, box_new_x, box_new_y) or
is_wall(grid, box_new_x + 1, box_new_y) or
any(b[0] in (box_new_x, box_new_x + 1) and b[1] == box_new_y for b in boxes if b != box_pos)):
return robot, boxes
# Move box
boxes.remove(box_pos)
boxes.add((box_new_x, box_new_y))
return (new_x, new_y), boxes
def calculate_gps_sum(boxes, height):
return sum((box[1] + 1) * 100 + (box[0] + 1) for box in boxes)
def calculate_gps_sum_wide(boxes, height):
return sum((box[1] + 1) * 100 + (box[0] + 1) for box in boxes)
def scale_map(map_lines):
scaled = []
for line in map_lines:
new_line = ''
for c in line:
if c == '#':
new_line += '##'
elif c == 'O':
new_line += '[]'
elif c == '.':
new_line += '..'
elif c == '@':
new_line += '@.'
scaled.append(new_line)
return scaled
def solve_part1(map_lines, moves):
grid = map_lines
robot, boxes = find_robot_and_boxes(grid)
for move in moves:
dx, dy = move_direction(move)
robot, boxes = simulate_move(grid, robot, boxes, dx, dy)
return str(calculate_gps_sum(boxes, len(grid)))
def solve_part2(map_lines, moves):
grid = scale_map(map_lines)
robot, boxes = find_robot_and_boxes_wide(grid)
for move in moves:
dx, dy = move_direction(move)
robot, boxes = simulate_move_wide(grid, robot, boxes, dx, dy)
return str(calculate_gps_sum_wide(boxes, len(grid)))
def main():
map_lines, moves = parse_input('./input.txt')
# Part 1
print(solve_part1(map_lines, moves))
# Part 2
print(solve_part2(map_lines, moves))
if __name__ == "__main__":
main() |