Skip to content

Commit 1b23a45

Browse files
committed
AoC 2024 Day 15 Part 1
1 parent 1ff2e7d commit 1b23a45

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

src/main/python/AoC2024_15.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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()

src/main/python/aoc/grid.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@ def set_value(self, c: Cell, value: T) -> None:
143143
def get_row_as_string(self, row: int) -> str:
144144
pass
145145

146+
@abstractmethod
147+
def get_col(self, col: int) -> list[T]:
148+
pass
149+
150+
@abstractmethod
151+
def replace_col(self, col: int, val: list[T]) -> None:
152+
pass
153+
146154
def size(self) -> int:
147155
return self.get_height() * self.get_width()
148156

@@ -264,6 +272,12 @@ def increment(self, c: Cell) -> None:
264272
def get_row_as_string(self, row: int) -> str:
265273
return "".join(str(_) for _ in self.values[row])
266274

275+
def get_col(self, col: int) -> list[int]:
276+
raise NotImplementedError
277+
278+
def replace_col(self, col: int, val: list[int]) -> None:
279+
raise NotImplementedError
280+
267281

268282
@dataclass(frozen=True)
269283
class CharGrid(Grid[str]):
@@ -295,6 +309,14 @@ def set_value(self, c: Cell, value: str) -> None:
295309
def get_row_as_string(self, row: int) -> str:
296310
return "".join(self.values[row])
297311

312+
def get_col(self, col: int) -> list[str]:
313+
return [self.values[row][col] for row in range(self.get_height())]
314+
315+
def replace_col(self, col: int, val: list[str]) -> None:
316+
assert len(val) == self.get_height()
317+
for row in range(self.get_height()):
318+
self.values[row][col] = val[row]
319+
298320
def get_col_as_string(self, col: int) -> str:
299321
return "".join(
300322
self.values[row][col] for row in range(self.get_height())

src/test/python/test_grid.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,20 @@ def test_roll_column(self) -> None:
108108
grid, CharGrid.from_strings(["#.#....", "###....", ".#....."])
109109
)
110110

111+
def test_get_col(self) -> None:
112+
grid = CharGrid.from_strings(["ABC", "DEF", "GHI"])
113+
114+
col = grid.get_col(1)
115+
116+
self.assertEqual(col, ["B", "E", "H"])
117+
118+
def test_replace_col(self) -> None:
119+
grid = CharGrid.from_strings(["ABC", "DEF", "GHI"])
120+
121+
grid.replace_col(1, ["X", "X", "X"])
122+
123+
self.assertEqual(grid, CharGrid.from_strings(["AXC", "DXF", "GXI"]))
124+
111125

112126
class IntGridIteratorTest(unittest.TestCase):
113127

0 commit comments

Comments
 (0)