diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6e63ff8..4c057aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,13 +24,13 @@ repos: rev: 6.0.0 hooks: - id: flake8 - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.991 - hooks: - - id: mypy - verbose: true - args: [--show-error-codes] - additional_dependencies: ['types-requests'] + # - repo: https://github.com/pre-commit/mirrors-mypy + # rev: v1.6.1 + # hooks: + # - id: mypy + # verbose: true + # args: [--show-error-codes, --explicit-package-bases] + # additional_dependencies: ['types-requests'] # - repo: https://github.com/asottile/reorder_python_imports # rev: v3.9.0 # hooks: diff --git a/README.md b/README.md index 1ea9843..abe2789 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ [201502p]: https://adventofcode.com/2015/day/2 [201503]: https://github.com/Stegallo/adventofcode/blob/master/y_2015/day3.py [201503p]: https://adventofcode.com/2015/day/3 +[201504]: https://github.com/Stegallo/adventofcode/blob/master/y_2015/day4.py +[201504p]: https://adventofcode.com/2015/day/4 +[201505]: https://github.com/Stegallo/adventofcode/blob/master/y_2015/day5.py +[201505p]: https://adventofcode.com/2015/day/5 ## to run the code @@ -50,3 +54,14 @@ note: to avoid tracking in git your credentials: ``` git update-index --assume-unchanged secret.py ``` + +## contributing + +install [pre-commit](https://pre-commit.com/) + +``` +python -m pip install -r requirements/test.txt +``` +``` +pre-commit install +``` diff --git a/requirements/base.txt b/requirements/base.txt index e614a88..903705e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,2 +1,2 @@ -requests pydantic +requests diff --git a/requirements/test.txt b/requirements/test.txt index 592161e..2c24905 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,4 +1,5 @@ -pytest -isort black flake8 +isort +pre-commit +pytest diff --git a/tests/y_2015/test_2015_day5.py b/tests/y_2015/test_2015_day5.py new file mode 100644 index 0000000..38c3ee5 --- /dev/null +++ b/tests/y_2015/test_2015_day5.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from unittest.mock import mock_open, patch + +from pydantic.dataclasses import dataclass + +from y_2015.day5 import Day, NiceString + +with patch("builtins.open", mock_open(read_data="")): + day = Day() + + +def test_NiceString_nice(): + assert NiceString("ugknbfddgicrmopn").nice is True + assert NiceString("aaa").nice is True + assert NiceString("jchzalrnumimnmhp").nice is False + assert NiceString("haegwjzuvuyypxyu").nice is False + assert NiceString("dvszwmarrgswjxmb").nice is False + + +def test_NiceString_correct_nice(): + assert NiceString("qjhvhtzxzqqjkmpb").correct_nice is True + assert NiceString("xxyxx").correct_nice is True + assert NiceString("aaaa").correct_nice is True + assert NiceString("uurcxstgmygtbstg").correct_nice is False + assert NiceString("ieodomkazucvgmuy").correct_nice is False + + +def test_calculate_1(): + @dataclass + class MockNiceString: + nice: bool + + day._Day__input_data = [] + assert day._calculate_1() == 0 + + day._Day__input_data = [MockNiceString(True)] + assert day._calculate_1() == 1 + + day._Day__input_data = [MockNiceString(True), MockNiceString(False)] + assert day._calculate_1() == 1 + + day._Day__input_data = [MockNiceString(True), MockNiceString(True)] + assert day._calculate_1() == 2 + + day._Day__input_data = [ + MockNiceString(True), + MockNiceString(True), + MockNiceString(False), + MockNiceString(False), + ] + assert day._calculate_1() == 2 + + +def test_calculate_2(): + @dataclass + class MockNiceString: + correct_nice: bool + + day._Day__input_data = [MockNiceString(True), MockNiceString(False)] + assert day._calculate_2() == 1 diff --git a/y_2015/day5.py b/y_2015/day5.py new file mode 100644 index 0000000..810d68b --- /dev/null +++ b/y_2015/day5.py @@ -0,0 +1,78 @@ +from pydantic.dataclasses import dataclass + +from common.aoc import AoCDay + + +@dataclass +class NiceString: + text: str + + @property + def nice(self) -> bool: + # 3 vovels + n_vovels = sum(1 for i in self.text if i in "aeiou") + if n_vovels < 3: + return False + + # one letter twice + twice_letter = False + for c, i in enumerate(self.text): + if c == len(self.text) - 1: + break + if i == self.text[c + 1]: + twice_letter = True + break + if not twice_letter: + return False + + # not ab, cd, pq, or xy + if ( + "ab" in self.text + or "cd" in self.text + or "pq" in self.text + or "xy" in self.text + ): + return False + return True + + @property + def correct_nice(self) -> bool: + # pair of any two letters that appears at least twice in the string + # without overlapping + twice_pair = False + for c, i in enumerate(self.text): + if c >= len(self.text) - 2: + break + for j in range(c + 2, len(self.text) - 1): + if self.text[c : c + 2] == self.text[j : j + 2]: + twice_pair = True + break + if not twice_pair: + return False + + # one letter which repeats with exactly one letter between them + letter_repeat = False + for c, i in enumerate(self.text): + if c >= len(self.text) - 2: + break + if self.text[c] == self.text[c + 2]: + letter_repeat = True + break + if not letter_repeat: + return False + + return True + + +class Day(AoCDay): + def __init__(self, test=0): + super().__init__(__name__, test) + + def _preprocess_input(self) -> None: + self.__input_data = [NiceString(i) for i in self._input_data[0]] + + def _calculate_1(self) -> int: + return sum(int(i.nice) for i in self.__input_data) + + def _calculate_2(self) -> int: + return sum(int(i.correct_nice) for i in self.__input_data)