247 lines
7.6 KiB
C#
247 lines
7.6 KiB
C#
using System.Numerics;
|
|
using Spectre.Console;
|
|
|
|
namespace AdventOfCode.Days;
|
|
|
|
public class Day17 : Day
|
|
{
|
|
public override int Number => 17;
|
|
public override string Name => "Chronospatial Computer";
|
|
|
|
public override void RunPart1(bool display = true)
|
|
{
|
|
var (registerA, registerB, registerC, program) = ParseState();
|
|
|
|
var toOutput = new List<int>();
|
|
var instructionPointer = 0;
|
|
|
|
while (instructionPointer < program.Length)
|
|
{
|
|
var opCode = program[instructionPointer];
|
|
|
|
var isComboOperand = opCode switch
|
|
{
|
|
0 => true,
|
|
1 => false,
|
|
2 => true,
|
|
3 => false,
|
|
4 => false,
|
|
5 => true,
|
|
6 => true,
|
|
7 => true,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(opCode))
|
|
};
|
|
|
|
var operand = (isComboOperand, program[instructionPointer + 1]) switch
|
|
{
|
|
(false, var literalValue) => literalValue,
|
|
(true, 0) => 0,
|
|
(true, 1) => 1,
|
|
(true, 2) => 2,
|
|
(true, 3) => 3,
|
|
(true, 4) => registerA,
|
|
(true, 5) => registerB,
|
|
(true, 6) => registerC,
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
};
|
|
|
|
switch (opCode)
|
|
{
|
|
// adv
|
|
case 0:
|
|
registerA = (int)(registerA / Math.Pow(2, operand));
|
|
break;
|
|
|
|
// bxl
|
|
case 1:
|
|
registerB = registerB ^ operand;
|
|
break;
|
|
|
|
// bst
|
|
case 2:
|
|
registerB = operand % 8;
|
|
break;
|
|
|
|
// jnz
|
|
case 3:
|
|
if (registerA is not 0)
|
|
{
|
|
instructionPointer = operand - 2;
|
|
}
|
|
break;
|
|
|
|
// bxc
|
|
case 4:
|
|
registerB = registerB ^ registerC;
|
|
break;
|
|
|
|
// out
|
|
case 5:
|
|
toOutput.Add(operand % 8);
|
|
break;
|
|
|
|
// bdv
|
|
case 6:
|
|
registerB = (int)(registerA / Math.Pow(2, operand));
|
|
break;
|
|
|
|
// cdv
|
|
case 7:
|
|
registerC = (int)(registerA / Math.Pow(2, operand));
|
|
break;
|
|
}
|
|
|
|
instructionPointer += 2;
|
|
}
|
|
|
|
if (display)
|
|
{
|
|
AnsiConsole.MarkupLine($"[green]Output: [yellow]{string.Join(',', toOutput)}[/][/]");
|
|
}
|
|
}
|
|
|
|
public override void RunPart2(bool display = true)
|
|
{
|
|
var (_, originalRegisterB, originalRegisterC, program) = ParseState();
|
|
var toOutput = new List<int>();
|
|
var instructionPointer = 0;
|
|
|
|
var finalRegisterA = 0;
|
|
|
|
AnsiConsole.Status().Start("Computing (0)...", ctx =>
|
|
{
|
|
for (var initialRegisterA = 0; initialRegisterA < int.MaxValue; initialRegisterA++)
|
|
{
|
|
// Reset state
|
|
var registerA = initialRegisterA;
|
|
var registerB = originalRegisterB;
|
|
var registerC = originalRegisterC;
|
|
|
|
toOutput.Clear();
|
|
instructionPointer = 0;
|
|
|
|
while (instructionPointer < program.Length)
|
|
{
|
|
var opCode = program[instructionPointer];
|
|
|
|
var isComboOperand = opCode switch
|
|
{
|
|
0 => true,
|
|
1 => false,
|
|
2 => true,
|
|
3 => false,
|
|
4 => false,
|
|
5 => true,
|
|
6 => true,
|
|
7 => true,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(opCode))
|
|
};
|
|
|
|
var operand = (isComboOperand, program[instructionPointer + 1]) switch
|
|
{
|
|
(false, var literalValue) => literalValue,
|
|
(true, 0) => 0,
|
|
(true, 1) => 1,
|
|
(true, 2) => 2,
|
|
(true, 3) => 3,
|
|
(true, 4) => registerA,
|
|
(true, 5) => registerB,
|
|
(true, 6) => registerC,
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
};
|
|
|
|
switch (opCode)
|
|
{
|
|
// adv
|
|
case 0:
|
|
registerA = (int)(registerA / Math.Pow(2, operand));
|
|
break;
|
|
|
|
// bxl
|
|
case 1:
|
|
registerB = registerB ^ operand;
|
|
break;
|
|
|
|
// bst
|
|
case 2:
|
|
registerB = operand % 8;
|
|
break;
|
|
|
|
// jnz
|
|
case 3:
|
|
if (registerA is not 0)
|
|
{
|
|
instructionPointer = operand - 2;
|
|
}
|
|
break;
|
|
|
|
// bxc
|
|
case 4:
|
|
registerB = registerB ^ registerC;
|
|
break;
|
|
|
|
// out
|
|
case 5:
|
|
toOutput.Add(operand % 8);
|
|
break;
|
|
|
|
// bdv
|
|
case 6:
|
|
registerB = (int)(registerA / Math.Pow(2, operand));
|
|
break;
|
|
|
|
// cdv
|
|
case 7:
|
|
registerC = (int)(registerA / Math.Pow(2, operand));
|
|
break;
|
|
}
|
|
|
|
instructionPointer += 2;
|
|
}
|
|
|
|
if (initialRegisterA % 1000 == 0)
|
|
{
|
|
ctx.Status($"Computing ({initialRegisterA})...");
|
|
}
|
|
|
|
// Check if output is the program itself
|
|
if (toOutput.SequenceEqual(program))
|
|
{
|
|
finalRegisterA = initialRegisterA;
|
|
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
if (display)
|
|
{
|
|
AnsiConsole.MarkupLine($"[green]Lowest possible value for register A to output itself: [yellow]{finalRegisterA}[/][/]");
|
|
}
|
|
}
|
|
|
|
private (int RegisterA, int RegisterN, int RegisterC, int[] Program) ParseState()
|
|
{
|
|
var lineIterator = Input.AsSpan().EnumerateLines();
|
|
|
|
lineIterator.MoveNext();
|
|
var registerA = int.Parse(lineIterator.Current[(lineIterator.Current.IndexOf(':') + 2)..]);
|
|
|
|
lineIterator.MoveNext();
|
|
var registerB = int.Parse(lineIterator.Current[(lineIterator.Current.IndexOf(':') + 2)..]);
|
|
|
|
lineIterator.MoveNext();
|
|
var registerC = int.Parse(lineIterator.Current[(lineIterator.Current.IndexOf(':') + 2)..]);
|
|
|
|
lineIterator.MoveNext();
|
|
lineIterator.MoveNext();
|
|
|
|
var program = lineIterator.Current[(lineIterator.Current.IndexOf(':') + 2)..]
|
|
.ToString()
|
|
.Split(',')
|
|
.Select(int.Parse)
|
|
.ToArray();
|
|
|
|
return (registerA, registerB, registerC, program);
|
|
}
|
|
} |