diff --git a/2020/__init__.py b/2020/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/2020/day10-adapter-array.py b/2020/day10-adapter-array.py deleted file mode 100644 index b140a53..0000000 --- a/2020/day10-adapter-array.py +++ /dev/null @@ -1,42 +0,0 @@ -import copy -import itertools -from typing import List - - -def main(filename: str, expected_part_1: int = None, expected_part_2: int = None): - print(f"\n+ Running on {filename}") - with open(filename) as f: - jolts = sorted(map(int, f.read().strip().split("\n"))) # type: List[int] - - jolts.append(jolts[-1] + 3) - - counter_part_1 = solve_part_1(jolts) - counter_part_2 = 0 - - print(f"1. Found {counter_part_1}") - if expected_part_1: - assert expected_part_1 == counter_part_1 - - print(f"2. Found {counter_part_2}") - if expected_part_2: - assert expected_part_2 == counter_part_2 - - -def solve_part_1(jolts): - diffs = count_diffs(jolts) - return diffs[1] * diffs[3] - - -def count_diffs(jolts): - diffs = {1: 0, 2: 0, 3: 0} - previous = 0 - for jolt in jolts: - diffs[jolt - previous] += 1 - previous = jolt - return diffs - - -if __name__ == "__main__": - main("inputs/day10-test1", 35, 8) - main("inputs/day10-test2", 220, 19208) - main("inputs/day10", 2450) diff --git a/2020/day10_adapter_array.py b/2020/day10_adapter_array.py new file mode 100644 index 0000000..a0ed0e5 --- /dev/null +++ b/2020/day10_adapter_array.py @@ -0,0 +1,83 @@ +import copy +import functools +import itertools +from typing import List, Tuple + +import networkx as nx + + +def main(filename: str, expected_part_1: int = None, expected_part_2: int = None): + print(f"\n+ Running on {filename}") + with open(filename) as f: + jolts = sorted(map(int, f.read().strip().split("\n"))) # type: List[int] + + jolts.append(jolts[-1] + 3) + + counter_part_1 = solve_part_1(jolts) + jolts.insert(0, 0) + counter_part_2 = solve_part_2(jolts) + + print(f"1. Found {counter_part_1}") + if expected_part_1: + assert expected_part_1 == counter_part_1 + + print(f"2. Found {counter_part_2}") + if expected_part_2: + assert expected_part_2 == counter_part_2 + + +def solve_part_1(jolts): + diffs = count_diffs(jolts) + return diffs[1] * diffs[3] + + +def count_diffs(jolts): + diffs = {1: 0, 2: 0, 3: 0} + previous = 0 + for jolt in jolts: + diffs[jolt - previous] += 1 + previous = jolt + return diffs + + +def solve_part_2(jolts: List[int]): + jolts = tuple(jolts) + return with_recursion(jolts, jolts[-1]) + + +@functools.lru_cache(maxsize=None) +def with_recursion(jolts: Tuple[int], target: int): + if target == 0: + return 1 + counter = 0 + for jolt in jolts: + if can_reach(jolt, target): + counter += with_recursion(jolts, jolt) + return counter + + +def can_reach(jolt, target): + return 0 < target - jolt < 4 + + +def with_graph(jolts): + """ + It works but it's way too slow for the real deal. + """ + graph = nx.DiGraph() + combinations = itertools.combinations(jolts, 2) + for combination in combinations: + combination = sorted(combination) + diff = combination[1] - combination[0] + if 0 < diff < 4: + graph.add_edge(combination[0], combination[1]) + paths_counter = 0 + for _ in nx.all_simple_paths(graph, jolts[0], jolts[-1]): + paths_counter += 1 + return paths_counter + + +if __name__ == "__main__": + main("inputs/day10-test1", 35, 8) + main("inputs/day10-test2", 220, 19208) + main("inputs/day10", 2450) diff --git a/2020/test_day10_part2.py b/2020/test_day10_part2.py new file mode 100644 index 0000000..5a4c7c4 --- /dev/null +++ b/2020/test_day10_part2.py @@ -0,0 +1,13 @@ +from day10_adapter_array import solve_part_2 + + +def test_only_one_path(): + assert solve_part_2([0, 1]) == 1 + + +def test_one_long_paths(): + assert solve_part_2([0, 1, 4, 7]) == 1 + + +def test_two_paths(): + assert solve_part_2([0, 1, 2]) == 2 diff --git a/poetry.lock b/poetry.lock index 51875c4..10ad2a9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,6 +6,28 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "20.3.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + [[package]] name = "cfgv" version = "3.2.0" @@ -14,6 +36,14 @@ category = "dev" optional = false python-versions = ">=3.6.1" +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "decorator" version = "4.4.2" @@ -61,6 +91,14 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" [package.extras] license = ["editdistance"] +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "networkx" version = "2.5" @@ -93,6 +131,17 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "packaging" +version = "20.7" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pyparsing = ">=2.0.2" + [[package]] name = "pdbpp" version = "0.10.2" @@ -110,6 +159,17 @@ wmctrl = "*" funcsigs = ["funcsigs"] testing = ["funcsigs", "pytest"] +[[package]] +name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +dev = ["pre-commit", "tox"] + [[package]] name = "pre-commit" version = "2.9.2" @@ -126,6 +186,14 @@ pyyaml = ">=5.1" toml = "*" virtualenv = ">=20.0.8" +[[package]] +name = "py" +version = "1.9.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "pygments" version = "2.7.3" @@ -134,6 +202,14 @@ category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + [[package]] name = "pyreadline" version = "2.1" @@ -150,6 +226,28 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "pytest" +version = "6.1.2" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=17.4.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<1.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +checkqa_mypy = ["mypy (==0.780)"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + [[package]] name = "pyyaml" version = "5.3.1" @@ -203,17 +301,29 @@ python-versions = "*" [metadata] lock-version = "1.1" python-versions = "3.8" -content-hash = "4419fde085bee7e6b0c5480981594be872c8f65085cf2c966652a33d763eecc7" +content-hash = "d3492eb0a2265986bb41e5d19316233006e02e29c8fd44e45535b7e2b9a52658" [metadata.files] appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, +] cfgv = [ {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, ] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] decorator = [ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, @@ -234,6 +344,10 @@ identify = [ {file = "identify-1.5.10-py2.py3-none-any.whl", hash = "sha256:cc86e6a9a390879dcc2976cef169dd9cc48843ed70b7380f321d1b118163c60e"}, {file = "identify-1.5.10.tar.gz", hash = "sha256:943cd299ac7f5715fcb3f684e2fc1594c1e0f22a90d15398e5888143bd4144b5"}, ] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] networkx = [ {file = "networkx-2.5-py3-none-any.whl", hash = "sha256:8c5812e9f798d37c50570d15c4a69d5710a18d77bafc903ee9c5fba7454c616c"}, {file = "networkx-2.5.tar.gz", hash = "sha256:7978955423fbc9639c10498878be59caf99b44dc304c2286162fd24b458c1602"}, @@ -242,17 +356,33 @@ nodeenv = [ {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, ] +packaging = [ + {file = "packaging-20.7-py2.py3-none-any.whl", hash = "sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376"}, + {file = "packaging-20.7.tar.gz", hash = "sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236"}, +] pdbpp = [ {file = "pdbpp-0.10.2.tar.gz", hash = "sha256:73ff220d5006e0ecdc3e2705d8328d8aa5ac27fef95cc06f6e42cd7d22d55eb8"}, ] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] pre-commit = [ {file = "pre_commit-2.9.2-py2.py3-none-any.whl", hash = "sha256:949b13efb7467ae27e2c8f9e83434dacf2682595124d8902554a4e18351e5781"}, {file = "pre_commit-2.9.2.tar.gz", hash = "sha256:e31c04bc23741194a7c0b983fe512801e151a0638c6001c49f2bd034f8a664a1"}, ] +py = [ + {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, + {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, +] pygments = [ {file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"}, {file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"}, ] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] pyreadline = [ {file = "pyreadline-2.1.win-amd64.exe", hash = "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b"}, {file = "pyreadline-2.1.win32.exe", hash = "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e"}, @@ -261,6 +391,10 @@ pyreadline = [ pyrepl = [ {file = "pyrepl-0.9.0.tar.gz", hash = "sha256:292570f34b5502e871bbb966d639474f2b57fbfcd3373c2d6a2f3d56e681a775"}, ] +pytest = [ + {file = "pytest-6.1.2-py3-none-any.whl", hash = "sha256:4288fed0d9153d9646bfcdf0c0428197dba1ecb27a33bb6e031d002fa88653fe"}, + {file = "pytest-6.1.2.tar.gz", hash = "sha256:c0a7e94a8cdbc5422a51ccdad8e6f1024795939cc89159a0ae7f0b316ad3823e"}, +] pyyaml = [ {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, diff --git a/pyproject.toml b/pyproject.toml index d61e363..17bde89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ pdbpp = "^0.10.2" [tool.poetry.dev-dependencies] pre-commit = "^2.9.2" +pytest = "^6.1.2" [build-system] requires = ["poetry-core>=1.0.0"]