Skip to content

Instantly share code, notes, and snippets.

@davidwhitney
Last active December 12, 2019 23:52
Show Gist options
  • Save davidwhitney/b56239716d4dedcfaa003a254e1c0c3f to your computer and use it in GitHub Desktop.
Save davidwhitney/b56239716d4dedcfaa003a254e1c0c3f to your computer and use it in GitHub Desktop.
Advent of Code IntCodeVm C# port
3,8,1001,8,10,8,105,1,0,0,21,34,47,72,93,110,191,272,353,434,99999,3,9,102,3,9,9,1001,9,3,9,4,9,99,3,9,102,4,9,9,1001,9,4,9,4,9,99,3,9,101,3,9,9,1002,9,3,9,1001,9,2,9,1002,9,2,9,101,4,9,9,4,9,99,3,9,1002,9,3,9,101,5,9,9,102,4,9,9,1001,9,4,9,4,9,99,3,9,101,3,9,9,102,4,9,9,1001,9,3,9,4,9,99,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,2,9,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,99
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace IntCodeVmSharp
{
public class IntCodeVm
{
public bool PauseOnOutput { get; set; }
public int RelativeBase { get; private set; }
public int InstructionPointer { get; private set; }
public List<int> State { get; private set; } = new List<int>();
public int ValueUnderInstructionPointer => State[InstructionPointer];
public Queue<int> Input { get; }
public List<int> Output { get; }
public Func<int> StdIn { get; set; }
public Action<int> StdOut { get; set; }
public IntCodeVm(IEnumerable<int> values)
{
State.AddRange(values);
Output = new List<int>();
Input = new Queue<int>();
StdIn = () => Input.Dequeue();
StdOut = value => Output.Add(value);
}
public bool Execute()
{
var operations = new List<Func<IntCodeVm, OpCode, int>>
{
Operations.NoOp,
Operations.Add,
Operations.Multiply,
Operations.StoreInput,
Operations.WriteOutput,
Operations.JumpIfTrue,
Operations.JumpIfFalse,
Operations.LessThan,
Operations.Equals,
Operations.AdjustRelativeBase
};
var opCode = OpCode.FromValue(ValueUnderInstructionPointer);
while (opCode.Value != 99 && operations[opCode.Value] != null)
{
var operation = operations[opCode.Value];
InstructionPointer = operation(this, opCode);
if (opCode.IsOutput() && PauseOnOutput)
{
return false;
}
opCode = OpCode.FromValue(ValueUnderInstructionPointer);
}
return true;
}
public IntCodeVm ResetStateUsing(int noun, int verb)
{
State[1] = noun;
State[2] = verb;
return this;
}
public static IntCodeVm WithInput(string path) => WithInputString(File.ReadAllText(path));
public static IntCodeVm WithInputString(string data) => new IntCodeVm(data.Split(',').Select(int.Parse));
public static IEnumerable<IntCodeVm> Create(int quantity, Func<IntCodeVm> factoryFunc)
{
for (var i = 0; i < quantity; i++)
{
yield return factoryFunc();
}
}
private class Operations
{
public static int NoOp(IntCodeVm ctx, OpCode opCode) => -1;
public static int Add(IntCodeVm ctx, OpCode opCode)
{
var p1 = GetValue(ctx, opCode, 1);
var p2 = GetValue(ctx, opCode, 2);
var target = GetTarget(ctx, opCode, 3);
ctx.State[target] = p1 + p2;
return ctx.InstructionPointer + 4;
}
public static int Multiply(IntCodeVm ctx, OpCode opCode)
{
var p1 = GetValue(ctx, opCode, 1);
var p2 = GetValue(ctx, opCode, 2);
var target = GetTarget(ctx, opCode, 3);
ctx.State[target] = p1 * p2;
return ctx.InstructionPointer + 4;
}
public static int StoreInput(IntCodeVm ctx, OpCode opCode)
{
var target = GetTarget(ctx, opCode, 1);
ctx.State[target] = ctx.StdIn();
return ctx.InstructionPointer + 2;
}
public static int WriteOutput(IntCodeVm ctx, OpCode opCode)
{
ctx.StdOut(GetValue(ctx, opCode, 1));
return ctx.InstructionPointer + 2;
}
public static int JumpIfTrue(IntCodeVm ctx, OpCode opCode)
{
var p1 = GetValue(ctx, opCode, 1);
var p2 = GetValue(ctx, opCode, 2);
return p1 != 0 ? p2 : ctx.InstructionPointer + 3;
}
public static int JumpIfFalse(IntCodeVm ctx, OpCode opCode)
{
var p1 = GetValue(ctx, opCode, 1);
var p2 = GetValue(ctx, opCode, 2);
return p1 == 0 ? p2 : ctx.InstructionPointer + 3;
}
public static int LessThan(IntCodeVm ctx, OpCode opCode)
{
var p1 = GetValue(ctx, opCode, 1);
var p2 = GetValue(ctx, opCode, 2);
var target = GetTarget(ctx, opCode, 3);
ctx.State[target] = p1 < p2 ? 1 : 0;
return ctx.InstructionPointer + 4;
}
public static int Equals(IntCodeVm ctx, OpCode opCode)
{
var p1 = GetValue(ctx, opCode, 1);
var p2 = GetValue(ctx, opCode, 2);
var target = GetTarget(ctx, opCode, 3);
ctx.State[target] = p1 == p2 ? 1 : 0;
return ctx.InstructionPointer + 4;
}
public static int AdjustRelativeBase(IntCodeVm ctx, OpCode opCode)
{
ctx.RelativeBase += GetValue(ctx, opCode, 1);
return ctx.InstructionPointer + 2;
}
private static int GetTarget(IntCodeVm ctx, OpCode opCode, int paramNumber)
{
var target = ctx.State[ctx.InstructionPointer + paramNumber];
return opCode.AccessModeForParameter(paramNumber) == 2 ? target + ctx.RelativeBase : target;
}
private static int GetValue(IntCodeVm ctx, OpCode opCode, int paramNumber)
{
var identifier = ctx.InstructionPointer + paramNumber;
switch (opCode.AccessModeForParameter(paramNumber))
{
case 0: return ctx.State[ctx.State[identifier]];
case 1: return ctx.State[identifier];
case 2: return ctx.State[(ctx.State[identifier] + ctx.RelativeBase)];
}
throw new Exception("Unsupported access mode.");
}
}
}
public class OpCode
{
public int Value;
public string AccessMask;
public OpCode(int opCode, string accessMask)
{
Value = opCode;
AccessMask = accessMask;
}
public int AccessModeForParameter(int paramOffset)
{
var offset = AccessMask.Length - paramOffset;
return offset < 0 ? 0 : int.Parse(AccessMask[offset].ToString());
}
public static OpCode FromPackedValue(string value)
{
var opCode = int.Parse(value.Substring(value.Length - 2));
var accessMask = value.Substring(0, value.Length - 2);
return new OpCode(opCode, accessMask);
}
public static OpCode FromValue(int? value)
{
if (value == null)
{
return HaltExecution();
}
return value.ToString().Length > 2
? FromPackedValue(value.ToString())
: AllPositionMode(value.Value);
}
public bool IsHalt() => Value == 0 || Value == 99;
public bool IsOutput() => Value == 4;
public static OpCode AllPositionMode(int opCode) => new OpCode(opCode, "000000000000000000");
public static OpCode HaltExecution() => AllPositionMode(0);
}
}
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace IntCodeVmSharp
{
public class IntCodeVmTests
{
[Test]
public void Example1()
{
var output = ChainAmpsWithProgram(
new[] { 3, 15, 3, 16, 1002, 16, 10, 16, 1, 16, 15, 15, 4, 15, 99, 0, 0 },
Q.With(4, 3, 2, 1, 0)
);
Assert.That(output, Is.EqualTo(43210));
}
[Test]
public void Example2()
{
var output = ChainAmpsWithProgram(
new[] { 3, 23, 3, 24, 1002, 24, 10, 24, 1002, 23, -1, 23, 101, 5, 23, 23, 1, 24, 23, 23, 4, 23, 99, 0, 0 },
Q.With(0, 1, 2, 3, 4)
);
Assert.That(output, Is.EqualTo(54321));
}
[Test]
public void Example3()
{
var output = ChainAmpsWithProgram(
new[] { 3, 31, 3, 32, 1002, 32, 10, 32, 1001, 31, -2, 31, 1007, 31, 0, 33, 1002, 33, 7, 33, 1, 33, 31, 31, 1, 32, 31, 31, 4, 31, 99, 0, 0, 0 },
Q.With(1, 0, 4, 3, 2)
);
Assert.That(output, Is.EqualTo(65210));
}
[Test]
public void Puzzle1()
{
var highest = 0;
var permutations = new[] {0, 1, 2, 3, 4}.GetPermutations();
foreach (var permutation in permutations)
{
var queue = Q.With(permutation.ToList());
var amps = IntCodeVm.Create(5, () => IntCodeVm.WithInput("day7-input1.txt"));
var output = RunEachAmpOnce(amps, queue);
if (output > highest)
{
highest = output;
}
}
Assert.That(highest, Is.EqualTo(17440));
}
private static int ChainAmpsWithProgram(int[] program, Queue<int> phaseSettings)
{
var amps = IntCodeVm.Create(5, () => new IntCodeVm(program));
return RunEachAmpOnce(amps, phaseSettings);
}
private static int RunEachAmpOnce(IEnumerable<IntCodeVm> amplifiers, Queue<int> phaseSettings)
{
var inputQueue = new Queue<int>();
inputQueue.Enqueue(0);
foreach(var amp in amplifiers)
{
var inputsForThisAmp = new Queue<int>();
inputsForThisAmp.Enqueue(phaseSettings.Dequeue());
inputsForThisAmp.Enqueue(inputQueue.Dequeue());
amp.StdIn = () => inputsForThisAmp.Dequeue();
amp.StdOut = output => inputQueue.Enqueue(output);
amp.Execute();
}
return inputQueue.Dequeue();
}
}
public static class Q
{
public static Queue<int> With(params int[] opCode) => With(opCode.ToList());
public static Queue<int> With(List<int> opCode)
{
var queue = new Queue<int>();
opCode.ForEach(c => queue.Enqueue(c));
return queue;
}
}
public static class SomeExtensions
{
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> enumerable)
{
var array = enumerable as T[] ?? enumerable.ToArray();
var factorials = Enumerable.Range(0, array.Length + 1)
.Select(Factorial)
.ToArray();
for (var i = 0L; i < factorials[array.Length]; i++)
{
var sequence = GenerateSequence(i, array.Length - 1, factorials);
yield return GeneratePermutation(array, sequence);
}
}
private static IEnumerable<T> GeneratePermutation<T>(T[] array, IReadOnlyList<int> sequence)
{
var clone = (T[])array.Clone();
for (var i = 0; i < clone.Length - 1; i++)
{
Swap(ref clone[i], ref clone[i + sequence[i]]);
}
return clone;
}
private static int[] GenerateSequence(long number, int size, IReadOnlyList<long> factorials)
{
var sequence = new int[size];
for (var j = 0; j < sequence.Length; j++)
{
var facto = factorials[sequence.Length - j];
sequence[j] = (int)(number / facto);
number = (int)(number % facto);
}
return sequence;
}
private static void Swap<T>(ref T a, ref T b)
{
var temp = a;
a = b;
b = temp;
}
private static long Factorial(int n)
{
long result = n;
for (var i = 1; i < n; i++)
{
result *= i;
}
return result;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment