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