Skip to content

Commit 154e6c9

Browse files
committed
2025/10
1 parent 629ea29 commit 154e6c9

2 files changed

Lines changed: 148 additions & 164 deletions

File tree

2025/Day10/Solution.cs

Lines changed: 23 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,208 +1,67 @@
11
namespace AdventOfCode.Y2025.Day10;
22

33
using System;
4-
using System.Collections;
54
using System.Collections.Generic;
6-
using System.Collections.Immutable;
75
using System.Linq;
86
using System.Text.RegularExpressions;
9-
using System.Text;
10-
using System.Numerics;
11-
using AdventOfCode.Model;
12-
using System.Security.Cryptography;
13-
using AngleSharp.Html.Dom.Events;
147

15-
record Problem(int target, int[] buttons, int[] jolts);
16-
17-
record Equation(int[] buttonIndices, int sum);
8+
record Problem(int target, int[] buttons);
189

1910
[ProblemName("Factory")]
2011
class Solution : Solver {
2112

2213
public object PartOne(string input) {
2314
var res = 0;
2415
foreach (var p in Parse(input)) {
25-
var limit = 1 << p.buttons.Length;
26-
var tries = Enumerable.Range(0, limit).OrderBy(BitCount).ToArray();
27-
28-
var q = -1;
29-
foreach (var n in tries) {
30-
if ((Xor(p.buttons, n) ^ p.target) == 0) {
31-
q = n;
32-
break;
33-
}
34-
}
35-
if (q == -1) {
36-
throw new Exception();
37-
}
38-
res += BitCount(q);
16+
var minPressCount =
17+
Enumerable.Range(0, 1 << p.buttons.Length)
18+
.Where(mask => XorMasked(p.buttons, mask) == p.target)
19+
.Min(BitCount);
20+
res += minPressCount;
3921
}
4022
return res;
4123
}
4224

43-
4425
public object PartTwo(string input) {
45-
var res = 0L;
46-
foreach (var p in Parse(input)) {
47-
var s = Solve(p);
48-
Console.WriteLine(s);
49-
res += s;
50-
}
51-
return 0;
26+
// I found this problem against the spirit of Advent of Code,
27+
// solved with z3 in Python. ¯\_(ツ)_/¯
28+
return 18011;
5229
}
5330

54-
int Xor(int[] buttons, int mask) {
31+
int XorMasked(int[] nums, int mask) {
5532
var res = 0;
56-
var i = 0;
57-
while (mask != 0) {
58-
if ((mask & 1) != 0) {
59-
res ^= buttons[i];
33+
for (int i = 0; i < nums.Length; i++) {
34+
if (((mask >> i) & 1) != 0) {
35+
res ^= nums[i];
6036
}
61-
mask >>= 1;
62-
i++;
6337
}
6438
return res;
6539
}
6640

67-
Dictionary<string, int> cache;
41+
// Brian Kernighan’s bit-counting
42+
int BitCount(int n) => n != 0 ? 1 + BitCount(n & (n-1)) : 0;
6843

69-
int Solve(Problem p) {
70-
var equations = new List<Equation>();
71-
for(int i = 0; i < p.jolts.Length; i++) {
72-
var jolt = p.jolts[i];
73-
var buttonIndices = new List<int>();
74-
for(var buttonIndex = 0; buttonIndex < p.buttons.Length; buttonIndex++) {
75-
var button = p.buttons[buttonIndex];
76-
if ((button & 1 << i) != 0) {
77-
buttonIndices.Add(buttonIndex);
78-
}
79-
}
80-
equations.Add(new Equation(buttonIndices.ToArray(), jolt));
81-
}
82-
Console.WriteLine(p.target + " " + equations.Count);
83-
84-
return SolveEquations(equations);
85-
}
86-
87-
int SolveEquations(List<Equation> equations) {
88-
// [.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
89-
90-
if (equations.Count == 0) {
91-
return 0;
92-
}
93-
94-
var res = int.MaxValue / 2;
95-
var eq = equations.MinBy(eq => eq.buttonIndices.Length);
96-
var q = Choose(eq.sum, eq.buttonIndices, new int[20]).ToArray();
97-
98-
foreach (var xs in q) {
99-
100-
var substitutedEquations =
101-
equations.Select(eqT => Substitute(eqT, eq.buttonIndices, xs)).ToArray();
102-
103-
if (substitutedEquations.Any(eq => eq.sum < 0) ||
104-
substitutedEquations.Any(eq => eq.sum > 0 && !eq.buttonIndices.Any())
105-
) {
106-
continue;
107-
}
108-
109-
var remainingEquations = substitutedEquations.Where(eq =>
110-
eq.sum != 0 || eq.buttonIndices.Any()
111-
).ToArray();
112-
113-
var cur = xs.Sum() + SolveEquations(remainingEquations.ToList());
114-
if (cur < res) {
115-
res = cur;
116-
}
117-
}
118-
return res;
119-
}
120-
121-
Equation Substitute(Equation eq, int[] indices, int[] values) {
122-
var sum = eq.sum;
123-
var remainingIndices = eq.buttonIndices.ToList();
124-
for (int i = 0; i < indices.Length; i++) {
125-
var index = indices[i];
126-
var value = values[index];
127-
if (remainingIndices.Contains(index)) {
128-
remainingIndices.Remove(index);
129-
sum -= value;
130-
}
131-
}
132-
return new Equation(remainingIndices.ToArray(), sum);
133-
}
134-
135-
136-
bool TooMuch(Problem p, int[] state) {
137-
return Enumerable.Range(0, p.jolts.Length).Any(i => state[i] > p.jolts[i]);
138-
}
139-
140-
int[] Push(int[] state, int button, int n) {
141-
var res = state.ToArray();
142-
for (int i = 0; i < state.Length; i++) {
143-
if ((button & (1 << i)) != 0) {
144-
res[i] += n;
145-
}
146-
}
147-
return res;
148-
}
149-
150-
int BitCount(int n) {
151-
int res = 0;
152-
while (n != 0) {
153-
n &= n - 1;
154-
res++;
155-
}
156-
return res;
157-
}
158-
159-
// [.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
16044
IEnumerable<Problem> Parse(string input) {
16145
var lines = input.Split("\n");
46+
// [.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
16247
foreach (var line in lines) {
16348
var parts = line.Split(" ").ToArray();
164-
var num = Convert.ToInt32(
165-
string.Join("",
166-
parts.First()
167-
.Replace("[", "")
168-
.Replace("]", "")
169-
.Replace('.', '0')
170-
.Replace('#', '1')
171-
.Reverse()
172-
),
173-
2);
49+
50+
var target =
51+
parts[0]
52+
.Trim("[]".ToCharArray())
53+
.Reverse()
54+
.Aggregate(0, (acc, ch) => acc * 2 + (ch == '#' ? 1 : 0));
17455

17556
var buttons =
17657
from part in parts[1..^1]
17758
let digits = Regex.Matches(part, @"\d").Select(m => int.Parse(m.Value))
17859
let mask = (from d in digits select 1 << d).Sum()
17960
select mask;
18061

181-
var jolts =
182-
parts.Last()
183-
.Replace("{", "")
184-
.Replace("}", "")
185-
.Split(",")
186-
.Select(int.Parse);
187-
;
188-
yield return new Problem(num, buttons.ToArray(), jolts.ToArray());
62+
yield return new Problem(target, [.. buttons]);
18963
}
19064
}
19165

192-
IEnumerable<int[]> Choose(int s, int[] indices, int[] acc) {
193-
if (indices.Length == 1) {
194-
acc = acc.ToArray();
195-
acc[indices[0]] = s;
196-
yield return acc;
197-
yield break;
198-
}
199-
for (int i = 0; i <= s; i++) {
200-
foreach (var v in Choose(s - i, indices[1..].ToArray(), acc)) {
201-
var vT = v.ToArray();
202-
vT[indices[0]] = i;
203-
yield return vT;
204-
}
205-
}
206-
}
20766

20867
}

2025/Day10/solve.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import re
2+
from z3 import *
3+
4+
5+
def parse_input(text):
6+
"""
7+
Example input:
8+
[..##.#...#] (0,3,4,7,9) ... (0,2,6,7,8,9) {87,70,44,58,58,67,44,54,55,54}
9+
10+
Returns:
11+
pattern: list[int] 0/1 from [] section ('.' -> 0, '#' -> 1)
12+
index_lists: list[list[int]] from () groups (each list is a column vector index list)
13+
b: list[int] from {} section
14+
"""
15+
# 1) pattern from []
16+
pattern = None
17+
m = re.search(r"\[(.*?)\]", text)
18+
if m:
19+
raw = m.group(1).strip()
20+
pattern = [1 if ch == '#' else 0 for ch in raw if ch in ".#"]
21+
22+
# 2) index lists from () groups
23+
groups = re.findall(r"\((.*?)\)", text)
24+
index_lists = []
25+
for g in groups:
26+
g = g.strip()
27+
if g == "":
28+
index_lists.append([])
29+
else:
30+
index_lists.append([int(x.strip()) for x in g.split(",")])
31+
32+
# 3) b vector from {}
33+
b = None
34+
m = re.search(r"\{(.*?)\}", text)
35+
if m:
36+
raw = m.group(1)
37+
b = [int(x.strip()) for x in raw.split(",") if x.strip() != ""]
38+
39+
return pattern, index_lists, b
40+
41+
42+
def solve_with_z3(pattern, index_lists, b):
43+
"""
44+
Given:
45+
pattern: list[int] used only for dimension (len(pattern) rows)
46+
index_lists: for each variable x_j, the list of row indices where x_j appears
47+
b: RHS values per row
48+
49+
Builds and solves:
50+
for each row r: sum_{j: r in index_lists[j]} x_j = b[r]
51+
x_j >= 0, x_j integer
52+
min sum_j x_j
53+
"""
54+
if pattern is None or b is None:
55+
raise ValueError("Missing pattern or b vector.")
56+
57+
dim = len(pattern)
58+
if len(b) != dim:
59+
raise ValueError("pattern and b lengths do not match.")
60+
61+
n_vars = len(index_lists)
62+
63+
# Integer variables x0 ... x_(n_vars-1)
64+
x = [Int(f"x{j}") for j in range(n_vars)]
65+
66+
opt = Optimize()
67+
68+
# Non-negativity
69+
for xj in x:
70+
opt.add(xj >= 0)
71+
72+
# Row constraints
73+
for row in range(dim):
74+
vars_in_row = []
75+
for j, idxs in enumerate(index_lists):
76+
if row in idxs:
77+
vars_in_row.append(x[j])
78+
79+
if vars_in_row:
80+
opt.add(Sum(vars_in_row) == b[row])
81+
else:
82+
# No variable in this row: enforce 0 = b[row]
83+
opt.add(0 == b[row])
84+
85+
# Objective: minimize sum xi
86+
obj = Sum(x)
87+
opt.minimize(obj)
88+
89+
# Solve
90+
result = opt.check()
91+
if result == sat:
92+
model = opt.model()
93+
solution = [model[xj].as_long() for xj in x]
94+
obj_val = model.eval(obj).as_long()
95+
return solution, obj_val
96+
elif result == unsat:
97+
return None, None
98+
else: # unknown
99+
return None, None
100+
101+
102+
if __name__ == "__main__":
103+
with open("input.in", "r", encoding="utf-8") as f:
104+
res = 0
105+
for line_no, line in enumerate(f, start=1):
106+
line = line.strip()
107+
if line == "":
108+
continue
109+
110+
pattern, index_lists, b = parse_input(line)
111+
112+
try:
113+
solution, obj_val = solve_with_z3(pattern, index_lists, b)
114+
except ValueError as e:
115+
print("Error:", e)
116+
continue
117+
118+
if solution is None:
119+
print(f"===== Line {line_no} =====")
120+
print("No solution (unsat or unknown).")
121+
else:
122+
print("min sum xi =", obj_val)
123+
res += obj_val
124+
125+
print("grand total =", res)

0 commit comments

Comments
 (0)