From ceabdf384d94dcc734a95a94c64dc13d72934e68 Mon Sep 17 00:00:00 2001 From: Nettika Date: Fri, 6 Dec 2024 23:08:08 -0800 Subject: [PATCH] Implement day 7 solver --- puzzles/4.py | 1 - puzzles/5.py | 1 - puzzles/6.py | 1 - puzzles/7.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 puzzles/7.py diff --git a/puzzles/4.py b/puzzles/4.py index aa30dec..97baf6d 100644 --- a/puzzles/4.py +++ b/puzzles/4.py @@ -1,6 +1,5 @@ from typing import Iterator - test_input = """ MMMSXXMASM MSAMXMSMSA diff --git a/puzzles/5.py b/puzzles/5.py index ba5acd7..57be724 100644 --- a/puzzles/5.py +++ b/puzzles/5.py @@ -1,6 +1,5 @@ from typing import Iterator - test_input = """ 47|53 97|13 diff --git a/puzzles/6.py b/puzzles/6.py index d0a9f97..8775a06 100644 --- a/puzzles/6.py +++ b/puzzles/6.py @@ -1,6 +1,5 @@ from typing import NamedTuple - test_input = """ ....#..... .........# diff --git a/puzzles/7.py b/puzzles/7.py new file mode 100644 index 0000000..be98f11 --- /dev/null +++ b/puzzles/7.py @@ -0,0 +1,84 @@ +from functools import partial +from typing import Callable, Iterator, NamedTuple +from math import log + + +test_input = """ +190: 10 19 +3267: 81 40 27 +83: 17 5 +156: 15 6 +7290: 6 8 6 15 +161011: 16 10 13 +192: 17 8 14 +21037: 9 7 18 13 +292: 11 6 16 20 +""".strip() + +test_solution_p1 = 3749 +test_solution_p2 = 11387 + + +def solve_p1(puzzle_input: str) -> int: + equations = _parse_equations(puzzle_input) + calibration_guard = partial( + _is_calibrated, + [ + int.__add__, + int.__mul__, + ], + ) + return sum(equation.target for equation in filter(calibration_guard, equations)) + + +def solve_p2(puzzle_input: str) -> int: + equations = _parse_equations(puzzle_input) + calibration_guard = partial( + _is_calibrated, + [ + int.__add__, + int.__mul__, + _concat_ints, + ], + ) + return sum(equation.target for equation in filter(calibration_guard, equations)) + + +class Equation(NamedTuple): + target: int + factors: tuple[int, ...] + + +def _parse_equations(puzzle_input: str) -> Iterator[Equation]: + for line in puzzle_input.split("\n"): + if line: + result_string, _, factors_string = line.partition(": ") + yield Equation( + int(result_string), + tuple(map(int, factors_string.split(" "))), + ) + + +Operator = Callable[[int, int], int] + + +def _is_calibrated(operators: list[Operator], equation: Equation) -> bool: + def _eval_permutations(factors: tuple[int, ...]) -> Iterator[int]: + assert len(factors) > 0 + tail = factors[-1] + if len(factors) == 1: + yield tail + return + for head in _eval_permutations(factors[:-1]): + if head > equation.target: + continue + for operator in operators: + yield operator(head, tail) + + return any( + result == equation.target for result in _eval_permutations(equation.factors) + ) + + +def _concat_ints(a: int, b: int) -> int: + return 10 ** int(log(b, 10) + 1) * a + b