Skip to content

Commit

Permalink
p16
Browse files Browse the repository at this point in the history
  • Loading branch information
vlasovskikh committed Dec 16, 2024
1 parent cb8e842 commit 15d0896
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 37 deletions.
44 changes: 7 additions & 37 deletions aoc24/p06.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,7 @@ class Tile(enum.StrEnum):
NEW_OBSTACLE = "O"


class Dir(enum.Enum):
UP = enum.auto()
RIGHT = enum.auto()
DOWN = enum.auto()
LEFT = enum.auto()

@property
def turn90(self) -> Dir:
match self:
case Dir.UP:
return Dir.RIGHT
case Dir.RIGHT:
return Dir.DOWN
case Dir.DOWN:
return Dir.LEFT
case Dir.LEFT:
return Dir.UP

def __repr__(self):
match self:
case Dir.UP:
return "^"
case Dir.RIGHT:
return ">"
case Dir.DOWN:
return "v"
case Dir.LEFT:
return "<"


type Pos = tuple[utils.Coord, Dir]
type Pos = tuple[utils.Coord, utils.Dir]


class Lab(utils.Grid[Tile]):
Expand All @@ -52,19 +22,19 @@ def find_guard(self) -> utils.Coord:
def step(self, pos: Pos) -> Pos:
c, d = pos
match d:
case Dir.UP:
case utils.Dir.UP:
return self.step_in_dir(c, d, (c[0] - 1, c[1]))
case Dir.RIGHT:
case utils.Dir.RIGHT:
return self.step_in_dir(c, d, (c[0], c[1] + 1))
case Dir.DOWN:
case utils.Dir.DOWN:
return self.step_in_dir(c, d, (c[0] + 1, c[1]))
case Dir.LEFT:
case utils.Dir.LEFT:
return self.step_in_dir(c, d, (c[0], c[1] - 1))
case _:
raise ValueError(f"Unknown direction: {d}")

def step_in_dir(
self, coord: utils.Coord, direction: Dir, new_coord: utils.Coord
self, coord: utils.Coord, direction: utils.Dir, new_coord: utils.Coord
) -> Pos:
if new_coord in self and self[new_coord] == Tile.OBSTACLE:
direction = direction.turn90
Expand All @@ -83,7 +53,7 @@ def path(self, pos: Pos) -> tuple[list[Pos], bool]:


def guard_path(lab: Lab) -> list[Pos]:
path, _ = lab.path((lab.find_guard(), Dir.UP))
path, _ = lab.path((lab.find_guard(), utils.Dir.UP))
return path


Expand Down
89 changes: 89 additions & 0 deletions aoc24/p16.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import collections
import enum
import heapq

from aoc24 import utils


class Cell(enum.StrEnum):
SPACE = "."
WALL = "#"
START = "S"
END = "E"
SEAT = "O"


type Pos = tuple[utils.Coord, utils.Dir]


class Maze(utils.Grid[Cell]):
def shortest_path(self) -> tuple[int, int]:
start = next(c for c in self if self[c] == Cell.START)
end = next(c for c in self if self[c] == Cell.END)
start_pos = start, utils.Dir.RIGHT
heap: list[tuple[int, Pos, Pos]] = [(0, start_pos, start_pos)]
best_score: dict[Pos, int] = {}
traceback: dict[Pos, set[Pos]] = collections.defaultdict(set)

while heap:
score, pos, prev = heapq.heappop(heap)
coord, direction = pos
if pos in best_score and score > best_score[pos]:
continue
if prev != pos:
traceback[pos].add(prev)
if pos in best_score and score == best_score[pos]:
continue
best_score[pos] = score
if self[coord] == Cell.END:
continue
heapq.heappush(heap, (score + 1000, (coord, direction.turn270), pos))
heapq.heappush(heap, (score + 1000, (coord, direction.turn90), pos))
next_coord = self.next(coord, direction)
if self[next_coord] != Cell.WALL:
heapq.heappush(heap, (score + 1, (next_coord, direction), pos))

min_score = min(
[best_score[(end, d)] for d in utils.Dir if (end, d) in best_score]
)
stack: list[Pos] = [
(end, d) for d in utils.Dir if best_score.get((end, d)) == min_score
]
coords: set[utils.Coord] = set()
while stack:
pos = stack.pop()
if pos[0] not in coords:
self[pos[0]] = Cell.SEAT
coords.add(pos[0])
if pos in traceback:
for prev in traceback[pos]:
stack.append(prev)

return min_score, len(coords)

def next(self, coord: utils.Coord, direction: utils.Dir) -> utils.Coord:
match direction:
case utils.Dir.UP:
diff = -1, 0
case utils.Dir.RIGHT:
diff = 0, 1
case utils.Dir.DOWN:
diff = 1, 0
case utils.Dir.LEFT:
diff = 0, -1
return utils.add_coord(coord, diff)


def parse_input(lines: list[str]) -> Maze:
return Maze([[Cell(c) for c in line] for line in lines])


def main() -> None:
maze = parse_input(utils.read_input_lines(__file__))
score, seats = maze.shortest_path()
print(score)
print(seats)


if __name__ == "__main__":
main()
43 changes: 43 additions & 0 deletions aoc24/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations
import dataclasses
import enum
import itertools
import os
import typing
Expand Down Expand Up @@ -113,3 +114,45 @@ def __str__(self) -> str:
def __contains__(self, coord: Coord) -> bool:
lines, positions = self.size()
return 0 <= coord[0] < lines and 0 <= coord[1] < positions


class Dir(enum.IntEnum):
UP = enum.auto()
RIGHT = enum.auto()
DOWN = enum.auto()
LEFT = enum.auto()

@property
def turn90(self) -> Dir:
match self:
case Dir.UP:
return Dir.RIGHT
case Dir.RIGHT:
return Dir.DOWN
case Dir.DOWN:
return Dir.LEFT
case Dir.LEFT:
return Dir.UP

@property
def turn270(self) -> Dir:
match self:
case Dir.UP:
return Dir.LEFT
case Dir.RIGHT:
return Dir.UP
case Dir.DOWN:
return Dir.RIGHT
case Dir.LEFT:
return Dir.DOWN

def __repr__(self):
match self:
case Dir.UP:
return "^"
case Dir.RIGHT:
return ">"
case Dir.DOWN:
return "v"
case Dir.LEFT:
return "<"
Loading

0 comments on commit 15d0896

Please sign in to comment.