|
| 1 | +#! /usr/bin/env python3 |
| 2 | +# |
| 3 | +# Advent of Code 2024 Day 15 |
| 4 | +# |
| 5 | + |
| 6 | +import sys |
| 7 | + |
| 8 | +from aoc import my_aocd |
| 9 | +from aoc.common import InputData |
| 10 | +from aoc.common import SolutionBase |
| 11 | +from aoc.common import aoc_samples, log |
| 12 | +from aoc.geometry import Direction |
| 13 | +from aoc.grid import CharGrid |
| 14 | +from aoc.grid import Cell |
| 15 | + |
| 16 | +Input = tuple[CharGrid, list[Direction]] |
| 17 | +Output1 = int |
| 18 | +Output2 = int |
| 19 | + |
| 20 | + |
| 21 | +TEST1 = """\ |
| 22 | +######## |
| 23 | +#..O.O.# |
| 24 | +##@.O..# |
| 25 | +#...O..# |
| 26 | +#.#.O..# |
| 27 | +#...O..# |
| 28 | +#......# |
| 29 | +######## |
| 30 | +
|
| 31 | +<^^>>>vv<v>>v<< |
| 32 | +""" |
| 33 | +TEST2 = """\ |
| 34 | +########## |
| 35 | +#..O..O.O# |
| 36 | +#......O.# |
| 37 | +#.OO..O.O# |
| 38 | + |
| 39 | +#O#..O...# |
| 40 | +#O..O..O.# |
| 41 | +#.OO.O.OO# |
| 42 | +#....O...# |
| 43 | +########## |
| 44 | +
|
| 45 | +<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^ |
| 46 | +vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v |
| 47 | +><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv< |
| 48 | +<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^ |
| 49 | +^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^>< |
| 50 | +^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^ |
| 51 | +>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^ |
| 52 | +<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<> |
| 53 | +^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v> |
| 54 | +v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^ |
| 55 | +""" |
| 56 | + |
| 57 | + |
| 58 | +class Solution(SolutionBase[Input, Output1, Output2]): |
| 59 | + def print_grid(self, grid: CharGrid) -> None: |
| 60 | + for line in grid.get_rows_as_strings(): |
| 61 | + log(line) |
| 62 | + |
| 63 | + def parse_input(self, input_data: InputData) -> Input: |
| 64 | + blocks = my_aocd.to_blocks(input_data) |
| 65 | + grid = CharGrid.from_strings(blocks[0]) |
| 66 | + dirs = list[Direction]() |
| 67 | + for line in blocks[1]: |
| 68 | + dirs.extend([Direction.from_str(ch) for ch in line]) |
| 69 | + return grid, dirs |
| 70 | + |
| 71 | + def part_1(self, input: Input) -> Output1: |
| 72 | + def move(line: list[str], r: int) -> list[str]: |
| 73 | + def swap(line: list[str], idx1: int, idx2: int) -> list[str]: |
| 74 | + tmp = line[idx1] |
| 75 | + line[idx1] = line[idx2] |
| 76 | + line[idx2] = tmp |
| 77 | + return line |
| 78 | + |
| 79 | + assert line[r] == "@" |
| 80 | + if line[r + 1] == "#": |
| 81 | + return line |
| 82 | + # max_idx = len(line) - 2 |
| 83 | + # #[email protected]....# -> #.....@O....# |
| 84 | + if line[r + 1] == ".": |
| 85 | + line = swap(line, r, r + 1) |
| 86 | + # #.....@O....# -> #......@O...# |
| 87 | + try: |
| 88 | + dot_idx = line.index(".", r) |
| 89 | + octo_idx = line.index("#", r) |
| 90 | + if dot_idx < octo_idx: |
| 91 | + line.pop(dot_idx) |
| 92 | + line.insert(r, ".") |
| 93 | + except ValueError: |
| 94 | + return line |
| 95 | + return line |
| 96 | + |
| 97 | + def to_array(s: str) -> list[str]: |
| 98 | + return [ch for ch in s] |
| 99 | + |
| 100 | + def to_str(a: list[str]) -> str: |
| 101 | + return "".join(a) |
| 102 | + |
| 103 | + assert to_str(move(to_array("#..@#"), 3)) == "#..@#" |
| 104 | + assert to_str( move( to_array( "#[email protected]....#"), 5)) == "#.....@O....#" # noqa E501 |
| 105 | + assert to_str(move(to_array("#.....@O....#"), 6)) == "#......@O...#" # noqa E501 |
| 106 | + assert to_str(move(to_array("#..@O#"), 3)) == "#..@O#" # noqa E501 |
| 107 | + assert to_str( move( to_array( "#[email protected].#"), 2)) == "#..@OO.#" # noqa E501 |
| 108 | + assert to_str(move(to_array("#.#@O..#"), 3)) == "#.#.@O.#" # noqa E501 |
| 109 | + assert to_str(move(to_array("#.@O#..#"), 2)) == "#.@O#..#" # noqa E501 |
| 110 | + grid, dirs = input |
| 111 | + # self.print_grid(grid) |
| 112 | + # log(dirs) |
| 113 | + robot = next(grid.get_all_equal_to("@")) |
| 114 | + for dir in dirs: |
| 115 | + match dir: |
| 116 | + case Direction.RIGHT: |
| 117 | + row = grid.values[robot.row] |
| 118 | + tmp = move(row, robot.col) |
| 119 | + grid.values[robot.row] = tmp |
| 120 | + robot = Cell(robot.row, tmp.index("@")) |
| 121 | + case Direction.LEFT: |
| 122 | + row = grid.values[robot.row] |
| 123 | + row.reverse() |
| 124 | + tmp = move(row, row.index("@")) |
| 125 | + tmp.reverse() |
| 126 | + grid.values[robot.row] = tmp |
| 127 | + robot = Cell(robot.row, tmp.index("@")) |
| 128 | + case Direction.DOWN: |
| 129 | + col = grid.get_col(robot.col) |
| 130 | + tmp = move(col, robot.row) |
| 131 | + grid.replace_col(robot.col, tmp) |
| 132 | + robot = Cell(tmp.index("@"), robot.col) |
| 133 | + case Direction.UP: |
| 134 | + col = grid.get_col(robot.col) |
| 135 | + col.reverse() |
| 136 | + tmp = move(col, col.index("@")) |
| 137 | + tmp.reverse() |
| 138 | + grid.replace_col(robot.col, tmp) |
| 139 | + robot = Cell(tmp.index("@"), robot.col) |
| 140 | + # self.print_grid(grid) |
| 141 | + ans = 0 |
| 142 | + for cell in grid.get_all_equal_to("O"): |
| 143 | + ans += cell.row * 100 + cell.col |
| 144 | + return ans |
| 145 | + |
| 146 | + def part_2(self, input: Input) -> Output2: |
| 147 | + return 0 |
| 148 | + |
| 149 | + @aoc_samples( |
| 150 | + ( |
| 151 | + ("part_1", TEST1, 2028), |
| 152 | + ("part_1", TEST2, 10092), |
| 153 | + # ("part_2", TEST1, "TODO"), |
| 154 | + ) |
| 155 | + ) |
| 156 | + def samples(self) -> None: |
| 157 | + pass |
| 158 | + |
| 159 | + |
| 160 | +solution = Solution(2024, 15) |
| 161 | + |
| 162 | + |
| 163 | +def main() -> None: |
| 164 | + solution.run(sys.argv) |
| 165 | + |
| 166 | + |
| 167 | +if __name__ == "__main__": |
| 168 | + main() |
0 commit comments