From 00000100ef73d2cb93d54bc6e8db846536c82b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Jane=C5=BEi=C4=8D?= Date: Fri, 5 Dec 2025 07:24:00 +0100 Subject: [PATCH] feat: add solution 2025/05 --- data/example/2025/05.txt | 11 +++ src/solution/year_2025/day_05.py | 122 +++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 data/example/2025/05.txt create mode 100644 src/solution/year_2025/day_05.py diff --git a/data/example/2025/05.txt b/data/example/2025/05.txt new file mode 100644 index 000000000..2e9078d --- /dev/null +++ b/data/example/2025/05.txt @@ -0,0 +1,11 @@ +3-5 +10-14 +16-20 +12-18 + +1 +5 +8 +11 +17 +32 diff --git a/src/solution/year_2025/day_05.py b/src/solution/year_2025/day_05.py new file mode 100644 index 000000000..9b8965f --- /dev/null +++ b/src/solution/year_2025/day_05.py @@ -0,0 +1,122 @@ +from typing import Any, Self + + +class Range: + def __init__(self, start: int, end: int) -> None: + self.start = start + self.end = end + + def __str__(self) -> str: + return f"{self.start}..={self.end}" + + def __repr__(self) -> str: + return f"Range({self.start}, {self.end})" + + def __len__(self) -> int: + return max(self.end - self.start + 1, 0) + + def __eq__(self, value: object, /) -> bool: + if not isinstance(value, Range): + return False + return self.start == value.start and self.end == value.end + + def __contains__(self, value: int) -> bool: + return self.start <= value <= self.end + + def discard(self, other: Self) -> list[Self]: + r1, r2 = self.start, self.end + d1, d2 = other.start, other.end + + res = [] + + if r1 < d1: + t = min(r2, d1 - 1) + res.append(Range(r1, t)) + + if r1 < d1 and r2 > d2: + res.append(Range(d2 + 1, r2)) + + if d1 <= r1 <= d2: + res.append(Range(d2 + 1, r2)) + + if r1 > d2: + res.append(self) + + return list(filter(len, res)) + + +def _parse_input(input_data) -> tuple[list[Range], list[int]]: + _ranges, _ids = input_data.split("\n\n") + + ranges = [] + for r in _ranges.splitlines(): + a, b = r.split("-") + ranges.append(Range(int(a), int(b))) + + ids = list(map(int, _ids.splitlines())) + + return ranges, ids + + +def part_1(input_data: str) -> Any: + ranges, ids = _parse_input(input_data) + + c = 0 + for i in ids: + for r in ranges: + if i in r: + c += 1 + break + + return c + + +def part_2(input_data: str) -> Any: + ranges, _ = _parse_input(input_data) + res: list[Range] = [] + + for i, r1 in enumerate(ranges): + acc = [r1] + + for r2 in ranges[i + 1 :]: + nacc = [] + for r3 in acc: + nacc.extend(r3.discard(r2)) + acc = nacc + res.extend(acc) + + return sum(map(len, res)) + + +def test_range(): + r1 = Range(1, 3) + r2 = Range(1, 0) + + # len + assert len(r1) == 3 + assert len(r2) == 0 + + # contains + assert 0 not in r1 + assert 1 in r1 + assert 2 in r1 + assert 3 in r1 + assert 4 not in r1 + + # eq + assert Range(2, 2) == Range(2, 2) + assert Range(300, 1234) == Range(300, 1234) + assert Range(2, 3) != Range(2, 2) + + # discard + r3 = Range(2, 2) + assert r1.discard(r2) == [r1] + assert r1.discard(r3) == [Range(1, 1), Range(3, 3)] + + +def test_part_1(example_data): + assert part_1(example_data) == 3 + + +def test_part_2(example_data): + assert part_2(example_data) == 14