about summary refs log tree commit diff
path: root/lib/IntCodeVM.cs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/IntCodeVM.cs')
-rw-r--r--lib/IntCodeVM.cs132
1 files changed, 88 insertions, 44 deletions
diff --git a/lib/IntCodeVM.cs b/lib/IntCodeVM.cs
index de2b98a..92cd539 100644
--- a/lib/IntCodeVM.cs
+++ b/lib/IntCodeVM.cs
@@ -1,22 +1,25 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Linq;
 
 namespace aoc2019.lib
 {
     public class IntCodeVM
     {
-        private int i;
-        public List<int> v;
-        public Queue<int> input, output;
-        private readonly List<int> program;
+        private long i;
+        private long relbase;
+        public long[] memory;
+        private readonly long[] program;
+        public Queue<long> input, output;
 
-        public IntCodeVM(List<int> tape)
+        public IntCodeVM(List<long> tape)
         {
             i = 0;
-            program = tape;
-            v = tape;
-            input = new Queue<int>();
-            output = new Queue<int>();
+            relbase = 0;
+            program = tape.ToArray();
+            memory = tape.ToArray();
+            input = new Queue<long>();
+            output = new Queue<long>();
         }
 
         public enum HaltType
@@ -25,66 +28,107 @@ namespace aoc2019.lib
             Waiting
         }
 
-        enum Op : int
-        {
-            ADD = 1, MUL = 2, INPUT = 3, OUTPUT = 4, JMP = 5, JNE = 6, LT = 7, EQ = 8, HALT = 99
-        }
-
         public void Reset()
         {
             i = 0;
-            v = program;
+            relbase = 0;
+            memory = program;
             input.Clear();
             output.Clear();
         }
 
-        public int Result => output.Dequeue();
+        public long Result => output.Dequeue();
 
-        public HaltType Run(params int[] additionalInput)
+        private long MemGet(long addr)
         {
-            foreach (var i in additionalInput) input.Enqueue(i);
-            return Run();
+            return addr < memory.Length ? memory[addr] : 0;
         }
-        public HaltType Run()
+
+        private void MemSet(long addr, long value)
         {
-            while (i < v.Count)
+            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))
             {
-                int Val(int mode, int val) =>
-                    mode == 0 ? v[val] : val;
+                case 0: return MemGet(param);
+                case 1: return param;
+                case 2: return MemGet(relbase + param);
+                default: throw new Exception("invalid parameter mode");
+            }
+        }
 
-                int Val1() => Val(v[i] / 100 % 10, v[i + 1]);
-                int Val2() => Val(v[i] / 1000, v[i + 2]);
+        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");
+            }
+        }
 
-                switch ((Op)(v[i] % 100))
+        public HaltType Run(params long[] additionalInput)
+        {
+            foreach (var s in additionalInput) input.Enqueue(s);
+            return Run();
+        }
+        public HaltType Run()
+        {
+            while (i < memory.Length)
+            {
+                var op = MemGet(i) % 100;
+                switch (op)
                 {
-                    case Op.ADD:
-                        v[v[i + 3]] = Val1() + Val2();
+                    case 1:
+                        Set(3, Get(1) + Get(2));
                         i += 4; break;
-                    case Op.MUL:
-                        v[v[i + 3]] = Val1() * Val2();
+                    case 2:
+                        Set(3, Get(1) * Get(2));
                         i += 4; break;
-                    case Op.INPUT:
+                    case 3:
                         if (!input.Any())
                             return HaltType.Waiting;
-                        v[v[i + 1]] = input.Dequeue();
+                        Set(1, input.Dequeue());
                         i += 2; break;
-                    case Op.OUTPUT:
-                        output.Enqueue(Val1());
+                    case 4:
+                        output.Enqueue(Get(1));
                         i += 2; break;
-                    case Op.JMP:
-                        i = Val1() == 0 ? i + 3 : Val2();
+                    case 5:
+                        i = Get(1) == 0 ? i + 3 : Get(2);
                         break;
-                    case Op.JNE:
-                        i = Val1() != 0 ? i + 3 : Val2();
+                    case 6:
+                        i = Get(1) != 0 ? i + 3 : Get(2);
                         break;
-                    case Op.LT:
-                        v[v[i + 3]] = Val1() < Val2() ? 1 : 0;
+                    case 7:
+                        Set(3, Get(1) < Get(2) ? 1 : 0);
                         i += 4; break;
-                    case Op.EQ:
-                        v[v[i + 3]] = Val1() == Val2() ? 1 : 0;
+                    case 8:
+                        Set(3, Get(1) == Get(2) ? 1 : 0);
                         i += 4; break;
-                    case Op.HALT:
+                    case 9:
+                        relbase += Get(1);
+                        i += 2; break;
+                    case 99:
                         return HaltType.Terminate;
+                    default:
+                        throw new Exception($"unknown op {op} at {i}");
                 }
             }