C#
This one is mostly thanks to reading @mykl@[email protected]'s code to understand WTF was going on for part 2, and then once I understood the basics, finally got to solving it myself. Instructions were read in as long
because I didn't want to deal with int
vs. long
all the time.
using System.Collections.Immutable;
using System.Diagnostics;
using Common;
namespace Day17;
static class Program
{
public record State(long A, long B, long C, int InstPtr, ImmutableList<long> Output);
static void Main()
{
var start = Stopwatch.GetTimestamp();
var (sampleReg, sampleInst) = ReceiveInput("sample.txt");
var (inputReg, inputInst) = ReceiveInput("input.txt");
Console.WriteLine($"Part 1 sample: {Part1(sampleReg, sampleInst)}");
Console.WriteLine($"Part 1 input: {Part1(inputReg, inputInst)}");
(sampleReg, sampleInst) = ReceiveInput("sample2.txt");
Console.WriteLine($"Part 2 sample: {Part2(sampleReg, sampleInst)}");
Console.WriteLine($"Part 2 input: {Part2(inputReg, inputInst)}");
Console.WriteLine($"That took about {Stopwatch.GetElapsedTime(start)}");
}
static object Part1(State state, ImmutableArray<long> instructions) =>
Execute(instructions, state).Output.StringifyAndJoin(",");
static object Part2(State state, ImmutableArray<long> instructions) =>
RecursiveSolve(instructions, state with { A = 0 }, []).First();
static IEnumerable<long> RecursiveSolve(ImmutableArray<long> instructions, State state, ImmutableList<long> soFar) =>
(soFar.Count == instructions.Length) ? [state.A] :
Enumerable.Range(0, 8)
.Select(a => state with { A = (state.A << 3) + a })
.Where(newState => newState.A != state.A)
.Select(newState => new { newState, Execute(instructions, newState).Output, })
.Where(states => states.Output.SequenceEqual(instructions.TakeLast(states.Output.Count)))
.SelectMany(states => RecursiveSolve(instructions, states.newState, states.Output));
static State Execute(ImmutableArray<long> instructions, State state)
{
while (state.InstPtr < instructions.Length)
{
var opcode = instructions[state.InstPtr];
var operand = instructions[state.InstPtr + 1];
state = Operations[opcode](state, operand);
}
return state;
}
static long ComboOperand(long operand, State state) => operand switch
{
>= 0 and <= 3 => operand,
4 => state.A,
5 => state.B,
6 => state.C,
_ => throw new Exception("Invalid operand."),
};
static long Adv(long op, State state) => state.A / (long)Math.Pow(2, ComboOperand(op, state));
static readonly Func<State, long, State>[] Operations =
[
(s, op) => s with { InstPtr = s.InstPtr + 2, A = Adv(op, s) },
(s, op) => s with { InstPtr = s.InstPtr + 2, B = s.B ^ op },
(s, op) => s with { InstPtr = s.InstPtr + 2, B = ComboOperand(op, s) % 8 },
(s, op) => s with { InstPtr = (s.A == 0) ? (s.InstPtr + 2) : (op <= int.MaxValue) ? (int)op : throw new ArithmeticException("Integer overflow!") },
(s, _) => s with { InstPtr = s.InstPtr + 2, B = s.B ^ s.C },
(s, op) => s with { InstPtr = s.InstPtr + 2, Output = s.Output.Add(ComboOperand(op, s) % 8) },
(s, op) => s with { InstPtr = s.InstPtr + 2, B = Adv(op, s) },
(s, op) => s with { InstPtr = s.InstPtr + 2, C = Adv(op, s) },
];
static (State, ImmutableArray<long> instructions) ReceiveInput(string file)
{
var input = File.ReadAllLines(file);
return
(
new State(
long.Parse(input[0].Substring("Register A: ".Length)),
long.Parse(input[1].Substring("Register B: ".Length)),
long.Parse(input[2].Substring("Register C: ".Length)),
0,
[]),
input[4].Substring("Program: ".Length)
.Split(",")
.Select(long.Parse)
.ToImmutableArray()
);
}
}