diff --git a/README.md b/README.md index f78e92bc..1f6a9aef 100644 --- a/README.md +++ b/README.md @@ -22,4 +22,4 @@ Development occurs in language-specific directories: |[Day15.hs](hs/src/Day15.hs)|[Day15.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day15.kt)|[day15.py](py/aoc2024/day15.py)|[day15.rs](rs/src/day15.rs)| |[Day16.hs](hs/src/Day16.hs)|[Day16.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day16.kt)|[day16.py](py/aoc2024/day16.py)|[day16.rs](rs/src/day16.rs)| |[Day17.hs](hs/src/Day17.hs)|[Day17.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day17.kt)|[day17.py](py/aoc2024/day17.py)|[day17.rs](rs/src/day17.rs)| -|[Day18.hs](hs/src/Day18.hs)|[Day18.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day18.kt)||| +|[Day18.hs](hs/src/Day18.hs)|[Day18.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day18.kt)|[day18.py](py/aoc2024/day18.py)|| diff --git a/py/aoc2024/day18.py b/py/aoc2024/day18.py new file mode 100644 index 00000000..52e5581d --- /dev/null +++ b/py/aoc2024/day18.py @@ -0,0 +1,86 @@ +""" +Day 18: RAM Run +""" + +from collections import deque +from typing import Iterable + +SAMPLE_INPUT = """ +5,4 +4,2 +4,5 +3,0 +2,1 +6,3 +2,4 +1,5 +0,6 +3,3 +2,6 +5,1 +1,2 +5,5 +2,5 +6,5 +1,4 +0,4 +6,4 +1,1 +6,1 +1,0 +0,5 +1,6 +2,0 +""" + + +def _parse(data: str) -> list[tuple[int, int]]: + return [ + (int(line[: (i := line.index(","))]), int(line[i + 1 :])) + for line in data.splitlines() + if "," in line + ] + + +def findpath(obstacles: Iterable[tuple[int, int]], size: int) -> list[tuple[int, int]]: + visited, queue = set(obstacles), deque(([(0, 0)],)) + while queue: + path = queue.popleft() + x, y = pos = path[-1] + if x == size and y == size: + return path + if pos in visited: + continue + visited.add(pos) + for pos in ((x - 1, y), (x, y - 1), (x, y + 1), (x + 1, y)): + x, y = pos + if 0 <= x <= size and 0 <= y <= size: + queue.append(path + [(x, y)]) + return None + + +def part1(data: str, size: int = 70, n: int = 1024) -> int: + """ + >>> part1(SAMPLE_INPUT, 6, 12) + 22 + """ + return len(findpath(_parse(data)[:n], size)) - 1 + + +def part2(data: str, size: int = 70) -> str: + """ + >>> part2(SAMPLE_INPUT, 6) + '6,1' + """ + obstacles, i = _parse(data), 0 + while True: + path = findpath(obstacles[: i + 1], size) + if path is None: + x, y = obstacles[i] + return f"{x},{y}" + path = set(path) + while obstacles[i] not in path: + i += 1 + + +parts = (part1, part2) diff --git a/py/pyproject.toml b/py/pyproject.toml index 20477690..f481a54e 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -41,6 +41,7 @@ day14 = "aoc2024.day14:parts" day15 = "aoc2024.day15:parts" day16 = "aoc2024.day16:parts" day17 = "aoc2024.day17:parts" +day18 = "aoc2024.day18:parts" [build-system] requires = ["poetry-core"]