Skip to content

Commit

Permalink
day10: coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-ong committed Dec 27, 2023
1 parent 0c53d23 commit 70f5ea9
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 28 deletions.
25 changes: 20 additions & 5 deletions day10/day10.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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]:
Expand Down Expand Up @@ -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:
Expand All @@ -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


Expand Down
6 changes: 1 addition & 5 deletions day10/lib/direction.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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")
11 changes: 8 additions & 3 deletions day10/lib/pipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")


Expand All @@ -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:
Expand Down
11 changes: 2 additions & 9 deletions day10/lib/position.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
5 changes: 5 additions & 0 deletions day10/tests/input-e.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
..F7.
.FJ|.
FJ.L7
|F--J
LJ...
5 changes: 5 additions & 0 deletions day10/tests/input-f.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
S.F7.
.FJ|.
FJ.L7
|F--J
LJ...
56 changes: 50 additions & 6 deletions day10/tests/test_day10.py
Original file line number Diff line number Diff line change
@@ -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))
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ exclude_lines = [
"def main",
"if TYPE_CHECKING:",
"raise AssertionError",
"# pragma: no cover",
]
omit = ["download_inputs.py", "maker.py"]
precision = 2
Expand Down

0 comments on commit 70f5ea9

Please sign in to comment.