diff --git a/2024/src/aoc/days/day22.py b/2024/src/aoc/days/day22.py new file mode 100644 index 0000000..71ca375 --- /dev/null +++ b/2024/src/aoc/days/day22.py @@ -0,0 +1,54 @@ +from collections import defaultdict + +import numpy +from numpy.lib.stride_tricks import sliding_window_view + +from . import SeparateRunner + + +def advance(secrets: numpy.array) -> numpy.array: + new_secrets = (secrets ^ (secrets << 6)) & 0xFFFFFF + new_secrets ^= new_secrets >> 5 + new_secrets ^= new_secrets << 11 + new_secrets &= 0xFFFFFF + + return new_secrets + + +class DayRunner(SeparateRunner): + @classmethod + def part1(cls, input: str) -> int: + secrets = numpy.fromstring(input, dtype=int, sep="\n") + + for _ in range(2000): + secrets = advance(secrets) + + return secrets.sum() + + @classmethod + def part2(cls, input: str) -> int: + secrets = numpy.fromstring(input, dtype=int, sep="\n") + + progression = [secrets] + + for _ in range(2000): + secrets = advance(secrets) + progression.append(secrets) + + field = numpy.stack(progression, axis=-1) % 10 + delta = field[:, 1:] - field[:, :-1] + + per_signal = defaultdict(int) + + for row_scores, row_deltas in zip(field, delta): + seen = set() + + for window, price in zip( + sliding_window_view(row_deltas, 4), row_scores[4:] + ): + key = tuple(window) + if key not in seen: + seen.add(key) + per_signal[key] += price + + return max(per_signal.values()) diff --git a/2024/tests/samples/22.1.txt b/2024/tests/samples/22.1.txt new file mode 100644 index 0000000..07b41ba --- /dev/null +++ b/2024/tests/samples/22.1.txt @@ -0,0 +1,4 @@ +1 +10 +100 +2024 diff --git a/2024/tests/samples/22.2.txt b/2024/tests/samples/22.2.txt new file mode 100644 index 0000000..201df76 --- /dev/null +++ b/2024/tests/samples/22.2.txt @@ -0,0 +1,4 @@ +1 +2 +3 +2024 diff --git a/2024/tests/test_day22.py b/2024/tests/test_day22.py new file mode 100644 index 0000000..2209d2b --- /dev/null +++ b/2024/tests/test_day22.py @@ -0,0 +1,11 @@ +from aoc.days.day22 import DayRunner + +from . import get_data + + +def test_sample_part1() -> None: + assert DayRunner.part1(get_data(22, 1)) == 37327623 + + +def test_sample_part2() -> None: + assert DayRunner.part2(get_data(22, 2)) == 23