From 5a9667094c61a24cdbb92e6ec581b43ce6ac2fc2 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Mon, 2 Dec 2024 08:54:30 +0100 Subject: [PATCH] Implement 2024 day 2 --- 2024/src/aoc/days/day2.py | 47 +++++++++++++++++++++++++++++++++++++++ 2024/tests/samples/02.txt | 6 +++++ 2024/tests/test_day2.py | 26 ++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 2024/src/aoc/days/day2.py create mode 100644 2024/tests/samples/02.txt create mode 100644 2024/tests/test_day2.py diff --git a/2024/src/aoc/days/day2.py b/2024/src/aoc/days/day2.py new file mode 100644 index 0000000..e303eb1 --- /dev/null +++ b/2024/src/aoc/days/day2.py @@ -0,0 +1,47 @@ +import numpy + +from . import SeparateRunner + + +def _safe(nums: numpy.ndarray) -> bool: + steps = nums[1:] - nums[:-1] + + if numpy.all(steps > 0): + return numpy.all((steps >= 1) & (steps <= 3)) + elif numpy.all(steps < 0): + return numpy.all((steps <= -1) & (steps >= -3)) + else: + return False + + +def is_safe(line: str) -> bool: + nums = numpy.fromstring(line, dtype=numpy.int32, sep=" ") + + return _safe(nums) + + +def is_savable(line: str) -> bool: + nums = numpy.fromstring(line, dtype=numpy.int32, sep=" ") + + return any( + _safe(numpy.concatenate((nums[:i], nums[i + 1 :]), axis=None)) + for i in range(len(nums)) + ) + + +class DayRunner(SeparateRunner): + @classmethod + def part1(cls, data: str) -> int: + lines = data.strip().split("\n") + + safe = sum(1 for line in lines if is_safe(line)) + + return safe + + @classmethod + def part2(cls, data: str) -> int: + lines = data.strip().split("\n") + + safe = sum(1 for line in lines if is_savable(line)) + + return safe diff --git a/2024/tests/samples/02.txt b/2024/tests/samples/02.txt new file mode 100644 index 0000000..b49c10d --- /dev/null +++ b/2024/tests/samples/02.txt @@ -0,0 +1,6 @@ +7 6 4 2 1 +1 2 7 8 9 +9 7 6 2 1 +1 3 2 4 5 +8 6 4 4 1 +1 3 6 7 9 diff --git a/2024/tests/test_day2.py b/2024/tests/test_day2.py new file mode 100644 index 0000000..f9843b1 --- /dev/null +++ b/2024/tests/test_day2.py @@ -0,0 +1,26 @@ +import os + +from aoc.days.day2 import DayRunner, is_savable + + +def get_data() -> str: + sample = os.path.dirname(__file__) + "/samples/02.txt" + with open(sample, mode="rt", encoding="utf-8") as f: + return f.read() + + +def test_individual_samples() -> None: + assert is_savable("7 6 4 2 1") + assert not is_savable("1 2 7 8 9") + assert not is_savable("9 7 6 2 1") + assert is_savable("1 3 2 4 5") + assert is_savable("8 6 4 4 1") + assert is_savable("1 3 6 7 9") + + +def test_sample_part1() -> None: + assert DayRunner.part1(get_data()) == 2 + + +def test_sample_part2() -> None: + assert DayRunner.part2(get_data()) == 4