Skip to content

Commit 5081590

Browse files
committed
Find the smallest solution for day 17 part 2
It works, phew!
1 parent 52f5624 commit 5081590

File tree

1 file changed

+23
-11
lines changed

1 file changed

+23
-11
lines changed

day17/src/day17.cs

+23-11
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,17 @@
2222
}
2323

2424
{
25-
long part2 = machine.SolveQuine();
26-
Console.WriteLine($"Part 2: {part2}");
25+
long? solution = null;
26+
long minSolution = -1;
27+
28+
while ((solution = machine.SolveQuine(solution)) != null) {
29+
Console.WriteLine($"Found solution: {solution.Value}");
30+
minSolution = solution.Value;
31+
}
32+
33+
Console.WriteLine($"Part 2: {minSolution}");
2734

28-
machine.Registers[0] = part2;
35+
machine.Registers[0] = minSolution;
2936
List<long> output = machine.Run();
3037
Console.WriteLine($"(outputs {string.Join(",", output)})");
3138
}
@@ -113,7 +120,7 @@ private List<long> RunOptimizedInputProgram()
113120
return outputs;
114121
}
115122

116-
public long SolveQuine()
123+
public long? SolveQuine(long? upperBound = null)
117124
{
118125
// For part 2 we use an approach inspired by a Reddit post by deferring the
119126
// heavy-lifting to the Z3 SMT solver:
@@ -135,14 +142,14 @@ public long SolveQuine()
135142
int outputs = 0;
136143
int iterations = 0;
137144

138-
string Int2Bv(int value) => $"((_ int2bv {bits}) {value})";
145+
string Long2Bv(long value) => $"((_ int2bv {bits}) {value})";
139146
string Register(int i, int offset = 0) => $"{registerVars[i]}{registerCounts[i] + offset}";
140147

141148
for (int i = 0; i < Program.Count;)
142149
{
143150
int opcode = Program[i];
144151
int operand = Program[i + 1];
145-
string literal = Int2Bv(operand);
152+
string literal = Long2Bv(operand);
146153
string combo = operand >= 4 && operand < 7 ? Register(operand - 4) : literal;
147154
bool jumped = false;
148155
switch (opcode)
@@ -156,7 +163,7 @@ public long SolveQuine()
156163
registerCounts[1]++;
157164
break;
158165
case 2: // bst (B store?)
159-
smtAssertions.Add($"(assert (= {Register(1, 1)} (bvand {combo} {Int2Bv(0b111)})))");
166+
smtAssertions.Add($"(assert (= {Register(1, 1)} (bvand {combo} {Long2Bv(0b111)})))");
160167
registerCounts[1]++;
161168
break;
162169
case 3: // jnz (jump not zero)
@@ -169,15 +176,15 @@ public long SolveQuine()
169176
else
170177
{
171178
// After the unrolled iterations we want the jump to fail so the loop exits
172-
smtAssertions.Add($"(assert (= {Int2Bv(0)} {Register(0)}))");
179+
smtAssertions.Add($"(assert (= {Long2Bv(0)} {Register(0)}))");
173180
}
174181
break;
175182
case 4: // bxc (B xor C)
176183
smtAssertions.Add($"(assert (= {Register(1, 1)} (bvxor {Register(1)} {Register(2)})))");
177184
registerCounts[1]++;
178185
break;
179186
case 5: // out (output)
180-
smtAssertions.Add($"(assert (= (bvand {combo} {Int2Bv(0b111)}) {Int2Bv(Program[outputs])}))");
187+
smtAssertions.Add($"(assert (= (bvand {combo} {Long2Bv(0b111)}) {Long2Bv(Program[outputs])}))");
181188
outputs++;
182189
break;
183190
case 6: // bdv (B divide)
@@ -195,12 +202,17 @@ public long SolveQuine()
195202
}
196203
}
197204

205+
if (upperBound != null)
206+
{
207+
smtAssertions.Add($"(assert (bvult a0 {Long2Bv(upperBound.Value)}))");
208+
}
209+
198210
var smtDeclarations = registerCounts.Zip(registerVars).SelectMany(p => Enumerable.Range(0, p.First + 1).Select(i => $"(declare-const {p.Second}{i} (_ BitVec {bits}))")).ToList();
199211
var smtTrailer = new List<string> { "(check-sat)", "(get-model)" };
200212
var smtProgram = smtDeclarations.Concat(smtAssertions).Concat(smtTrailer).ToList();
201213

202214
// Uncomment to debug-log the generated SMT-LIB program
203-
Console.WriteLine(string.Join("\n", smtProgram));
215+
// Console.WriteLine(string.Join("\n", smtProgram));
204216

205217
using (var process = new Process())
206218
{
@@ -237,7 +249,7 @@ public long SolveQuine()
237249
}
238250
}
239251
}
240-
throw new Exception("No result found");
252+
return null;
241253
}
242254
}
243255

0 commit comments

Comments
 (0)