mirror of
https://github.com/parnic/advent-of-code-2024.git
synced 2025-06-16 12:30:13 -05:00
Day 24
I...again don't fully understand what I've done here. After reading the problem and printing out the binary representation of x + y and z, I looked at the bits that weren't correct and started trying to reason about why that might be. Manually swapping around outputs started to yield some results, so I hacked around until I got something that worked and submitted that answer. Once I had it by hand, I went searching for help on Reddit for how to solve this programmatically, and found a lot of chatter about half-adders and junk, so this is implementing some posts I saw there to search for the incorrect sequences and rewire them.
This commit is contained in:
209
src/24.cs
Normal file
209
src/24.cs
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
namespace aoc2024;
|
||||||
|
|
||||||
|
internal class Day24 : Day
|
||||||
|
{
|
||||||
|
class op(string gate1, string gate2, string outGate, string operation)
|
||||||
|
{
|
||||||
|
public string Gate1 = gate1;
|
||||||
|
public string Gate2 = gate2;
|
||||||
|
public string OutGate = outGate;
|
||||||
|
public string Operation = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, int> gates = [];
|
||||||
|
private List<op> operations = [];
|
||||||
|
|
||||||
|
internal override void Parse()
|
||||||
|
{
|
||||||
|
var lines = Util.Parsing.ReadAllLines($"{GetDay()}").ToList();
|
||||||
|
var mode = 0;
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
if (line.Length == 0)
|
||||||
|
{
|
||||||
|
mode++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
var split = line.Split(": ", StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
gates.Add(split[0], int.Parse(split[1]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
var split = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var o = new op(split[0], split[2], split[4], split[1]);
|
||||||
|
operations.Add(o);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long GetResultOf(char c, Dictionary<string, int> gs)
|
||||||
|
{
|
||||||
|
long result = 0;
|
||||||
|
var ordered = gs.Where(g => g.Key.StartsWith(c)).OrderBy(g => g.Key).ToList();
|
||||||
|
for (int i = 0; i < ordered.Count; i++)
|
||||||
|
{
|
||||||
|
result += (long)gs[$"{c}{i:00}"] << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override string Part1()
|
||||||
|
{
|
||||||
|
var gatesCopy = Run(gates, operations);
|
||||||
|
long result = GetResultOf('z', gatesCopy);
|
||||||
|
return $"Computed number: <+white>{result}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, int> Run(Dictionary<string, int> gs, List<op> ops)
|
||||||
|
{
|
||||||
|
var gatesCopy = gs.ToDictionary();
|
||||||
|
var q = new Queue<op>();
|
||||||
|
foreach (var inst in ops)
|
||||||
|
{
|
||||||
|
q.Enqueue(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (q.TryDequeue(out var inst))
|
||||||
|
{
|
||||||
|
if (!gatesCopy.ContainsKey(inst.Gate1) || !gatesCopy.ContainsKey(inst.Gate2))
|
||||||
|
{
|
||||||
|
q.Enqueue(inst);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
gatesCopy.TryAdd(inst.OutGate, 0);
|
||||||
|
|
||||||
|
switch (inst.Operation)
|
||||||
|
{
|
||||||
|
case "AND":
|
||||||
|
gatesCopy[inst.OutGate] = gatesCopy[inst.Gate1] == 1 && gatesCopy[inst.Gate2] == 1 ? 1 : 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "XOR":
|
||||||
|
gatesCopy[inst.OutGate] = gatesCopy[inst.Gate1] != gatesCopy[inst.Gate2] ? 1 : 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "OR":
|
||||||
|
gatesCopy[inst.OutGate] = gatesCopy[inst.Gate1] == 0 && gatesCopy[inst.Gate2] == 0 ? 0 : 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gatesCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> GetWrongBits(Dictionary<string, int> gs)
|
||||||
|
{
|
||||||
|
List<string> wrongBits = [];
|
||||||
|
|
||||||
|
var x = GetResultOf('x', gs);
|
||||||
|
var y = GetResultOf('y', gs);
|
||||||
|
var actualVal = GetResultOf('z', gs);
|
||||||
|
var desiredVal = x + y;
|
||||||
|
// Logger.LogLine(Convert.ToString(actualVal, 2));
|
||||||
|
// Logger.LogLine(Convert.ToString(desiredVal, 2));
|
||||||
|
for (int i = 0; i < gs.Count(g => g.Key.StartsWith('z')); i++)
|
||||||
|
{
|
||||||
|
if ((desiredVal >> i) % 2 != (actualVal >> i) % 2)
|
||||||
|
{
|
||||||
|
wrongBits.Add($"z{i:00}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrongBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Find(string gate1, string gate2, string operation, List<op> operations)
|
||||||
|
{
|
||||||
|
var test = operations.Where(o => o.Gate1 == gate1 && o.Gate2 == gate2 && o.Operation == operation)
|
||||||
|
.Select(o => o.OutGate).FirstOrDefault("");
|
||||||
|
if (!string.IsNullOrEmpty(test))
|
||||||
|
{
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
test = operations.Where(o => o.Gate1 == gate2 && o.Gate2 == gate1 && o.Operation == operation)
|
||||||
|
.Select(o => o.OutGate).FirstOrDefault("");
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FixupOutputs(List<op> operations)
|
||||||
|
{
|
||||||
|
var highestZ = int.Parse(operations.Select(o => o.OutGate).OrderDescending().First()[1..]);
|
||||||
|
List<string> swapped = [];
|
||||||
|
string lastXyAnd = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < highestZ; i++)
|
||||||
|
{
|
||||||
|
var n = $"{i:00}";
|
||||||
|
string newXyXorOut = "";
|
||||||
|
string xyOrOut = "";
|
||||||
|
|
||||||
|
string xyXorOut = Find($"x{n}", $"y{n}", "XOR", operations);
|
||||||
|
string xyAndOut = Find($"x{n}", $"y{n}", "AND", operations);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(lastXyAnd))
|
||||||
|
{
|
||||||
|
var newXyAndOut = Find(lastXyAnd, xyXorOut, "AND", operations);
|
||||||
|
if (string.IsNullOrEmpty(newXyAndOut))
|
||||||
|
{
|
||||||
|
(xyXorOut, xyAndOut) = (xyAndOut, xyXorOut);
|
||||||
|
swapped.AddRange([xyXorOut, xyAndOut]);
|
||||||
|
newXyAndOut = Find(lastXyAnd, xyXorOut, "AND", operations);
|
||||||
|
}
|
||||||
|
|
||||||
|
newXyXorOut = Find(lastXyAnd, xyXorOut, "XOR", operations);
|
||||||
|
if (xyXorOut.StartsWith('z'))
|
||||||
|
{
|
||||||
|
(xyXorOut, newXyXorOut) = (newXyXorOut, xyXorOut);
|
||||||
|
swapped.AddRange([xyXorOut, newXyXorOut]);
|
||||||
|
}
|
||||||
|
if (xyAndOut.StartsWith('z'))
|
||||||
|
{
|
||||||
|
(xyAndOut, newXyXorOut) = (newXyXorOut, xyAndOut);
|
||||||
|
swapped.AddRange([xyAndOut, newXyXorOut]);
|
||||||
|
}
|
||||||
|
if (newXyAndOut.StartsWith('z'))
|
||||||
|
{
|
||||||
|
(newXyAndOut, newXyXorOut) = (newXyXorOut, newXyAndOut);
|
||||||
|
swapped.AddRange([newXyAndOut, newXyXorOut]);
|
||||||
|
}
|
||||||
|
|
||||||
|
xyOrOut = Find(newXyAndOut, xyAndOut, "OR", operations);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xyOrOut.StartsWith('z') && xyOrOut != $"z{highestZ}")
|
||||||
|
{
|
||||||
|
(xyOrOut, newXyXorOut) = (newXyXorOut, xyOrOut);
|
||||||
|
swapped.AddRange([xyOrOut, newXyXorOut]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(lastXyAnd))
|
||||||
|
{
|
||||||
|
lastXyAnd = xyAndOut;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastXyAnd = xyOrOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
swapped.Sort();
|
||||||
|
return string.Join(',', swapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override string Part2()
|
||||||
|
{
|
||||||
|
return $"Incorrect outputs: <+white>{FixupOutputs(operations)}";
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user