Add day 24
This commit is contained in:
370
Days/Day24.cs
Normal file
370
Days/Day24.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user