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))