From 00000230e21e154aacad555f3daa0e04a766f2f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Jane=C5=BEi=C4=8D?= Date: Wed, 10 Dec 2025 21:01:47 +0100 Subject: [PATCH] feat: add solution 2025/10 --- data/example/2025/10.txt | 3 + requirements.txt | 5 ++ src/solution/year_2025/day_10.py | 98 ++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 data/example/2025/10.txt create mode 100644 src/solution/year_2025/day_10.py diff --git a/data/example/2025/10.txt b/data/example/2025/10.txt new file mode 100644 index 000000000..dd91d7b --- /dev/null +++ b/data/example/2025/10.txt @@ -0,0 +1,3 @@ +[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7} +[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2} +[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5} diff --git a/requirements.txt b/requirements.txt index d2c6cda..d0e3636 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,7 @@ +# cli python-dotenv==1.2.1 requests==2.32.5 + +# solutions +numpy==2.3.5 +scipy==1.16 diff --git a/src/solution/year_2025/day_10.py b/src/solution/year_2025/day_10.py new file mode 100644 index 000000000..d891649 --- /dev/null +++ b/src/solution/year_2025/day_10.py @@ -0,0 +1,98 @@ +import collections +import re +from typing import Any + +import numpy as np +from scipy import optimize + + +def _parse_input(input_data: str): + res: list[tuple[int, list[list[int]], list[int]]] = [] + + for line in input_data.splitlines(): + _state = re.findall(r"\[([.#]*)\]", line)[0] + state = sum(1 << i for i, x in enumerate(_state) if x == "#") + + _buttons = re.findall(r"\(([0-9,]*)\)", line) + buttons = [[int(n) for n in m.split(",")] for m in _buttons] + + _joltage = re.findall(r" {([0-9,]*)}$", line)[0].strip() + joltage = list(map(int, _joltage.split(","))) + + res.append((state, buttons, joltage)) + + return res + + +def _bfs(end: int, neighs: list[int]) -> int: + start = 0 + + dq = collections.deque([(0, start)]) + visited = {0} + + while dq: + d, p = dq.popleft() + + if p == end: + return d + + for n in neighs: + np = p ^ n + + if np in visited: + continue + + visited.add(np) + dq.append((d + 1, np)) + + raise ValueError(f"unable to calibrate {end}") + + +def _milp(vecs: list[list[int]], goal: list[int]) -> int: + n = len(goal) + m = len(vecs) + + a = np.zeros((n, m), dtype=int) + + for j, vec in enumerate(vecs): + for i in vec: + a[i][j] = 1 + + c = np.ones(m) + b = np.array(goal) + + constraints = optimize.LinearConstraint(a, b, b) # type: ignore + bounds = optimize.Bounds(0, np.inf) + integrality = np.ones(m) + + result = optimize.milp( + c, + constraints=constraints, + bounds=bounds, + integrality=integrality, + ) + + return int(result.fun) + + +def part_1(input_data: str) -> Any: + parsed = _parse_input(input_data) + + s = 0 + for end, switches, _ in parsed: + neighs = [sum(1 << n for n in m) for m in switches] + s += _bfs(end, neighs) + + return s + + +def part_2(input_data: str) -> Any: + return sum(_milp(vecs, goal) for _, vecs, goal in _parse_input(input_data)) + + +def test_part_1(example_data): + assert part_1(example_data) == 7 + + +def test_part_2(example_data): + assert part_2(example_data) == 33