From f315ef05c548da80410cb4f6665a9bba7a953f94 Mon Sep 17 00:00:00 2001 From: Tom van der Lee Date: Tue, 2 Dec 2025 07:57:38 +0100 Subject: Day 1 and 2 --- .gitignore | 2 ++ aoc/__init__.py | 6 +++--- day1/__init__.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ day1/example.txt | 10 ++++++++++ day1/test_init.py | 38 +++++++++++++++++++++++++++++++++++ day2/__init__.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ day2/example.txt | 1 + day2/test_init.py | 41 +++++++++++++++++++++++++++++++++++++ 8 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 day1/__init__.py create mode 100644 day1/example.txt create mode 100644 day1/test_init.py create mode 100644 day2/__init__.py create mode 100644 day2/example.txt create mode 100644 day2/test_init.py diff --git a/.gitignore b/.gitignore index 0618b3a..6ad58aa 100644 --- a/.gitignore +++ b/.gitignore @@ -233,3 +233,5 @@ dmypy.json .DS_Store # End of https://www.toptal.com/developers/gitignore/api/python,pycharm+all + +day*/input.txt diff --git a/aoc/__init__.py b/aoc/__init__.py index 7a089f6..0257b69 100644 --- a/aoc/__init__.py +++ b/aoc/__init__.py @@ -17,8 +17,8 @@ class BaseAssignment(Generic[T, I], ABC): def __str__(self): return f"{self.__module__}.{self.__class__.__name__}" - def parse_item(self, item: str) -> I: - return item + def parse_item(self, item: str) -> Iterator[I]: + yield item @property def part(self) -> int: @@ -37,7 +37,7 @@ class BaseAssignment(Generic[T, I], ABC): with open(file, "r") as input_file: for line in input_file.readlines(): - yield self.parse_item(line.strip("\n")) + yield from self.parse_item(line.strip("\n")) def run(self, input: Iterator[I]) -> T: raise NotImplementedError("Please implement run") diff --git a/day1/__init__.py b/day1/__init__.py new file mode 100644 index 0000000..6e95329 --- /dev/null +++ b/day1/__init__.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +from abc import ABC +from typing import Iterator + +from aoc import BaseAssignment, I + + +class Assignment(BaseAssignment, ABC): + def parse_item(self, item: str) -> Iterator[I]: + [rotation, *places] = item + places = int("".join(places)) + + yield -places if rotation == "L" else places + + +class AssignmentOne(Assignment): + example_result = 3 + + def run(self, input: Iterator[int]) -> int: + seen_0 = 0 + position = 50 + for rotation in input: + position = (position + rotation) % 100 + if position == 0: + seen_0 += 1 + + return seen_0 + + +class AssignmentTwo(Assignment): + example_result = 6 + + @staticmethod + def calculate_new_position(position, rotation) -> tuple[int, int]: + previous_position = position + new_position = position + rotation + + overturned = new_position >= 100 or new_position < 0 + + multiplier = position - (0 - -previous_position) // 100 + + if overturned: + new_position = new_position % 100 + + seen_0 = 0 + if new_position == 0 and multiplier == 0: + seen_0 += multiplier + 1 + elif overturned: + seen_0 += multiplier + + return new_position, seen_0 + + def run(self, input: Iterator[int]) -> int: + seen_0_total = 0 + position = 50 + for rotation in input: + position, seen_0 = self.calculate_new_position(position, rotation) + seen_0_total += seen_0 + + return seen_0_total diff --git a/day1/example.txt b/day1/example.txt new file mode 100644 index 0000000..53287c7 --- /dev/null +++ b/day1/example.txt @@ -0,0 +1,10 @@ +L68 +L30 +R48 +L5 +R60 +L55 +L1 +L99 +R14 +L82 diff --git a/day1/test_init.py b/day1/test_init.py new file mode 100644 index 0000000..a24e272 --- /dev/null +++ b/day1/test_init.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +import pytest + +from day1 import AssignmentTwo + + +class TestAssignmentTwo: + data = [ + # Example Data + (50, -68, 82, 1), + (82, -30, 52, 0), + (52, 48, 0, 1), + (0, -5, 95, 0), + (95, 60, 55, 1), + (55, -55, 0, 1), + (0, -1, 99, 0), + (99, -99, 0, 1), + (0, 14, 14, 0), + (14, -82, 32, 1), + # Additional cases + (0, 10, 10, 0), + (0, -10, 90, 0), + (0, 100, 0, 1), + (0, -100, 0, 1), + (80, 40, 20, 1), + (80, 140, 20, 2), + (80, 100, 80, 1), + (0, -300, 0, 3), + (0, 299, 99, 2), + (0, -299, 1, 2), + ] + + @pytest.mark.parametrize("position,rotation,new_position,seen_0", data) + def test_calculate_new_position(self, position, rotation, new_position, seen_0): + assert AssignmentTwo.calculate_new_position(position, rotation) == ( + new_position, + seen_0, + ) diff --git a/day2/__init__.py b/day2/__init__.py new file mode 100644 index 0000000..882bcad --- /dev/null +++ b/day2/__init__.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +import re +from abc import ABC +from typing import Iterator + +from aoc import BaseAssignment, I, T + + +class Assignment(BaseAssignment, ABC): + def parse_item(self, item: str) -> Iterator[range]: + for r in item.split(","): + [start, end] = r.split("-") + yield range(int(start), int(end) + 1) + + def find_invalid_ids(self, item: range) -> Iterator[int]: + raise NotImplementedError() + + def run(self, input: Iterator[range]) -> int: + totals = 0 + for item in input: + totals += sum(self.find_invalid_ids(item)) + + return totals + + +class AssignmentOne(Assignment): + example_result = 1227775554 + + @classmethod + def find_invalid_ids(cls, item: range): + for i in item: + i_str = str(i) + if i_str[: len(i_str) // 2] == i_str[len(i_str) // 2 :]: + yield i + + +class AssignmentTwo(Assignment): + example_result = 4174379265 + + @classmethod + def is_invalid_id(cls, item: str) -> bool: + for i in range(1, (len(item) // 2) + 1): + pattern = item[:i] + rest = item[i:] + + if re.match(rf"({pattern})+$", rest): + return True + + return False + + @classmethod + def find_invalid_ids(cls, item: range): + for i in item: + i_str = str(i) + + if AssignmentTwo.is_invalid_id(i_str): + yield i diff --git a/day2/example.txt b/day2/example.txt new file mode 100644 index 0000000..a3f22ef --- /dev/null +++ b/day2/example.txt @@ -0,0 +1 @@ +11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124 diff --git a/day2/test_init.py b/day2/test_init.py new file mode 100644 index 0000000..dc1ab40 --- /dev/null +++ b/day2/test_init.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +import pytest + +from day2 import AssignmentOne, AssignmentTwo + + +class TestAssignmentOne: + data = [ + (range(11, 23), [11, 22]), + (range(95, 116), [99]), + (range(998, 1013), [1010]), + (range(1188511880, 1188511891), [1188511885]), + (range(222220, 222225), [222222]), + (range(1698522, 1698529), []), + (range(446443, 446450), [446446]), + (range(38593856, 38593863), [38593859]), + ] + + @pytest.mark.parametrize("r,invalid", data) + def test_find_invalid_ids(self, r: range, invalid: list[str]): + assert list(AssignmentOne.find_invalid_ids(r)) == invalid + + +class TestAssignmentTwo: + data = [ + (range(11, 23), [11, 22]), + (range(95, 116), [99, 111]), + (range(998, 1013), [999, 1010]), + (range(1188511880, 1188511891), [1188511885]), + (range(222220, 222225), [222222]), + (range(1698522, 1698529), []), + (range(446443, 446450), [446446]), + (range(38593856, 38593863), [38593859]), + (range(565653, 565660), [565656]), + (range(824824821, 824824828), [824824824]), + (range(2121212118, 2121212125), [2121212121]), + ] + + @pytest.mark.parametrize("r,invalid", data) + def test_find_invalid_ids(self, r: range, invalid: list[str]): + assert list(AssignmentTwo.find_invalid_ids(r)) == invalid -- cgit v1.2.3