day12 finished

This commit is contained in:
Ruediger Ludwig 2022-12-12 18:25:47 +01:00
parent 28fb30d493
commit 5c0183ab93
5 changed files with 163 additions and 0 deletions

View file

@ -0,0 +1,93 @@
from __future__ import annotations
from dataclasses import dataclass
from queue import Queue
from typing import Iterator, Self
day_num = 12
def part1(lines: Iterator[str]) -> int:
return Map.create(lines).find_path('S')
def part2(lines: Iterator[str]) -> int:
return Map.create(lines).find_path('a')
@dataclass(frozen=True, slots=True, order=True, eq=True)
class Position:
x: int
y: int
def neighbors(self, width: int, height: int) -> Iterator[Position]:
if self.x < width - 1:
yield Position(self.x + 1, self.y)
if self.y > 0:
yield Position(self.x, self.y - 1)
if self.x > 0:
yield Position(self.x - 1, self.y)
if self.y < height - 1:
yield Position(self.x, self.y + 1)
@dataclass(slots=True, frozen=True)
class Map:
map: list[str]
width: int
height: int
@classmethod
def create(cls, input: Iterator[str]) -> Self:
map = list(input)
width = len(map[0])
height = len(map)
return Map(map, width, height)
def can_climb(self, *, from_pos: Position, to_pos: Position) -> bool:
""" Checks if one gan walk from the elevation at from_pos to the elevation at to_pos """
from_elevation = self.get_elevation(from_pos)
to_elevation = self.get_elevation(to_pos)
if to_elevation == 'E':
return from_elevation >= 'y'
if from_elevation == 'S':
return to_elevation <= 'b'
return ord(to_elevation) <= ord(from_elevation) + 1
def get_elevation(self, position: Position) -> str:
""" returns the elevation at the given position """
return self.map[position.y][position.x]
def find_marker(self, point: str) -> Position:
""" Returns the position of the first marker matching the argument """
for y, row in enumerate(self.map):
for x, char in enumerate(row):
if char == point:
return Position(x, y)
raise Exception(f"Did not find point {point}")
def find_path(self, target: str):
""" Finds a path backwards from the Endpoint to an elevation/marker target """
endpoint = self.find_marker('E')
found: set[Position] = {endpoint}
queue: Queue[tuple[int, Position]] = Queue()
queue.put((0, endpoint))
while not queue.empty():
current_len, current_pos = queue.get()
for next_position in self.next_step(current_pos):
if next_position not in found:
elevation = self.get_elevation(next_position)
if elevation == target:
return current_len + 1
found.add(next_position)
queue.put((current_len + 1, next_position))
raise Exception('No Path found')
def next_step(self, current_pos: Position) -> Iterator[Position]:
""" yields all neighbors, that could have been the previous step to this one"""
for neighbor in current_pos.neighbors(self.width, self.height):
if self.can_climb(from_pos=neighbor, to_pos=current_pos):
yield neighbor