You and The Historians arrive at the edge of a large grove somewhere in the jungle. After the last incident, the Elves installed a small device that monitors the fruit. While The Historians search the grove, one of them asks if you can take a look at the monitoring device; apparently, it's been malfunctioning recently.
The device seems to be trying to produce a number through some boolean logic gates. Each gate has two inputs and one output. The gates all operate on values that are either true (1
) or false (0
).
Visit the website for the full story and full puzzle description.
The first half of the problem was a familiar logic circuit evaluator, we have done this before. I really liked the second part, although I don't have a super generic solution for it. I generated a graph from my input using Graphviz, then realized that the circuit tries to implement a Ripple-carry adder.
A single full adder consists of two half adders:
Which are chained one after the other like this to get a Ripple-carry adder:
I took the images from build-electronic-circuits.com.
I implemented this logic in my 'fix' method. I start with the inputs x01 and y01 and try to identify the gates for the individual steps. Where I found an error, I manually checked my input to figure out what went wrong. I had just two different kind of errors which can be corrected by the fixer.
namespace AdventOfCode.Y2024.Day24;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Circuit = System.Collections.Generic.Dictionary<string, Gate>;
record struct Gate(string in1, string kind, string in2);
[ProblemName("Crossed Wires")]
class Solution : Solver {
public object PartOne(string input) {
var (inputs, circuit) = Parse(input);
var outputs = from label in circuit.Keys where label.StartsWith("z") select label;
var res = 0L;
foreach (var label in outputs.OrderByDescending(label=>label)) {
res = res * 2 + Eval(label, circuit, inputs);
}
return res;
}
int Eval(string label, Circuit circuit, Dictionary<string, int> inputs) {
if (inputs.TryGetValue(label, out var res)) {
return res;
} else {
return circuit[label] switch {
Gate(var in1, "AND", var in2) => Eval(in1, circuit, inputs) & Eval(in2, circuit, inputs),
Gate(var in1, "OR", var in2) => Eval(in1, circuit, inputs) | Eval(in2, circuit, inputs),
Gate(var in1, "XOR", var in2) => Eval(in1, circuit, inputs) ^ Eval(in2, circuit, inputs),
_ => throw new Exception(circuit[label].ToString()),
};
}
}
public object PartTwo(string input) {
var circuit = Parse(input).circuit;
return string.Join(",", Fix(circuit).OrderBy(label => label));
}
// the circuit should define a full adder for two 44 bit numbers
// this fixer is specific to my input.
IEnumerable<string> Fix(Circuit circuit) {
var cin = Output(circuit, "x00", "AND", "y00");
for (var i = 1; i < 45; i++) {
var x = $"x{i:D2}";
var y = $"y{i:D2}";
var z = $"z{i:D2}";
var xor1 = Output(circuit, x, "XOR", y);
var and1 = Output(circuit, x, "AND", y);
var xor2 = Output(circuit, cin, "XOR", xor1);
var and2 = Output(circuit, cin, "AND", xor1);
if (xor2 == null && and2 == null) {
return SwapAndFix(circuit, xor1, and1);
}
var carry = Output(circuit, and1, "OR", and2);
if (xor2 != z) {
return SwapAndFix(circuit, z, xor2);
} else {
cin = carry;
}
}
return [];
}
IEnumerable<string> SwapAndFix(Circuit circuit, string out1, string out2) {
(circuit[out1], circuit[out2]) = (circuit[out2], circuit[out1]);
return Fix(circuit).Concat([out1, out2]);
}
string Output(Circuit circuit, string x, string kind, string y) =>
circuit.SingleOrDefault(pair =>
(pair.Value.in1 == x && pair.Value.kind == kind && pair.Value.in2 == y) ||
(pair.Value.in1 == y && pair.Value.kind == kind && pair.Value.in2 == x)
).Key;
(Dictionary<string, int> inputs, Circuit circuit) Parse(string input) {
var inputs = new Dictionary<string, int>();
var circuit = new Circuit();
var blocks = input.Split("\n\n");
foreach (var line in blocks[0].Split("\n")) {
var parts = line.Split(": ");
inputs.Add(parts[0], int.Parse(parts[1]));
}
foreach (var line in blocks[1].Split("\n")) {
var parts = Regex.Matches(line, "[a-zA-z0-9]+").Select(m => m.Value).ToArray();
circuit.Add(parts[3], new Gate(in1: parts[0], kind: parts[1], in2: parts[2]));
}
return (inputs, circuit);
}
}
Please ☆ my repo if you like it!