about summary refs log tree commit diff
path: root/aoc2019/IntCodeVM.cs
diff options
context:
space:
mode:
Diffstat (limited to 'aoc2019/IntCodeVM.cs')
-rw-r--r--aoc2019/IntCodeVM.cs155
1 files changed, 155 insertions, 0 deletions
diff --git a/aoc2019/IntCodeVM.cs b/aoc2019/IntCodeVM.cs
new file mode 100644
index 0000000..d6f1214
--- /dev/null
+++ b/aoc2019/IntCodeVM.cs
@@ -0,0 +1,155 @@
+namespace aoc2019;
+
+public class IntCodeVM
+{
+    public enum HaltType
+    {
+        Terminate,
+        Waiting
+    }
+
+    private readonly long[] program;
+    private long i;
+    public Queue<long> input, output;
+    public long[] memory;
+    private long relativeBase;
+
+    public IntCodeVM(string tape)
+    {
+        i = 0;
+        relativeBase = 0;
+        program = tape.Split(',').Select(long.Parse).ToArray();
+        memory = program;
+        input = new Queue<long>();
+        output = new Queue<long>();
+    }
+
+    public long Result => output.Dequeue();
+
+    public void Reset()
+    {
+        i = 0;
+        relativeBase = 0;
+        memory = program;
+        input.Clear();
+        output.Clear();
+    }
+
+    public void AddInput(params long[] values)
+    {
+        foreach (var v in values) AddInput(v);
+    }
+
+    public void AddInput(long value)
+    {
+        input.Enqueue(value);
+    }
+
+    private long MemGet(long addr)
+    {
+        return addr < memory.Length ? memory[addr] : 0;
+    }
+
+    private void MemSet(long addr, long value)
+    {
+        if (addr < 0) addr = 0;
+        if (addr >= memory.Length)
+            Array.Resize(ref memory, (int)addr + 1);
+        memory[addr] = value;
+    }
+
+    private long Mode(long idx)
+    {
+        var mode = MemGet(i) / 100;
+        for (var s = 1; s < idx; s++)
+            mode /= 10;
+        return mode % 10;
+    }
+
+    private long Get(long idx)
+    {
+        var param = MemGet(i + idx);
+        return Mode(idx) switch
+        {
+            0 => MemGet(param),
+            1 => param,
+            2 => MemGet(relativeBase + param),
+            _ => throw new Exception("invalid parameter mode"),
+        };
+    }
+
+    private void Set(long idx, long val)
+    {
+        var param = MemGet(i + idx);
+        switch (Mode(idx))
+        {
+            case 0:
+                MemSet(param, val);
+                break;
+            case 1: throw new Exception("cannot set in immediate mode");
+            case 2:
+                MemSet(relativeBase + param, val);
+                break;
+            default: throw new Exception("invalid parameter mode");
+        }
+    }
+
+    public HaltType Run(params long[] additionalInput)
+    {
+        foreach (var s in additionalInput) AddInput(s);
+        return Run();
+    }
+
+    public HaltType Run()
+    {
+        while (i < memory.Length)
+        {
+            var op = MemGet(i) % 100;
+            switch (op)
+            {
+                case 1:
+                    Set(3, Get(1) + Get(2));
+                    i += 4;
+                    break;
+                case 2:
+                    Set(3, Get(1) * Get(2));
+                    i += 4;
+                    break;
+                case 3:
+                    if (!input.Any())
+                        return HaltType.Waiting;
+                    Set(1, input.Dequeue());
+                    i += 2;
+                    break;
+                case 4:
+                    output.Enqueue(Get(1));
+                    i += 2;
+                    break;
+                case 5:
+                    i = Get(1) == 0 ? i + 3 : Get(2);
+                    break;
+                case 6:
+                    i = Get(1) != 0 ? i + 3 : Get(2);
+                    break;
+                case 7:
+                    Set(3, Get(1) < Get(2) ? 1 : 0);
+                    i += 4;
+                    break;
+                case 8:
+                    Set(3, Get(1) == Get(2) ? 1 : 0);
+                    i += 4;
+                    break;
+                case 9:
+                    relativeBase += Get(1);
+                    i += 2;
+                    break;
+                case 99:
+                    return HaltType.Terminate;
+                default:
+                    throw new Exception($"unknown op {op} at {i}");
+            }
+        }
+
+        return HaltType.Terminate;
+    }
+}