Last active
December 12, 2019 23:52
-
-
Save davidwhitney/b56239716d4dedcfaa003a254e1c0c3f to your computer and use it in GitHub Desktop.
Advent of Code IntCodeVm C# port
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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