diff --git a/.github/README.md b/.github/README.md index 4f36cec..f334214 100644 --- a/.github/README.md +++ b/.github/README.md @@ -14,33 +14,33 @@ Puzzle inputs and descriptions are not included in this repository. You'll have -| Day | | Python | Go | -| --------------------------- | ----------------------------- | ------------------------------------- | --------------------------------- | -| [1](/01-reportRepair) | ![Completed][check] | [Link](/01-reportRepair/python) | [Link](/01-reportRepair/go) | -| [2](/02-passwordPhilosophy) | ![Completed][check] | [Link](/02-passwordPhilosophy/python) | [Link](/02-passwordPhilosophy/go) | -| [3](/03-tobogganTrajectory) | ![Completed][check] | [Link](/03-tobogganTrajectory/python) | [Link](/03-tobogganTrajectory/go) | -| [4](/04-passportProcessing) | ![Completed][check] | [Link](/04-passportProcessing/python) | [Link](/04-passportProcessing/go) | -| [5](/05-binaryBoarding) | ![Completed][check] | [Link](/05-binaryBoarding/python) | [Link](/05-binaryBoarding/go) | -| [6](/06-customCustoms) | ![Completed][check] | [Link](/06-customCustoms/python) | [Link](/06-customCustoms/go) | -| [7](/07-handyHaversacks) | ![Completed][check] | [Link](/07-handyHaversacks/python) | [Link](/07-handyHaversacks/go) | -| [8](/08-handheldHalting) | ![Completed][check] | [Link](/08-handheldHalting/python) | [Link](/08-handheldHalting/go) | -| [9](/09-encodingError) | ![Completed][check] | [Link](/09-encodingError/python) | [Link](/09-encodingError/go) | -| [10](/10-adapterArray) | ![Completed][check] | [Link](/10-adapterArray/python) | [Link](/10-adapterArray/go) | -| [11](/11-seatingSystem) \* | ![Completed][check] | [Link](/11-seatingSystem/python) | [Link](/11-seatingSystem/python) | -| [12](/12-rainRisk) \* | ![Completed][check] | [Link](/12-rainRisk/python) | [Link](/12-rainRisk/go) | -| 13 | ![Not yet attempted][pending] | | | -| 14 | | | | -| 15 | | | | -| 16 | | | | -| 17 | | | | -| 18 | | | | -| 19 | | | | -| 20 | | | | -| 21 | | | | -| 22 | | | | -| 23 | | | | -| 24 | | | | -| 25 | | | | +| Day | | Python | Go | +| --------------------------- | ------------------------------ | ------------------------------------- | --------------------------------- | +| [1](/01-reportRepair) | ![Completed][check] | [Link](/01-reportRepair/python) | [Link](/01-reportRepair/go) | +| [2](/02-passwordPhilosophy) | ![Completed][check] | [Link](/02-passwordPhilosophy/python) | [Link](/02-passwordPhilosophy/go) | +| [3](/03-tobogganTrajectory) | ![Completed][check] | [Link](/03-tobogganTrajectory/python) | [Link](/03-tobogganTrajectory/go) | +| [4](/04-passportProcessing) | ![Completed][check] | [Link](/04-passportProcessing/python) | [Link](/04-passportProcessing/go) | +| [5](/05-binaryBoarding) | ![Completed][check] | [Link](/05-binaryBoarding/python) | [Link](/05-binaryBoarding/go) | +| [6](/06-customCustoms) | ![Completed][check] | [Link](/06-customCustoms/python) | [Link](/06-customCustoms/go) | +| [7](/07-handyHaversacks) | ![Completed][check] | [Link](/07-handyHaversacks/python) | [Link](/07-handyHaversacks/go) | +| [8](/08-handheldHalting) | ![Completed][check] | [Link](/08-handheldHalting/python) | [Link](/08-handheldHalting/go) | +| [9](/09-encodingError) | ![Completed][check] | [Link](/09-encodingError/python) | [Link](/09-encodingError/go) | +| [10](/10-adapterArray) | ![Completed][check] | [Link](/10-adapterArray/python) | [Link](/10-adapterArray/go) | +| [11](/11-seatingSystem) \* | ![Completed][check] | [Link](/11-seatingSystem/python) | [Link](/11-seatingSystem/python) | +| [12](/12-rainRisk) \* | ![Completed][check] | [Link](/12-rainRisk/python) | [Link](/12-rainRisk/go) | +| [13](/13-shuttleSearch) | ![Partially complete][partial] | [Link](/13-shuttleSearch/python) | | +| 14 | | | | +| 15 | | | | +| 16 | | | | +| 17 | | | | +| 18 | | | | +| 19 | | | | +| 20 | | | | +| 21 | | | | +| 22 | | | | +| 23 | | | | +| 24 | | | | +| 25 | | | | diff --git a/13-shuttleSearch/README.md b/13-shuttleSearch/README.md new file mode 100644 index 0000000..e18a5ed --- /dev/null +++ b/13-shuttleSearch/README.md @@ -0,0 +1,35 @@ +# [Day 13: Shuttle Search](https://adventofcode.com/2020/day/13) + +No, I don't really know why the maths works, but it works! + +Here are the notes I made for part two to work this out for a test case before implementing it for the first time to make sure that my method worked. + +![test notes](working.jpg) + +### Related + +* [Maths with Jay - Chinese remainder theorem](https://www.youtube.com/watch?v=zIFehsBHB8o) +* [StackOverflow - Modular multiplicative inverse function in Python](https://stackoverflow.com/a/9758173) + +
Script output + +``` +❯ python .\python\ +AoC 2020: day 13 - Shuttle Search +Python 3.8.5 + +Test cases +1.1 pass +2.1 pass +2.2 pass +2.3 pass +2.4 pass +2.5 pass +2.6 pass + +Answers +Part 1: 4782 +Part 2: 1118684865113056 +``` + +
diff --git a/13-shuttleSearch/info.json b/13-shuttleSearch/info.json new file mode 100644 index 0000000..061428e --- /dev/null +++ b/13-shuttleSearch/info.json @@ -0,0 +1,39 @@ +{ + "year": "2020", + "day": "13", + "title": "Shuttle Search", + "testCases": { + "one": [ + { + "input": "939\n7,13,x,x,59,x,31,19", + "expected": 295 + } + ], + "two": [ + { + "input": "939\n7,13,x,x,59,x,31,19", + "expected": 1068781 + }, + { + "input": "1\n17,x,13,19", + "expected": 3417 + }, + { + "input": "1\n67,7,59,61", + "expected": 754018 + }, + { + "input": "1\n67,x,7,59,61", + "expected": 779210 + }, + { + "input": "1\n67,7,x,59,61", + "expected": 1261476 + }, + { + "input": "1\n1789,37,47,1889", + "expected": 1202161486 + } + ] + } +} \ No newline at end of file diff --git a/13-shuttleSearch/python/__main__.py b/13-shuttleSearch/python/__main__.py new file mode 100644 index 0000000..da7ce4c --- /dev/null +++ b/13-shuttleSearch/python/__main__.py @@ -0,0 +1,77 @@ +import json +import platform +import sys + +from rich import print + +from partOne import partOne +from partTwo import partTwo + + +def run_tests(test_cases): + do_tests = True + if len(test_cases) == 0: + do_tests = False + elif len(test_cases["one"]) == 0 and len(test_cases["two"]) == 0: + do_tests = False + + if not do_tests: + print("Info: no test cases specified. Skipping tests\n") + return + + print("Test cases") + + def rt(tcs, f, n): + for i, tc in enumerate(tcs): + print(f"{n}.{i+1} ", end="") + expectedInt = tc["expected"] + result = f(str(tc["input"])) + if result == expectedInt: + print("[green]pass[/green]") + else: + print(f"[red]fail[/red] (got {result}, expected {expectedInt})") + + rt(test_cases["one"], partOne, 1) + rt(test_cases["two"], partTwo, 2) + + print() + + +if __name__ == "__main__": + try: + info = open("info.json").read() + except FileNotFoundError: + print("Error: could not open info.json") + sys.exit(-1) + + info = json.loads(info) + + year = info["year"] + day = info["day"] + title = info["title"] + + print(f"[yellow]AoC {year}[/yellow]: day {day} - {title}") + print(f"Python {platform.python_version()}\n") + + try: + challenge_input = open("input.txt").read() + except FileNotFoundError: + print("Error: could not open input.txt") + sys.exit(-1) + + if "vis" in sys.argv: + import visualise + + print("[green]Running visualisation....[/green]") + + visualise.visualise(challenge_input) + sys.exit() + + run_tests(info["testCases"]) + + if "debug" in sys.argv: + sys.exit() + + print("Answers") + print("Part 1:", partOne(challenge_input)) + print("Part 2:", partTwo(challenge_input)) diff --git a/13-shuttleSearch/python/partOne.py b/13-shuttleSearch/python/partOne.py new file mode 100644 index 0000000..e330a5d --- /dev/null +++ b/13-shuttleSearch/python/partOne.py @@ -0,0 +1,29 @@ +from typing import List + + +class Timetable: + earliest_departure: int + services: List[int] + + def __init__(self, earliest_departure: int, services: str): + self.earliest_departure = earliest_departure + self.services = [int(s) for s in services.split(",") if s != "x"] + + +def parse(instr: str) -> Timetable: + instr = instr.strip().split("\n") + return Timetable(int(instr[0]), instr[1]) + + +def partOne(instr: str) -> int: + timetable = parse(instr) + + earliest_times = [] + for service in timetable.services: + earliest_times.append( + (service, (int(timetable.earliest_departure / service) + 1) * service) + ) + + route, earliest_departure = min(earliest_times, key=lambda x: x[1]) + + return route * (earliest_departure - timetable.earliest_departure) diff --git a/13-shuttleSearch/python/partTwo.py b/13-shuttleSearch/python/partTwo.py new file mode 100644 index 0000000..3619f9a --- /dev/null +++ b/13-shuttleSearch/python/partTwo.py @@ -0,0 +1,32 @@ +def partTwo(instr: str) -> int: + # This is the parsing section + service_list = instr.strip().split("\n")[-1].split(",") + + eqns = [] + for i, svc in enumerate(service_list): + if svc == "x": + continue + svc = int(svc) + + v = 0 + if i != 0: + v = svc - i # This is the only maths stuff in the parsing + + eqns.append((v, svc)) + + # This is the maths section + + n = 1 + for (_, v) in eqns: + n *= v + + sigma_x = 0 + for (bi, ni) in eqns: + # this type cast could potentially cause a problem. + # int required for pow function and the division *should* produce a whole number anyway + Ni = int(n / ni) + yi = pow(Ni, -1, ni) # https://stackoverflow.com/a/9758173 + print(Ni, ni, yi) + sigma_x += bi * Ni * yi + + return sigma_x % n diff --git a/13-shuttleSearch/working.jpg b/13-shuttleSearch/working.jpg new file mode 100644 index 0000000..45fc794 Binary files /dev/null and b/13-shuttleSearch/working.jpg differ