Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor day 18 #28

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 9 additions & 24 deletions aoc_2024/day_18/a.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from dataclasses import dataclass
from functools import cached_property
from queue import PriorityQueue
from aoc_2024.day_18.parser import Parser
from aoc_2024.day_18.solver import Solver
from aoc_2024.utils.point import Point


Expand All @@ -13,29 +13,14 @@ class Day18PartASolver:

@property
def solution(self) -> int:
seen: set[Point] = set()
queue = PriorityQueue[tuple[int, int, Point]]()
queue.put((0, self.start.dist(self.end), self.start))
while not queue.empty():
steps, _, pos = queue.get()

# success
if pos == self.end:
return steps

elif pos not in seen:
seen.add(pos)
for n in pos.neighbors:
if all(
[
self.in_bounds(n),
n not in seen,
n not in self.corrupted_bytes,
]
):
queue.put((steps + 1, n.dist(self.end), n))

assert False, "don't get here plz"
steps = Solver(
byte_positions=self.byte_positions,
grid_size=self.grid_size,
bytes_fallen=self.bytes_fallen,
).min_steps

assert steps is not None
return steps

def in_bounds(self, point: Point) -> bool:
return 0 <= point.x < self.grid_size and 0 <= point.y < self.grid_size
Expand Down
53 changes: 2 additions & 51 deletions aoc_2024/day_18/b.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from dataclasses import dataclass
from functools import cached_property
from queue import PriorityQueue
from aoc_2024.day_18.parser import Parser
from aoc_2024.day_18.solver import Solver
from aoc_2024.utils.point import Point


Expand All @@ -21,62 +20,14 @@ def solution(self) -> str:
grid_size=self.grid_size,
bytes_fallen=mid,
)
if solver.has_solution:
if solver.min_steps is not None:
left = mid + 1
else:
right = mid
p = self.byte_positions[left - 1]
return f"{p.x},{p .y}"


@dataclass
class Solver:
byte_positions: list[Point]
grid_size: int
bytes_fallen: int

@cached_property
def has_solution(self) -> bool:
seen: set[Point] = set()
queue = PriorityQueue[tuple[int, int, Point]]()
queue.put((0, self.start.dist(self.end), self.start))
while not queue.empty():
steps, _, pos = queue.get()

# success
if pos == self.end:
return True

elif pos not in seen:
seen.add(pos)
for n in pos.neighbors:
if all(
[
self.in_bounds(n),
n not in seen,
n not in self.corrupted_bytes,
]
):
queue.put((steps + 1, n.dist(self.end), n))

return False

def in_bounds(self, point: Point) -> bool:
return 0 <= point.x < self.grid_size and 0 <= point.y < self.grid_size

@cached_property
def start(self) -> Point:
return Point(0, 0)

@cached_property
def end(self) -> Point:
return Point(self.grid_size - 1, self.grid_size - 1)

@cached_property
def corrupted_bytes(self) -> set[Point]:
return set(self.byte_positions[: self.bytes_fallen])


def solve(input: str, grid_size: int = 71, bytes_fallen: int = 1024) -> str:
data = Parser.parse(input)
solver = Day18PartASolver(
Expand Down
53 changes: 53 additions & 0 deletions aoc_2024/day_18/solver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from dataclasses import dataclass
from functools import cached_property
from queue import PriorityQueue

from aoc_2024.utils.point import Point


@dataclass
class Solver:
byte_positions: list[Point]
grid_size: int
bytes_fallen: int

@cached_property
def min_steps(self) -> int | None:
seen: set[Point] = set()
queue = PriorityQueue[tuple[int, int, Point]]()
queue.put((0, self.start.dist(self.end), self.start))
while not queue.empty():
steps, _, pos = queue.get()

# success
if pos == self.end:
return steps

elif pos not in seen:
seen.add(pos)
for n in pos.neighbors:
if all(
[
self.in_bounds(n),
n not in seen,
n not in self.corrupted_bytes,
]
):
queue.put((steps + 1, n.dist(self.end), n))

return None

def in_bounds(self, point: Point) -> bool:
return 0 <= point.x < self.grid_size and 0 <= point.y < self.grid_size

@cached_property
def start(self) -> Point:
return Point(0, 0)

@cached_property
def end(self) -> Point:
return Point(self.grid_size - 1, self.grid_size - 1)

@cached_property
def corrupted_bytes(self) -> set[Point]:
return set(self.byte_positions[: self.bytes_fallen])
Loading