diff --git a/.gitignore b/.gitignore index b3e1e2c..41e19ef 100644 --- a/.gitignore +++ b/.gitignore @@ -132,5 +132,5 @@ dmypy.json .idea # Advent of code related -.session +.setup.json src/adventofcode/inputs diff --git a/.setup.json.template b/.setup.json.template new file mode 100644 index 0000000..edc70d3 --- /dev/null +++ b/.setup.json.template @@ -0,0 +1,4 @@ +{ + "user_agent": "github.com/your-username/your-repo", + "session_cookie": "session cookie value" +} diff --git a/src/adventofcode/scripts/get_inputs.py b/src/adventofcode/scripts/get_inputs.py index 3791dc5..2199abb 100644 --- a/src/adventofcode/scripts/get_inputs.py +++ b/src/adventofcode/scripts/get_inputs.py @@ -1,25 +1,36 @@ +import json import os +from typing import Required, TypedDict from httpx import get from adventofcode.config import ROOT_DIR +class Setup(TypedDict): + user_agent: Required[str] + session_cookie: Required[str] + + def get_input(year: int, day: int): - session = _read_session() - data = _download_input(year, day, session) + """ + Retrieves input from the Advent of Code website for the given year/day. + After retrieving the input, the input is stored in the project. + """ + setup = _read_setup() + data = _download_input(year, day, setup) _save_input(data, year, day) -def _download_input(year: int, day: int, session: str) -> bytes: +def _download_input(year: int, day: int, setup: Setup) -> bytes: """ Downloads the input as text from the advent of code site """ - cookies = {"session": session} + cookies = {"session": setup["session_cookie"]} url = f"https://adventofcode.com/{year}/day/{day}/input" - resp = get(url, cookies=cookies) + resp = get(url, cookies=cookies, headers={"User-Agent": setup["user_agent"]}) resp.raise_for_status() - return resp.content # type: ignore + return resp.content def _save_input(data: bytes, year: int, day: int) -> None: @@ -32,9 +43,15 @@ def _save_input(data: bytes, year: int, day: int) -> None: file.write(data) -def _read_session(): - target = os.path.join(ROOT_DIR, "../../.session") +def _read_setup() -> Setup: + """ + Reads .setup.json from the projects' root directory + + Returns: + Setup: a typed dict + """ + target = os.path.join(ROOT_DIR, "../../.setup.json") path = os.path.abspath(target) with open(path) as f: - return f.read() + return json.load(f) diff --git a/.session.template b/src/adventofcode/year_2024/__init__.py similarity index 100% rename from .session.template rename to src/adventofcode/year_2024/__init__.py diff --git a/src/adventofcode/year_2024/day_01_2024.py b/src/adventofcode/year_2024/day_01_2024.py new file mode 100644 index 0000000..7197c6d --- /dev/null +++ b/src/adventofcode/year_2024/day_01_2024.py @@ -0,0 +1,64 @@ +from collections import Counter + +from adventofcode.registry.decorators import register_solution +from adventofcode.util.exceptions import SolutionNotFoundError +from adventofcode.util.input_helpers import get_input_for_day + + +def parse_input(input_data: list[str]) -> tuple[list, list]: + left_list: list[int] = [] + right_list: list[int] = [] + + for row in input_data: + left, right = row.split(" ") + left_list.append(int(left)) + right_list.append(int(right)) + + return sorted(left_list), sorted(right_list) + + +def calculate_distances(data: tuple[list[int], list[int]]) -> int: + total = 0 + for left, right in zip(*data, strict=True): + total += abs(left - right) + + return total + + +def calculate_similarity(data: tuple[list[int], list[int]]) -> int: + total = 0 + left_list, right_list = data + right_counter = Counter(right_list) + + for num in left_list: + total += right_counter[num] * num + + return total + + +@register_solution(2024, 1, 1) +def part_one(input_data: list[str]): + parsed_input = parse_input(input_data) + answer = calculate_distances(parsed_input) + + if not answer: + raise SolutionNotFoundError(2024, 1, 1) + + return answer + + +@register_solution(2024, 1, 2) +def part_two(input_data: list[str]): + parsed_input = parse_input(input_data) + answer = calculate_similarity(parsed_input) + + if not answer: + raise SolutionNotFoundError(2024, 1, 2) + + return answer + + +if __name__ == "__main__": + data = get_input_for_day(2024, 1) + part_one(data) + part_two(data) diff --git a/tests/adventofcode/year_2024/__init__.py b/tests/adventofcode/year_2024/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/adventofcode/year_2024/test_day_01_2024.py b/tests/adventofcode/year_2024/test_day_01_2024.py new file mode 100644 index 0000000..07f029c --- /dev/null +++ b/tests/adventofcode/year_2024/test_day_01_2024.py @@ -0,0 +1,11 @@ +from adventofcode.year_2024.day_01_2024 import part_one, part_two + +test_input = ["3 4", "4 3", "2 5", "1 3", "3 9", "3 3"] + + +def test_part_one(): + assert part_one(test_input) == 11 + + +def test_part_two(): + assert part_two(test_input) == 31