From 07db73aa3edbd11324454dd4567dfd5e589fcbe1 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Sat, 23 Jan 2021 15:52:12 +0100 Subject: [PATCH] Implement 2019 day 1 with tests in Python --- 2019/.gitignore | 141 +++++++++++++++++++++++++++++++++++++++ 2019/README.md | 8 +++ 2019/aoc2019/__init__.py | 0 2019/aoc2019/__main__.py | 28 ++++++++ 2019/aoc2019/day01.py | 23 +++++++ 2019/requirements.txt | 1 + 2019/tests/__init__.py | 0 2019/tests/test_day01.py | 22 ++++++ 8 files changed, 223 insertions(+) create mode 100644 2019/README.md create mode 100644 2019/aoc2019/__init__.py create mode 100644 2019/aoc2019/__main__.py create mode 100644 2019/aoc2019/day01.py create mode 100644 2019/requirements.txt create mode 100644 2019/tests/__init__.py create mode 100644 2019/tests/test_day01.py diff --git a/2019/.gitignore b/2019/.gitignore index e69de29..a1ce8e7 100644 --- a/2019/.gitignore +++ b/2019/.gitignore @@ -0,0 +1,141 @@ +# Virtual environment +venv + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/2019/README.md b/2019/README.md new file mode 100644 index 0000000..6c7fb91 --- /dev/null +++ b/2019/README.md @@ -0,0 +1,8 @@ +# Advent of Code 2019 + +This is a quick-and-dirty implementation of all 2019 problems implemented in +Python because I got fed up with C++ and really couldn't stand the missing +stars on the [events page](https://adventofcode.com/2020/events). + +I'll try to incorporate unit tests and all because it's just nice programming, +but this edition has decidedly less polish than my other attempts. diff --git a/2019/aoc2019/__init__.py b/2019/aoc2019/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/2019/aoc2019/__main__.py b/2019/aoc2019/__main__.py new file mode 100644 index 0000000..50b3aec --- /dev/null +++ b/2019/aoc2019/__main__.py @@ -0,0 +1,28 @@ +import argparse +import importlib +import sys + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument('day', type=int) + parser.add_argument('input', type=argparse.FileType('rt'), nargs='?', default=sys.stdin) + parser.add_argument('-2', '--part2', action='store_true') + + args = parser.parse_args() + + try: + day = importlib.import_module(f'.day{args.day:02d}', __package__) + + if args.part2: + function = day.part2 + else: + function = day.part1 + + print(function(args.input)) + + except ImportError: + sys.exit(f'Invalid day: {args.day}') + + +main() diff --git a/2019/aoc2019/day01.py b/2019/aoc2019/day01.py new file mode 100644 index 0000000..ac06f40 --- /dev/null +++ b/2019/aoc2019/day01.py @@ -0,0 +1,23 @@ +from typing import TextIO + + +def fuel_required(weight: int) -> int: + return max(0, weight // 3 - 2) + + +def recursive_fuel_required(weight: int) -> int: + total = 0 + + while weight > 0: + weight = fuel_required(weight) + total += weight + + return total + + +def part1(data: TextIO) -> int: + return sum(fuel_required(int(line)) for line in data) + + +def part2(data: TextIO) -> int: + return sum(recursive_fuel_required(int(line)) for line in data) diff --git a/2019/requirements.txt b/2019/requirements.txt new file mode 100644 index 0000000..e079f8a --- /dev/null +++ b/2019/requirements.txt @@ -0,0 +1 @@ +pytest diff --git a/2019/tests/__init__.py b/2019/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/2019/tests/test_day01.py b/2019/tests/test_day01.py new file mode 100644 index 0000000..2eaf866 --- /dev/null +++ b/2019/tests/test_day01.py @@ -0,0 +1,22 @@ +from aoc2019.day01 import fuel_required, recursive_fuel_required + +import pytest + + +@pytest.mark.parametrize('weight,required', [ + (12, 2), + (14, 2), + (1969, 654), + (100756, 33583) +]) +def test_fuel_required(weight: int, required: int) -> None: + assert fuel_required(weight) == required + + +@pytest.mark.parametrize('weight,required', [ + (14, 2), + (1969, 966), + (100756, 50346) +]) +def test_fuel_required_recursive(weight: int, required: int) -> None: + assert recursive_fuel_required(weight) == required