Files
advent-of-code-2024/17/ChronospatialComputer/Program.cs
T

191 lines
4.2 KiB
C#

using System.Diagnostics;
if (args.Length is 0)
{
Console.WriteLine("Please provide a path to the input file.");
return;
}
if (File.Exists(args[0]) is false)
{
Console.WriteLine("The provided file does not exist.");
return;
}
var isPart2 = args.Length is 2 && args[1] is "part2";
var input = await File.ReadAllTextAsync(args[0]);
var stopwatch = new Stopwatch();
stopwatch.Start();
var computer = Computer.From(input);
var msg = isPart2
? $"The program outputs a copy at itself when the A register is: {computer.FindCopy()}"
: $"The output of the program is {computer.RunProgram()}";
stopwatch.Stop();
Console.WriteLine($"{msg}. ({stopwatch.ElapsedMilliseconds}ms)");
class Computer
{
private readonly int[] _program;
private readonly Dictionary<int, Func<int, bool>> _instructions;
private long _aRegister;
private long _bRegister;
private long _cRegister;
private int _instructionPointer;
private readonly List<long> _output = [];
private Computer(long a, long b, long c, int[] program)
{
_aRegister = a;
_bRegister = b;
_cRegister = c;
_program = program;
_instructions = new()
{
{ 0, Adv },
{ 1, Bxl },
{ 2, Bst },
{ 3, Jnz },
{ 4, Bxc },
{ 5, Out },
{ 6, Bdv },
{ 7, Cdv },
};
}
public static Computer From(string input)
{
var parts = input.Split($"{Environment.NewLine}{Environment.NewLine}");
if (parts.Length is not 2)
{
throw new ArgumentException("The computer input has more than 2 parts");
}
var registersPart = parts[0];
var programPart = parts[1];
var registers = registersPart.Split(Environment.NewLine);
if (registers.Length is not 3)
{
throw new ArgumentException($"There were only {registers.Length} registers given");
}
var registerA = int.Parse(registers[0].Split(':')[1].Trim());
var registerB = int.Parse(registers[1].Split(':')[1].Trim());
var registerC = int.Parse(registers[2].Split(':')[1].Trim());
var program = programPart.Split(':')[1].Trim().Split(',').Select(int.Parse).ToArray();
return new(registerA, registerB, registerC, program);
}
public string RunProgram()
{
while (_instructionPointer < _program.Length)
{
var opcode = _program[_instructionPointer];
var operand = _program[_instructionPointer + 1];
if (_instructions.TryGetValue(opcode, out var instruction) is false)
{
throw new InvalidOperationException($"{nameof(opcode)} is unknown: {opcode}");
}
var shouldAdvance = instruction(operand);
if (shouldAdvance is false)
{
continue;
}
_instructionPointer += 2;
}
return string.Join(',', _output);
}
public long FindCopy()
{
long lowestInitialValue = _aRegister;
var targetOutput = string.Join(',', _program);
string output;
do
{
lowestInitialValue += 1;
output = new Computer(lowestInitialValue, _bRegister, _cRegister, _program).RunProgram();
} while (output != targetOutput);
return lowestInitialValue;
}
private long GetComboOperand(int operand) => operand switch
{
0 or 1 or 2 or 3 => operand,
4 => _aRegister,
5 => _bRegister,
6 => _cRegister,
_ => throw new ArgumentException($"{nameof(operand)} is not a valid operand: {operand}"),
};
private bool Adv(int operand)
{
_aRegister /= (int)Math.Pow(2, GetComboOperand(operand));
return true;
}
private bool Bxl(int operand)
{
_bRegister ^= operand;
return true;
}
private bool Bst(int operand)
{
_bRegister = GetComboOperand(operand) % 8;
return true;
}
private bool Jnz(int operand)
{
if (_aRegister is 0)
{
return true;
}
_instructionPointer = operand;
return false;
}
private bool Bxc(int operand)
{
_bRegister ^= _cRegister;
return true;
}
private bool Out(int operand)
{
_output.Add(GetComboOperand(operand) % 8);
return true;
}
private bool Bdv(int operand)
{
_bRegister = _aRegister / (int)Math.Pow(2, GetComboOperand(operand));
return true;
}
private bool Cdv(int operand)
{
_cRegister = _aRegister / (int)Math.Pow(2, GetComboOperand(operand));
return true;
}
}