diff --git a/Days/Day24.cs b/Days/Day24.cs new file mode 100644 index 0000000..ab2ee2f --- /dev/null +++ b/Days/Day24.cs @@ -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(); + + 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 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? 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 Signals, List Gates, HashSet ZGates) ParseInput() + { + var readingSignals = true; + + var signals = new Dictionary(); + var gates = new List(); + var zGates = new HashSet(); + + 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(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 + + /// + /// Heap's algorithm to find all permutations. Non recursive, more efficient. + /// + /// Items to permute in each possible ways + /// + /// Return true if cancelled + private static bool ForAllPermutation(T[] items, Func 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; + } + + /// + /// Swap 2 elements of same type + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Swap(ref T a, ref T b) + { + T temp = a; + a = b; + b = temp; + } +} \ No newline at end of file diff --git a/Inputs/Day24.txt b/Inputs/Day24.txt new file mode 100644 index 0000000..ff4240e --- /dev/null +++ b/Inputs/Day24.txt @@ -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 \ No newline at end of file