From 5c0183ab93cd58305917c11cd803635bd30c3362 Mon Sep 17 00:00:00 2001 From: Ruediger Ludwig Date: Mon, 12 Dec 2022 18:25:47 +0100 Subject: [PATCH] day12 finished --- advent/days/day12/__init__.py | 0 advent/days/day12/data/input.txt | 41 +++++++++++++ advent/days/day12/data/test01.txt | 5 ++ advent/days/day12/solution.py | 93 ++++++++++++++++++++++++++++++ advent/days/day12/test_solution.py | 24 ++++++++ 5 files changed, 163 insertions(+) create mode 100644 advent/days/day12/__init__.py create mode 100644 advent/days/day12/data/input.txt create mode 100644 advent/days/day12/data/test01.txt create mode 100644 advent/days/day12/solution.py create mode 100644 advent/days/day12/test_solution.py diff --git a/advent/days/day12/__init__.py b/advent/days/day12/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/advent/days/day12/data/input.txt b/advent/days/day12/data/input.txt new file mode 100644 index 0000000..d469653 --- /dev/null +++ b/advent/days/day12/data/input.txt @@ -0,0 +1,41 @@ +abcccaaaaaaccccccccaaaaaccccccaaaaaaccccccaaaaaaaacccaaaaaaaccaaaacccccccccccccccccccccccccaaaaaacccccccccccccccccccccccccccccaaaaaa +abcccaaaaaacccccccaaaaaaccccaaaaaaaacccccccaaaaaaaaaaaaaaaaccaaaaacccccccccccccccccccccccccaaaaaacccccccccccccccccccccccccccccaaaaaa +abccccaaaaacaaaccaaaaaaaacccaaaaaaaaacccccccaaaaaaaaaaaaaaaacaaaaaacccccccccaaacccccccccccaaaaaaaaccccccccccaaccccccccccccccccaaaaaa +abccccaaaaccaaaaaaaaaaaaacccaaaaaaaaaacccccaaaaaaaaaaaaaaaaaaacaaaacccccccccaaaacccccccccaaaaaaaaaacccccccccaaaccccccccccccccccccaaa +abcccccccccaaaaaacccaacccccccccaaacaaaccccccaacccccccaaaaaaaaacaacccccccccccaaaacccccccccaaaaaaaaaacccccccccaaaccacaaccccccccccccaaa +abcccccccccaaaaaacccaacccccccccaaacccccccccccccccccccaaaacaaaacccccccaacaaccaaaccccccccccaccaaaaacacccccccccaaaacaaaaccccccccccccaac +abccccccccccaaaaacccccccccccccccacccaaaacccccccccccccaaaacccccccccccccaaaacccccccccccaacccccaaaaccccccccjjjjaaaaaaaaaccccccccccccccc +abccccccccccaaaacccccccccccccccccccaaaaacccccccccccccaaaccccccccccccccaaaaacccccccccaaaaaacccaaccccccccjjjjjjkkaaaacccccccccaacccccc +abcccccaaccccccccccccccccccccccccccaaaaaacccccccccccccaacccccccccccccaaaaaaccccccccccaaaaaccccccccccccjjjjjjjkkkkaacccccaacaaacccccc +abccaaaacccccccccccccccccccccccccccaaaaaaccccccccccccccccccccccccccccaaaacaccccccccaaaaaaaccccaacccccjjjjoooookkkkkkkklllaaaaaaacccc +abccaaaaaacccccccccccccccccccccccccaaaaacccccccccccccccccccccccccccccccaaccccccccccaaaaaaaaccaaaaccccjjjoooooookkkkkkkllllaaaaaacccc +abcccaaaaacccccccccccccccccccccccccccaaaccccccccaaaacccccccccccccccccccccccccccccccaaaaaaaaccaaaaccccjjooooooooppkkppplllllaccaacccc +abccaaaaaccccccccccccaccccccccccccccccccccccccccaaaacccccccccccccccccccccccccccccccccaaacacccaaaacccijjooouuuuoppppppppplllccccccccc +abcccccaacccccccccccaaaaaaaaccccccccccccccccccccaaaaccccaaccccccccaaacccccccccccccaacaaccccccccccccciijoouuuuuuppppppppplllcccaccccc +abcccccccccccccccccccaaaaaaccccccccccccccccccccccaaccccaaaacccccccaaaaccccccccccaaaaaaccccccccccccciiiiootuuuuuupuuuvvpppllccccccccc +abcccccccccccccccccccaaaaaaccaaaaacccccccccccccccccccccaaaacccccccaaaaccccccccccaaaaaaccccccccccccciiinnotuuxxxuuuuvvvpppllccccccccc +abccccccccccccccacccaaaaaaaacaaaaaaacccccccccccccccccccaaaacccccccaaacccccaaaaccaaaaaccccaaccccccciiiinnnttxxxxuuyyyvvqqqllccccccccc +abcccccccccccaaaaccaaaaaaaaaaaaaaaaaaccaacccccccccccccccccccccccccccccccccaaaacccaaaaaccaaacccccciiinnnnnttxxxxxyyyyvvqqqllccccccccc +abaaaacccccccaaaaaaaaaaaaaaaaaaaaaaaaaaaacccccccccccccccccccccccccccccccccaaaacccaaaaaacaaaccccciiinnnnttttxxxxxyyyyvvqqmmmccccccccc +abaaaaccccccccaaaaacccaaaaacaaaaaacaaaaaaccccccccccccccccaaccccccccccccccccaacccccccaaaaaaaaaaciiinnnnttttxxxxxyyyyvvqqqmmmccccccccc +SbaaaacccccccaaaaaccccaaaaaccaaaaaaaaaaaccccccccccccccccaaacaacccccccccccccccccccccccaaaaaaaaachhhnnntttxxxEzzzzyyvvvqqqmmmccccccccc +abaaaacccccccaacaacccccaaaaaaaacaaaaaaaaaccccccccccccccccaaaaaccccccccccccccccccccccccaaaaaaacchhhnnntttxxxxxyyyyyyvvvqqmmmdddcccccc +abaaaacccccccccccccccccccaaaaaacaaaaaaaaaacccccccccccccaaaaaaccccccccaaaccccccccccccccaaaaaaccchhhnnntttxxxxywyyyyyyvvvqqmmmdddccccc +abaacccccccccccccccccccaaaaaaacccccaaaaaaacccccccccccccaaaaaaaacccccaaaacccccccccccccaaaaaaacaahhhmmmttttxxwwyyyyyyyvvvqqmmmdddccccc +abcccccccccccccccccccccaaaaaaacaaccaaacccccccccccccccccaacaaaaacccccaaaacccccccccccccaaacaaaaaahhhmmmmtsssswwyywwwwvvvvqqqmmdddccccc +abcccccccccccccccaaaccccaaaaaaaaaacaaccaaccccccccccccccccaaacaccccccaaaacccccccccccccccccaaaaacahhhmmmmmsssswwywwwwwvvrrqqmmdddccccc +abcccccccccccccaaaaaaccccaaaaaaaaaccaaaacccccccccccccccccaacccccccccccccccccccccccaaaccccaaaaaaahhhhhmmmmssswwwwwrrrrrrrrmmmmddccccc +abcccccccccccccaaaaaaccccaaaaaaaaaaaaaaaaaccccccccccccccccccccccccccccccccccccccaaaaaacccccaaaaachhhhhmmmmsswwwwrrrrrrrrrkkmdddccccc +abccccccccccccccaaaaaccccccaaaaaaaaaaaaaaaccccccccccccccccccccccccccccccccccccccaaaaaaccccaaaaacccchhggmmmssswwrrrrrkkkkkkkkdddacccc +abccaaaacccccccaaaaacccccccccaaaaaacaaaaacccccccccccccccccccccccccccccccccccccccaaaaaaccccaacaaaccccggggmmsssssrrlkkkkkkkkkdddaccccc +abccaaaacccccccaaaaacccccccccaaaaaaccccaacccccccccccccccccccccccccccccccccccccccaaaaaccccccccaaccccccgggmllssssrllkkkkkkkeeeddaccccc +abccaaaacccccccaaacccccccccccaaaaaacccccccccccccccccccaacccccccccccccccccccccccaaaaaacccccccccccccccccggllllssslllkkeeeeeeeeeaaacccc +abcccaaccccccccaaacaaaccccccaaaaaaaaaaacccccccccccccaaaaaacccccccccccccccccccccaaacaaacccccaacccccccccggglllllllllfeeeeeeeeaaaaacccc +abccccccccccaaaaaaaaaaccccccccccccaccaaaccacccccccccaaaaaaccccaaccaacccaaccccccaaaaaaacccccaaccccccccccggglllllllfffeeecccaaaaaacccc +abccccccccccaaaaaaaaacccccccccccccccaaaaaaaccccccccccaaaaaccccaaaaaacccaaaaaaccaaaaaacccaaaaaaaacccccccggggllllfffffccccccaacccccccc +abcccccccccccaaaaaaacccccccccccccccccaaaaaaccaacccccaaaaaccccccaaaaacccaaaaaacaaaaaaacccaaaaaaaaccccccccgggffffffffccccccccccccccccc +abccccccccccccaaaaaaacccccccccccccaaaaaaaaacaaaaccccaaaaacaaaaaaaaaacaaaaaaacaaaaaaaaaccccaaaacccccccccccggffffffacccccccccccccccaaa +abccccccccccccaaaaaaacaaccccccccccaaaaaaaaacaaaacccccaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaacccaaaaacccccccccccaffffaaaaccccccccccccccaaa +abccccccccccccaaacaaaaaacccccccccccaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaacccaaacaaaccaaaaaacccccccccccccccccaaaccccccccccccccaaa +abccccccccccccaaccaaaaaccccccccccccccaaaaaaaccccaaaaaaaaaaaaccccaacccccaaaaaacccaaaccccccaaccaacccccccccccccccccaaacccccccccccaaaaaa +abcccccccccccccccaaaaaaaaccccccccccccaacccacccccccaaaaaaaaaaccccaacccccaaccccccccaccccccccccccccccccccccccccccccccccccccccccccaaaaaa diff --git a/advent/days/day12/data/test01.txt b/advent/days/day12/data/test01.txt new file mode 100644 index 0000000..433e0d2 --- /dev/null +++ b/advent/days/day12/data/test01.txt @@ -0,0 +1,5 @@ +Sabqponm +abcryxxl +accszExk +acctuvwj +abdefghi \ No newline at end of file diff --git a/advent/days/day12/solution.py b/advent/days/day12/solution.py new file mode 100644 index 0000000..ebc768b --- /dev/null +++ b/advent/days/day12/solution.py @@ -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 diff --git a/advent/days/day12/test_solution.py b/advent/days/day12/test_solution.py new file mode 100644 index 0000000..ca0cbce --- /dev/null +++ b/advent/days/day12/test_solution.py @@ -0,0 +1,24 @@ +from advent.common import input + +from .solution import Map, day_num, part1, part2 + + +def test_part1(): + lines = input.read_lines(day_num, 'test01.txt') + expected = 31 + result = part1(lines) + assert result == expected + + +def test_part2(): + lines = input.read_lines(day_num, 'test01.txt') + expected = 29 + result = part2(lines) + assert result == expected + + +def test_path(): + lines = input.read_lines(day_num, 'test01.txt') + expected = 31 + result = Map.create(lines).find_path('S') + assert result == expected