Python: Day 4 solution

This commit is contained in:
Adrian Rumpold
2022-12-16 12:44:56 +01:00
parent d3dc061564
commit afc4963401
4 changed files with 1185 additions and 0 deletions

67
04/py/solution.py Normal file
View File

@@ -0,0 +1,67 @@
import operator
from collections.abc import Callable
from dataclasses import dataclass
from pathlib import Path
@dataclass(frozen=True, slots=True)
class Range:
lower: int
upper: int
@classmethod
def from_pair(cls, s: str) -> "Range":
left, right = [int(num) for num in s.split("-")]
return cls(left, right)
@staticmethod
def _compare(a: "Range", b: "Range", _op: Callable[[bool, bool], bool]):
if _op(
b.lower <= a.lower <= b.upper,
b.lower <= a.upper <= b.upper,
):
return True
if _op(
a.lower <= b.lower <= a.upper,
a.lower <= b.upper <= a.upper,
):
return True
return False
def fully_contains(self, other: "Range") -> bool:
return Range._compare(self, other, operator.and_)
def overlaps(self, other: "Range") -> bool:
return Range._compare(self, other, operator.or_)
def part1(data: str) -> int:
count = 0
for line in data.splitlines():
left, right = [Range.from_pair(s) for s in line.split(",")]
if left.fully_contains(right):
count += 1
return count
def part2(data: str) -> int:
count = 0
for line in data.splitlines():
left, right = [Range.from_pair(s) for s in line.split(",")]
if left.overlaps(right):
count += 1
return count
# Setup
data = (Path(__file__).parents[1] / "input").read_text()
# Problem solving - part 1
result = part1(data)
print(f"Part 1 solution: {result}")
# Problem solving - part 2
result = part2(data)
print(f"Part 2 solution: {result}")

55
04/py/test_day04.py Normal file
View File

@@ -0,0 +1,55 @@
import pytest
from solution import Range, part1, part2
@pytest.fixture
def example() -> str:
return """2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8"""
@pytest.mark.parametrize(
"a,b,expected",
[
(Range(2, 4), Range(3, 4), True),
(Range(2, 4), Range(1, 4), True),
(Range(2, 4), Range(2, 4), True),
(Range(4, 4), Range(3, 4), True),
(Range(2, 4), Range(3, 5), False),
(Range(4, 4), Range(3, 3), False),
],
)
def test_range_fully_contains(a: Range, b: Range, expected: bool) -> None:
assert a.fully_contains(b) == expected
assert b.fully_contains(a) == expected
@pytest.mark.parametrize(
"a,b,expected",
[
(Range(5, 7), Range(7, 9), True),
(Range(2, 8), Range(3, 7), True),
(Range(6, 6), Range(4, 6), True),
(Range(2, 6), Range(4, 8), True),
(Range(5, 7), Range(8, 9), False),
(Range(2, 3), Range(5, 7), False),
(Range(6, 6), Range(4, 5), False),
(Range(2, 2), Range(4, 4), False),
],
)
def test_range_overlaps(a: Range, b: Range, expected: bool) -> None:
assert a.overlaps(b) == expected
assert b.overlaps(a) == expected
def test_part1(example: str) -> None:
assert part1(example) == 2
def test_part2(example: str) -> None:
assert part2(example) == 4