summaryrefslogtreecommitdiff
path: root/2024/day15/solve2.py
blob: 86e5c824104d9b10e662a579625ab3ec99fdf941 (plain)
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
def get_affected_boxes_if_can_move(data: list[list[str]], current_pos: tuple[int, int],
                                   affected_box_positions: list[tuple[int, int]], direction: tuple[int, int]) -> bool:
    current_char = data[current_pos[0]][current_pos[1]]
    if current_char == "]":
        current_pos = (current_pos[0], current_pos[1] - 1)

    if current_char in affected_box_positions:
        return True

    if current_char != "@":
        affected_box_positions.append(current_pos)

    pushed_positions = []
    if current_char == "@":
        pushed_positions.append(direction)
    elif direction == (0, 1):
        pushed_positions.append((0, 2))
    elif direction == (0, -1):
        pushed_positions.append(direction)
    else:
        pushed_positions.append(direction)
        pushed_positions.append((direction[0], 1))

    for pushed in pushed_positions:
        pushed_pos = (current_pos[0] + pushed[0], current_pos[1] + pushed[1])
        pushed_char = data[pushed_pos[0]][pushed_pos[1]]
        if pushed_char == "#":
            return False
        elif pushed_char == "[" or pushed_char == "]":
            success = get_affected_boxes_if_can_move(data, pushed_pos, affected_box_positions, direction)
            if not success:
                return False

    return True


def move(data: list[list[str]], current_pos: tuple[int, int],
         current_order: str, movement_dict: dict[str, tuple[int, int]]) -> tuple[int, int]:
    assert data[current_pos[0]][current_pos[1]] == "@"

    direction = movement_dict[current_order]
    can_move = True
    steps = 0
    affected_box_positions = []
    success = get_affected_boxes_if_can_move(data, current_pos, affected_box_positions, direction)
    if not success:
        return current_pos

    data[current_pos[0]][current_pos[1]] = "."
    for box_pos in affected_box_positions:
        data[box_pos[0]][box_pos[1]] = "."
        data[box_pos[0]][box_pos[1] + 1] = "."

    current_pos = (current_pos[0] + direction[0], current_pos[1] + direction[1])
    data[current_pos[0]][current_pos[1]] = "@"

    for box_pos in affected_box_positions:
        data[box_pos[0] + direction[0]][box_pos[1] + direction[1]] = "["
        data[box_pos[0] + direction[0]][box_pos[1] + direction[1] + 1] = "]"

    return current_pos

def calculate_gps_data(data: list[list[str]]):
    result = 0
    max_cols = len(data[0])
    for row_pos, row in enumerate(data):
        for col_pos, char in enumerate(row):
            if char == "[":
                result += row_pos * 100 + col_pos
    return result

movement_dirs = { "<": (0, -1), "v": (1, 0), "^": (-1, 0), ">": (0, 1) }
map_data = []
movement_data = ""

with open("input") as f:
    reading_movement = False
    for line in f:
        line = line.strip()
        if reading_movement:
            movement_data += line
        else:
            if line == "":
                reading_movement = True
            else:
                new_line = []
                map_data.append(new_line)
                for char in line:
                    if char == ".":
                        new_line.append(".")
                        new_line.append(".")
                    elif char == "#":
                        new_line.append("#")
                        new_line.append("#")
                    elif char == "O":
                        new_line.append("[")
                        new_line.append("]")
                    else:
                        new_line.append(char)
                        new_line.append(".")

for row_idx, row in enumerate(map_data):
    for col_idx, char in enumerate(row):
        if char == "@":
            robot_row = row_idx
            robot_col = col_idx
            break
    else:
        continue
    break

new_pos = (robot_row, robot_col)
for order in movement_data:
    new_pos = move(map_data,new_pos, order, movement_dirs)

for line in map_data:
    print("".join(line))

print(calculate_gps_data(map_data))