Add day 24

This commit is contained in:
2025-01-24 10:19:13 +01:00
parent bc79ea9fa6
commit d647742ca0
2 changed files with 683 additions and 0 deletions

370
Days/Day24.cs Normal file
View File

@@ -0,0 +1,370 @@
using System.Collections.Frozen;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics.X86;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.VisualBasic;
using Spectre.Console;
namespace AdventOfCode.Days;
public class Day24 : Day
{
public override int Number => 24;
public override string Name => "Crossed Wires";
public override void RunPart1(bool display = true)
{
var (signals, gates, zGates) = ParseInput();
while (zGates.Count > 0)
{
foreach (var (inputLeft, inputRight, output, logicOperator) in gates)
{
if (!signals.TryGetValue(inputLeft, out var inputLeftValue) ||
!signals.TryGetValue(inputRight, out var inputRightValue))
{
continue;
}
signals[output] = Compute(inputLeftValue, inputRightValue, logicOperator);
if (output.StartsWith('z'))
{
zGates.Remove(output);
}
}
}
var zOutputValue = signals
.Where(s => s.Key.StartsWith('z'))
.Select(s => new { Index = int.Parse(s.Key[1..]), Value = (long)s.Value })
.Sum(s => s.Value << s.Index);
if (display)
{
AnsiConsole.MarkupLine($"[green]Value output on z signal: [yellow]{zOutputValue}[/][/]");
}
}
public override void RunPart2(bool display = true)
{
var (initialSignals, initialGates, initialZGates) = ParseInput();
var xInputValue = initialSignals
.Where(s => s.Key.StartsWith('x'))
.Select(s => new { Index = int.Parse(s.Key[1..]), Value = (long)s.Value })
.Sum(s => s.Value << s.Index);
var yInputValue = initialSignals
.Where(s => s.Key.StartsWith('y'))
.Select(s => new { Index = int.Parse(s.Key[1..]), Value = (long)s.Value })
.Sum(s => s.Value << s.Index);
// Find wires that need to be changed
const long initialZValue = 58740594706150L;
var targetZValue = xInputValue + yInputValue;
var difference = targetZValue ^ initialZValue;
var possibleChange = new HashSet<string>();
for (var i = 0; i < sizeof(long) * 8; i++)
{
if (((1 << i) & difference) != 0)
{
possibleChange.Add($"z{i:D2}");
}
}
// Find all dependencies
int initialSize;
do
{
initialSize = possibleChange.Count;
foreach (var gate in initialGates)
{
if (possibleChange.Contains(gate.Output))
{
possibleChange.Add(gate.InputLeft);
possibleChange.Add(gate.InputRight);
}
}
} while (possibleChange.Count != initialSize);
var gatesToSwapIndices = initialGates
.Index()
.ToList()
.Where(t => possibleChange.Contains(t.Item.Output))
.Select(t => t.Index)
.ToList();
// Try out all possible permutations of concerned outputs
List<string> changedOutputs = [];
for (var a = 0; a < gatesToSwapIndices.Count - 7; a++)
{
for (var b = a + 1; b < gatesToSwapIndices.Count - 6; b++)
{
for (var c = b + 1; c < gatesToSwapIndices.Count - 5; c++)
{
for (var d = c + 1; d < gatesToSwapIndices.Count - 4; d++)
{
for (var e = d + 1; e < gatesToSwapIndices.Count - 3; e++)
{
for (var f = e + 1; f < gatesToSwapIndices.Count - 2; f++)
{
for (var g = f + 1; g < gatesToSwapIndices.Count - 1; g++)
{
for (var h = g + 1; h < gatesToSwapIndices.Count; h++)
{
Console.WriteLine($"h: {h}");
ForAllPermutation([
gatesToSwapIndices[a],
gatesToSwapIndices[b],
gatesToSwapIndices[c],
gatesToSwapIndices[d],
gatesToSwapIndices[e],
gatesToSwapIndices[f],
gatesToSwapIndices[g],
gatesToSwapIndices[h]], permutations =>
{
if (CheckPermutations(permutations) is { } modifiedOutputs)
{
changedOutputs = modifiedOutputs;
return true;
}
return false;
});
}
}
}
}
}
}
}
}
var swappedOutputs = string.Join(',', changedOutputs.Order());
if (display)
{
AnsiConsole.MarkupLine($"[green]Swapped outputs: [yellow]{swappedOutputs}[/][/]");
}
return;
List<string>? CheckPermutations(int[] permutations)
{
var gates = initialGates.ToArray();
var zGates = initialZGates.ToHashSet();
var signals = initialSignals.ToDictionary();
SwapOutput(ref gates[permutations[0]], ref gates[permutations[1]]);
SwapOutput(ref gates[permutations[2]], ref gates[permutations[3]]);
SwapOutput(ref gates[permutations[4]], ref gates[permutations[5]]);
SwapOutput(ref gates[permutations[6]], ref gates[permutations[7]]);
var zOutputValue = 0L;
while (zGates.Count > 0)
{
// Avoid locks when result is not reachable (infinite loop)
var resultsCount = signals.Count;
foreach (var (inputLeft, inputRight, output, logicOperator) in gates)
{
if (signals.ContainsKey(output))
{
continue;
}
if (!signals.TryGetValue(inputLeft, out var inputLeftValue) ||
!signals.TryGetValue(inputRight, out var inputRightValue))
{
continue;
}
var computed = Compute(inputLeftValue, inputRightValue, logicOperator);
signals[output] = computed;
if (output.StartsWith('z'))
{
zGates.Remove(output);
zOutputValue += (long)computed << int.Parse(output[1..]);
}
}
// No new signal value computed, it's an infinite loop
if (signals.Count == resultsCount)
{
return null;
}
}
if (xInputValue + yInputValue == zOutputValue)
{
return permutations.Select(i => gates[i].Output).ToList();
}
return null;
}
void SwapOutput(ref LogicGate first, ref LogicGate second)
{
var temp = first.Output;
first = first with { Output = second.Output };
second = second with { Output = temp };
}
}
private int Compute(int inputLeftValue, int inputRightValue, Operator logicOperator) => logicOperator switch
{
Operator.And => inputLeftValue & inputRightValue,
Operator.Or => inputLeftValue | inputRightValue,
Operator.Xor => inputLeftValue ^ inputRightValue,
_ => throw new ArgumentOutOfRangeException(nameof(logicOperator), logicOperator, null)
};
private (Dictionary<string, int> Signals, List<LogicGate> Gates, HashSet<string> ZGates) ParseInput()
{
var readingSignals = true;
var signals = new Dictionary<string, int>();
var gates = new List<LogicGate>();
var zGates = new HashSet<string>();
foreach (var line in Input.AsSpan().EnumerateLines())
{
if (line.IsWhiteSpace())
{
readingSignals = false;
continue;
}
if (readingSignals)
{
var signalName = line[..line.IndexOf(':')];
var signalValue = int.Parse(line[(line.IndexOf(':') + 2)..]);
signals[signalName.ToString()] = signalValue;
}
else
{
var split = line.Split(' ');
split.MoveNext();
var inputLeft = line[split.Current];
split.MoveNext();
var logicOperator = Enum.Parse<Operator>(line[split.Current], true);
split.MoveNext();
var inputRight = line[split.Current];
split.MoveNext();
split.MoveNext();
var output = line[split.Current];
gates.Add(new LogicGate(inputLeft.ToString(), inputRight.ToString(), output.ToString(), logicOperator));
if (output.StartsWith('z'))
{
zGates.Add(output.ToString());
}
}
}
return (signals, gates, zGates);
}
private record LogicGate(string InputLeft, string InputRight, string Output, Operator LogicOperator);
private enum Operator
{
And,
Or,
Xor
}
// Source: https://stackoverflow.com/a/36634935
/// <summary>
/// Heap's algorithm to find all permutations. Non recursive, more efficient.
/// </summary>
/// <param name="items">Items to permute in each possible ways</param>
/// <param name="funcExecuteAndTellIfShouldStop"></param>
/// <returns>Return true if cancelled</returns>
private static bool ForAllPermutation<T>(T[] items, Func<T[], bool> funcExecuteAndTellIfShouldStop)
{
int countOfItem = items.Length;
if (countOfItem <= 1)
{
return funcExecuteAndTellIfShouldStop(items);
}
var indexes = new int[countOfItem];
// Unnecessary. Thanks to NetManage for the advise
// for (int i = 0; i < countOfItem; i++)
// {
// indexes[i] = 0;
// }
if (funcExecuteAndTellIfShouldStop(items))
{
return true;
}
for (int i = 1; i < countOfItem;)
{
if (indexes[i] < i)
{
// On the web there is an implementation with a multiplication which should be less efficient.
if ((i & 1) == 1) // if (i % 2 == 1) ... more efficient ??? At least the same.
{
Swap(ref items[i], ref items[indexes[i]]);
}
else
{
Swap(ref items[i], ref items[0]);
}
if (funcExecuteAndTellIfShouldStop(items))
{
return true;
}
indexes[i]++;
i = 1;
}
else
{
indexes[i++] = 0;
}
}
return false;
}
/// <summary>
/// Swap 2 elements of same type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="a"></param>
/// <param name="b"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
}

