about summary refs log tree commit diff
path: root/aoc2019/lib/IntCodeVM.cs
diff options
context:
space:
mode:
Diffstat (limited to 'aoc2019/lib/IntCodeVM.cs')
-rw-r--r--aoc2019/lib/IntCodeVM.cs160
1 files changed, 160 insertions, 0 deletions
diff --git a/aoc2019/lib/IntCodeVM.cs b/aoc2019/lib/IntCodeVM.cs
new file mode 100644
index 0000000..2190579
--- /dev/null
+++ b/aoc2019/lib/IntCodeVM.cs
@@ -0,0 +1,160 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace aoc2019.lib
+{
+    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 relbase;
+
+        public IntCodeVM(string tape)
+        {
+            i = 0;
+            relbase = 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;
+            relbase = 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);
+            switch (Mode(idx))
+            {
+                case 0: return MemGet(param);
+                case 1: return param;
+                case 2: return MemGet(relbase + param);
+                default: 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(relbase + 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:
+                        relbase += Get(1);
+                        i += 2;
+                        break;
+                    case 99:
+                        return HaltType.Terminate;
+                    default:
+                        throw new Exception($"unknown op {op} at {i}");
+                }
+            }
+
+            return HaltType.Terminate;
+        }
+    }
+}
\ No newline at end of file