mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 12:50:32 +01:00
Implement 2024 day 18
This commit is contained in:
70
2024/src/aoc/days/day18.py
Normal file
70
2024/src/aoc/days/day18.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from collections import deque
|
||||
|
||||
from . import SeparateRunner
|
||||
|
||||
|
||||
def parse_input(data: str) -> list[tuple[int, int]]:
|
||||
return [tuple(map(int, line.split(","))) for line in data.strip().split("\n")]
|
||||
|
||||
|
||||
def find_exit(fallen: set[tuple[int, int]], width: int, height: int) -> int | None:
|
||||
todo = deque([(0, 0, 0)])
|
||||
|
||||
best = {(0, 0): 0}
|
||||
|
||||
def enqueue(dist: int, x: int, y: int):
|
||||
# print(f"trying {x},{y}")
|
||||
if (x, y) in fallen:
|
||||
return
|
||||
|
||||
if (x, y) not in best or best[x, y] > dist:
|
||||
best[x, y] = dist
|
||||
todo.append((dist, x, y))
|
||||
|
||||
while todo:
|
||||
dist, x, y = todo.popleft()
|
||||
# print(x, y)
|
||||
|
||||
if x == width - 1 and y == height - 1:
|
||||
return dist
|
||||
|
||||
if x > 0:
|
||||
enqueue(dist + 1, x - 1, y)
|
||||
|
||||
if x + 1 < width:
|
||||
enqueue(dist + 1, x + 1, y)
|
||||
|
||||
if y > 0:
|
||||
enqueue(dist + 1, x, y - 1)
|
||||
|
||||
if y + 1 < height:
|
||||
enqueue(dist + 1, x, y + 1)
|
||||
|
||||
|
||||
class DayRunner(SeparateRunner):
|
||||
@classmethod
|
||||
def part1(
|
||||
cls, input: str, width: int = 71, height: int = 71, limit: int = 1024
|
||||
) -> int:
|
||||
falling = parse_input(input)
|
||||
|
||||
return find_exit(set(falling[:limit]), width, height)
|
||||
|
||||
@classmethod
|
||||
def part2(cls, input: str, width: int = 71, height: int = 71) -> str:
|
||||
falling = parse_input(input)
|
||||
|
||||
lower = 0
|
||||
upper = len(falling)
|
||||
|
||||
while lower < upper:
|
||||
mid = lower + (upper - lower) // 2
|
||||
|
||||
if find_exit(set(falling[:mid]), width, height) is not None:
|
||||
lower = mid + 1
|
||||
else:
|
||||
upper = mid
|
||||
|
||||
first_blocker = falling[lower - 1]
|
||||
|
||||
return f"{first_blocker[0]},{first_blocker[1]}"
|
||||
25
2024/tests/samples/18.txt
Normal file
25
2024/tests/samples/18.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
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
|
||||
11
2024/tests/test_day18.py
Normal file
11
2024/tests/test_day18.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from aoc.days.day18 import DayRunner
|
||||
|
||||
from . import get_data
|
||||
|
||||
|
||||
def test_sample_part1() -> None:
|
||||
assert DayRunner.part1(get_data(18), width=7, height=7, limit=12) == 22
|
||||
|
||||
|
||||
def test_sample_part2() -> None:
|
||||
assert DayRunner.part2(get_data(18), width=7, height=7) == "6,1"
|
||||
Reference in New Issue
Block a user