313
Inputs/Day24.txt Normal file
View File

@@ -0,0 +1,313 @@
x00: 1
x01: 0
x02: 1
x03: 1
x04: 0
x05: 0
x06: 1
x07: 1
x08: 0
x09: 1
x10: 1
x11: 1
x12: 1
x13: 1
x14: 0
x15: 1
x16: 0
x17: 1
x18: 0
x19: 1
x20: 1
x21: 0
x22: 0
x23: 0
x24: 1
x25: 1
x26: 1
x27: 0
x28: 1
x29: 1
x30: 1
x31: 0
x32: 0
x33: 1
x34: 1
x35: 0
x36: 0
x37: 0
x38: 1
x39: 0
x40: 0
x41: 0
x42: 0
x43: 1
x44: 1
y00: 1
y01: 0
y02: 0
y03: 1
y04: 1
y05: 0
y06: 0
y07: 0
y08: 0
y09: 0
y10: 0
y11: 1
y12: 0
y13: 0
y14: 0
y15: 0
y16: 1
y17: 0
y18: 0
y19: 1
y20: 0
y21: 1
y22: 0
y23: 1
y24: 0
y25: 0
y26: 1
y27: 0
y28: 0
y29: 1
y30: 0
y31: 0
y32: 0
y33: 1
y34: 0
y35: 0
y36: 0
y37: 1
y38: 0
y39: 0
y40: 1
y41: 0
y42: 1
y43: 1
y44: 1
x44 XOR y44 -> drc
phq OR frm -> hjs
vdh AND nwn -> gqd
y40 XOR x40 -> vkn
x21 XOR y21 -> cnb
cnb AND wmb -> vvk
dfb XOR bfn -> hbk
bhd XOR mmh -> z06
fkc XOR bwj -> z22
gnj AND jfw -> spq
dwh AND kqn -> fhp
x19 AND y19 -> kkg
drc XOR qqt -> z44
fkc AND bwj -> jbb
y32 XOR x32 -> qwt
y37 AND x37 -> kgg
x07 AND y07 -> dqn
dsp AND bvp -> hff
pmv OR pkn -> wmt
cjf OR pfk -> z45
hgq XOR phb -> z27
qnq OR dpc -> djp
x32 AND y32 -> nbb
qwt AND jqm -> fdk
x18 XOR y18 -> grp
vkf OR hdm -> kqn
cqv AND jss -> bwd
x00 AND y00 -> jfw
cjb XOR srm -> z19
jss XOR cqv -> z35
ntt OR spq -> ndd
cqm XOR qqj -> z43
x01 AND y01 -> ntt
y14 XOR x14 -> dfb
nbk XOR wrk -> z05
dvw AND rpg -> z23
vvc OR kcv -> qqj
bqc XOR fwr -> z26
dwh XOR kqn -> z41
x15 XOR y15 -> bkb
rjm XOR gjr -> z24
x22 XOR y22 -> bwj
y22 AND x22 -> hpj
x08 XOR y08 -> hnf
y27 AND x27 -> frm
wrw OR swr -> fds
gtm AND rmt -> mkv
kdh AND qvq -> ghr
fgv AND mfm -> kcv
hnf AND gqs -> pmv
kkg OR qvs -> vdh
fdk OR nbb -> rmt
y29 AND x29 -> cwd
hjk OR bts -> vkg
vtk AND npm -> tqb
dvw XOR rpg -> dbb
y39 XOR x39 -> mnm
y05 XOR x05 -> wrk
djd AND fds -> dqt
tvh OR sqm -> npm
cdr XOR cmt -> z10
x28 XOR y28 -> hgd
x33 XOR y33 -> gtm
mnm XOR vhv -> z39
fbv XOR bwg -> z38
hqs AND nhr -> vbt
kth OR qcp -> hgq
wjj OR scs -> bfn
bkv OR vvk -> fkc
cmt AND cdr -> pph
dqt OR gqb -> bqc
y35 XOR x35 -> jss
vkb OR krd -> cdr
mqf AND cvh -> trj
x36 XOR y36 -> dsp
y41 XOR x41 -> dwh
y38 XOR x38 -> bwg
ghr OR tpc -> jqm
ckn XOR hqq -> z07
vkn AND vkg -> vkf
y28 AND x28 -> wkc
x31 XOR y31 -> qvq
rjm AND gjr -> swr
gcb OR dbb -> rjm
y18 AND x18 -> z18
y24 AND x24 -> wrw
x17 XOR y17 -> kbh
y24 XOR x24 -> gjr
x26 XOR y26 -> fwr
y43 AND x43 -> bvm
y15 AND x15 -> sbt
y12 XOR x12 -> fvh
kvn OR ffb -> cjb
y31 AND x31 -> tpc
y37 XOR x37 -> bnh
y11 AND x11 -> scv
hgd AND hjs -> phr
jfw XOR gnj -> z01
fvh XOR sfk -> z12
fds XOR djd -> z25
qwt XOR jqm -> z32
bvp XOR dsp -> z36
phr OR wkc -> jdq
y07 XOR x07 -> hqq
y43 XOR x43 -> cqm
bnh XOR kss -> z37
trg OR vbt -> kdh
cwd OR pkc -> nhr
y19 XOR x19 -> srm
dkd AND jdq -> pkc
wrk AND nbk -> fnk
hjs XOR hgd -> z28
rnt AND qbs -> rcp
djp XOR mft -> z03
cht OR mkv -> mqf
hbk XOR bkb -> z15
x44 AND y44 -> pfk
x26 AND y26 -> qcp
ndd XOR jgw -> z02
x06 AND y06 -> dhs
ckn AND hqq -> cpt
y13 AND x13 -> wjj
x10 AND y10 -> tvr
ffr OR gqd -> wmb
y09 XOR x09 -> tjb
nhr XOR hqs -> z30
hgq AND phb -> phq
x00 XOR y00 -> z00
y16 AND x16 -> prt
dqn OR cpt -> gqs
x23 AND y23 -> gcb
mft AND djp -> tvh
bkb AND hbk -> qtw
kfk AND chk -> vgb
vhv AND mnm -> hjk
y42 XOR x42 -> mfm
x25 XOR y25 -> djd
fgv XOR mfm -> z42
grp XOR fgr -> kvn
x16 XOR y16 -> chk
x09 AND y09 -> krd
cqb OR rmg -> mqm
y30 XOR x30 -> hqs
kss AND bnh -> dvf
y11 XOR x11 -> rnt
x04 AND y04 -> ptm
y13 XOR x13 -> cbr
rnt XOR qbs -> z11
hff OR fjf -> kss
x25 AND y25 -> gqb
kdh XOR qvq -> z31
y06 XOR x06 -> mmh
cnb XOR wmb -> z21
y02 XOR x02 -> jgw
x17 AND y17 -> hkn
y34 AND x34 -> cvh
x27 XOR y27 -> phb
x42 AND y42 -> vvc
x03 XOR y03 -> mft
y35 AND x35 -> nvg
x10 XOR y10 -> cmt
y20 AND x20 -> ffr
x30 AND y30 -> trg
ptm OR tqb -> nbk
bfn AND dfb -> sjr
jgw AND ndd -> qnq
y39 AND x39 -> bts
y38 AND x38 -> knq
npm XOR vtk -> z04
prt OR vgb -> jqc
kfk XOR chk -> z16
rvd OR dhs -> ckn
fnk OR wkv -> bhd
y03 AND x03 -> sqm
x08 AND y08 -> pkn
y02 AND x02 -> dpc
bwg AND fbv -> pdw
mqf XOR cvh -> z34
tjb AND wmt -> vkb
jdq XOR dkd -> z29
x34 XOR y34 -> tfn
y21 AND x21 -> bkv
tfn OR trj -> cqv
fgr AND grp -> ffb
bwd OR nvg -> bvp
cjv OR hkn -> fgr
fhp OR rnc -> fgv
sjr OR tck -> z14
vkn XOR vkg -> z40
kbh XOR jqc -> z17
kgg OR dvf -> fbv
tjb XOR wmt -> z09
tvr OR pph -> qbs
qqj AND cqm -> gsg
pdw OR knq -> vhv
gqs XOR hnf -> z08
y20 XOR x20 -> nwn
kbh AND jqc -> cjv
bqc AND fwr -> kth
rcp OR scv -> sfk
sbt OR qtw -> kfk
bhd AND mmh -> rvd
nwn XOR vdh -> z20
y29 XOR x29 -> dkd
y23 XOR x23 -> rpg
jbb OR hpj -> dvw
cbr XOR mqm -> z13
y33 AND x33 -> cht
y12 AND x12 -> rmg
mqm AND cbr -> scs
sfk AND fvh -> cqb
y14 AND x14 -> tck
x04 XOR y04 -> vtk
y05 AND x05 -> wkv
rmt XOR gtm -> z33
y01 XOR x01 -> gnj
srm AND cjb -> qvs
x36 AND y36 -> fjf
drc AND qqt -> cjf
y40 AND x40 -> hdm
y41 AND x41 -> rnc
gsg OR bvm -> qqt