diff --git a/day10/day10.py b/day10/day10.py index 79e0a49..f4b306f 100644 --- a/day10/day10.py +++ b/day10/day10.py @@ -35,7 +35,8 @@ def find_s(pipe_map: PipeMap) -> Position: return Position(row_idx, col_idx) except StopIteration: continue - raise RuntimeError("No S pipe found!") + + raise AssertionError("No S pipe found!") def calculate_s(start: Position, pipe_map: PipeMap) -> str: @@ -48,19 +49,22 @@ def calculate_s(start: Position, pipe_map: PipeMap) -> str: for direction in pipe_directions: pos: Position = start.next_position(direction) - tile: Pipe | None = pipe_map.get_pipe(pos) - if tile is None: - raise RuntimeError("Expecting valid pipe") + tile: Pipe | None = pipe_map.get_pipe_safe(pos) + if tile is None: # e.g. S is on an edge + continue opposite_direction = direction.opposite() if opposite_direction in Pipe.PIPE_DIRECTION[tile.character]: connecting.append(direction) + if len(connecting) == 0: + raise ValueError("S is not a a valid pipe") + # should now have connecting == [NORTH, EAST]: for character, pipe_directions in Pipe.PIPE_DIRECTION.items(): if set(connecting) == set(pipe_directions): return character - raise ValueError("No mapping found for `s` pipe") + raise AssertionError("No mapping found for `s` pipe") def find_cycles(pipe_map: PipeMap) -> list[Pipe]: @@ -210,6 +214,10 @@ def part1(pipe_map: PipeMap) -> int: def part2(pipe_map: PipeMap) -> int: find_cycles(pipe_map) + + # print our map before we mutate it + print(pipe_map) + big_map: PipeMap = expand_map(pipe_map) # you can use this to view it lol. with open("day10/big_unfilled.txt", "w", encoding="utf8") as file: @@ -227,6 +235,13 @@ def part2(pipe_map: PipeMap) -> int: total_unknown += sum( 1 if col.pipe_bounds == PipeBounds.UNKNOWN else 0 for col in row ) + # extra: show unknowns: + for row in pipes: + for col in row: + if col.pipe_bounds == PipeBounds.UNKNOWN: + col.pipe_bounds = PipeBounds.INSIDE + print(small_map) + return total_unknown diff --git a/day10/lib/direction.py b/day10/lib/direction.py index d8eb81a..eac6cd5 100644 --- a/day10/lib/direction.py +++ b/day10/lib/direction.py @@ -7,10 +7,6 @@ class Direction(Enum): SOUTH = 2 EAST = 3 WEST = 4 - NORTH_EAST = 5 - NORTH_WEST = 6 - SOUTH_EAST = 7 - SOUTH_WEST = 8 def opposite(self) -> "Direction": if self == Direction.NORTH: @@ -21,4 +17,4 @@ def opposite(self) -> "Direction": return Direction.WEST if self == Direction.WEST: return Direction.EAST - raise RuntimeError("invalid direction") + raise AssertionError("invalid direction") diff --git a/day10/lib/pipes.py b/day10/lib/pipes.py index 42d9f29..61fa42d 100644 --- a/day10/lib/pipes.py +++ b/day10/lib/pipes.py @@ -78,7 +78,7 @@ def next_position( def position(self) -> Position: return Position(self.row, self.col) - def __hash__(self) -> int: + def __hash__(self) -> int: # pragma: no cover return hash(f"{self.row},{self.col}") @@ -95,11 +95,16 @@ def __post_init__(self) -> None: def get_pipe(self, position: Position) -> Pipe: """returns a pipe given its position""" + if not self.is_in_map(position): + raise ValueError(f"Position outside map {position}") return self.pipes[position.row][position.col] + def is_in_map(self, position: Position) -> bool: + return 0 <= position.row < self.height and 0 <= position.col < self.width + def get_pipe_safe(self, position: Position) -> Pipe | None: - if 0 <= position.row < self.height and 0 <= position.col < self.width: - return self.get_pipe(position) + if self.is_in_map(position): + return self.pipes[position.row][position.col] return None def __str__(self) -> str: diff --git a/day10/lib/position.py b/day10/lib/position.py index cbaed32..1ae7a19 100644 --- a/day10/lib/position.py +++ b/day10/lib/position.py @@ -18,15 +18,8 @@ def next_position(self, direction: Direction) -> "Position": return Position(self.row, self.col - 1) elif direction == Direction.SOUTH: return Position(self.row + 1, self.col) - elif direction == Direction.NORTH_EAST: - return Position(self.row - 1, self.col + 1) - elif direction == Direction.NORTH_WEST: - return Position(self.row - 1, self.col - 1) - elif direction == Direction.SOUTH_WEST: - return Position(self.row + 1, self.col - 1) - elif direction == Direction.SOUTH_EAST: - return Position(self.row + 1, self.col + 1) - raise ValueError(f"invalid direction {direction}") + + raise AssertionError(f"invalid direction {direction}") def __hash__(self) -> int: return hash(f"{self.row}:{self.col}") diff --git a/day10/tests/input-e.txt b/day10/tests/input-e.txt new file mode 100644 index 0000000..81c221b --- /dev/null +++ b/day10/tests/input-e.txt @@ -0,0 +1,5 @@ +..F7. +.FJ|. +FJ.L7 +|F--J +LJ... diff --git a/day10/tests/input-f.txt b/day10/tests/input-f.txt new file mode 100644 index 0000000..7a4ef17 --- /dev/null +++ b/day10/tests/input-f.txt @@ -0,0 +1,5 @@ +S.F7. +.FJ|. +FJ.L7 +|F--J +LJ... diff --git a/day10/tests/test_day10.py b/day10/tests/test_day10.py index cfa567b..c755d9e 100644 --- a/day10/tests/test_day10.py +++ b/day10/tests/test_day10.py @@ -1,16 +1,60 @@ -from day10.day10 import INPUT_A, INPUT_B, INPUT_C, INPUT_D, part1, part2, read_input +from typing import TYPE_CHECKING + +import pytest + +from day10.day10 import ( + INPUT_A, + INPUT_B, + INPUT_C, + INPUT_D, + calculate_s, + expand_pipe, + find_s, + part1, + part2, + read_input, +) +from day10.lib.position import Position + +INPUT_E = "day10/tests/input-e.txt" +INPUT_F = "day10/tests/input-f.txt" + + +if TYPE_CHECKING: + from day10.lib.pipes import PipeMap def test_day10() -> None: - pipe_map_a = read_input(INPUT_A) - pipe_map_b = read_input(INPUT_B) + pipe_map_a: PipeMap = read_input(INPUT_A) + pipe_map_b: PipeMap = read_input(INPUT_B) # q1 assert part1(pipe_map_a) == 4 assert part1(pipe_map_b) == 8 + s_pos: Position = find_s(pipe_map_b) + assert s_pos == Position(2, 0) + assert calculate_s(s_pos, pipe_map_b) == "F" + # q2 - pipe_map_c = read_input(INPUT_C) - print(pipe_map_c) + pipe_map_c: PipeMap = read_input(INPUT_C) assert part2(pipe_map_c) == 4 - pipe_map_d = read_input(INPUT_D) + pipe_map_d: PipeMap = read_input(INPUT_D) assert part2(pipe_map_d) == 8 + + # pipe_map no s + pipe_map_no_s: PipeMap = read_input(INPUT_E) + with pytest.raises(AssertionError): + s_pos = find_s(pipe_map_no_s) + + # pipe map, s is incalculable + pipe_map_bad_s: PipeMap = read_input(INPUT_F) + s_pos = find_s(pipe_map_bad_s) + + with pytest.raises(ValueError): + calculate_s(s_pos, pipe_map_no_s) + + with pytest.raises(ValueError): + expand_pipe("^", True) + + with pytest.raises(ValueError): + pipe_map_bad_s.get_pipe(Position(-1, -1)) diff --git a/pyproject.toml b/pyproject.toml index dc1a74a..67fd26c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ exclude_lines = [ "def main", "if TYPE_CHECKING:", "raise AssertionError", + "# pragma: no cover", ] omit = ["download_inputs.py", "maker.py"] precision = 2