my advent of code solutions
1namespace Solutions._2019;
2
3public class IntCodeVM
4{
5 public enum HaltType { Terminate, Waiting }
6
7 private readonly Queue<long> _input = new();
8 private readonly long[] _program;
9 public readonly Queue<long> Output = new();
10 private long _i;
11 private long _relativeBase;
12 public long[] Memory;
13
14 public IntCodeVM(string tape)
15 {
16 _i = 0;
17 _relativeBase = 0;
18 _program = tape.Split(',').Select(long.Parse).ToArray();
19 Memory = _program;
20 }
21
22 public long Result => Output.Dequeue();
23
24 public void Reset()
25 {
26 _i = 0;
27 _relativeBase = 0;
28 Memory = _program;
29 _input.Clear();
30 Output.Clear();
31 }
32
33 public void AddInput(long value) => _input.Enqueue(value);
34
35 private long MemGet(long addr) => addr < Memory.Length ? Memory[addr] : 0;
36
37 private void MemSet(long addr, long value)
38 {
39 if (addr < 0) addr = 0;
40 if (addr >= Memory.Length) Array.Resize(ref Memory, (int)addr + 1);
41 Memory[addr] = value;
42 }
43
44 private long Mode(long idx)
45 {
46 var mode = MemGet(_i) / 100;
47 for (var s = 1; s < idx; s++) mode /= 10;
48 return mode % 10;
49 }
50
51 private long Get(long idx)
52 {
53 var param = MemGet(_i + idx);
54 return Mode(idx) switch
55 {
56 0 => MemGet(param),
57 1 => param,
58 2 => MemGet(_relativeBase + param),
59 _ => throw new("invalid parameter mode"),
60 };
61 }
62
63 private void Set(long idx, long val)
64 {
65 var param = MemGet(_i + idx);
66 switch (Mode(idx))
67 {
68 case 0:
69 MemSet(param, val);
70 break;
71 case 1: throw new("cannot set in immediate mode");
72 case 2:
73 MemSet(_relativeBase + param, val);
74 break;
75 default: throw new("invalid parameter mode");
76 }
77 }
78
79 public HaltType Run(params long[] additionalInput)
80 {
81 foreach (var s in additionalInput) AddInput(s);
82 return Run();
83 }
84
85 public HaltType Run()
86 {
87 while (_i < Memory.Length)
88 {
89 var op = MemGet(_i) % 100;
90 switch (op)
91 {
92 case 1:
93 Set(3, Get(1) + Get(2));
94 _i += 4;
95 break;
96 case 2:
97 Set(3, Get(1) * Get(2));
98 _i += 4;
99 break;
100 case 3:
101 if (_input.Count == 0)
102 return HaltType.Waiting;
103 Set(1, _input.Dequeue());
104 _i += 2;
105 break;
106 case 4:
107 Output.Enqueue(Get(1));
108 _i += 2;
109 break;
110 case 5:
111 _i = Get(1) == 0 ? _i + 3 : Get(2);
112 break;
113 case 6:
114 _i = Get(1) != 0 ? _i + 3 : Get(2);
115 break;
116 case 7:
117 Set(3, Get(1) < Get(2) ? 1 : 0);
118 _i += 4;
119 break;
120 case 8:
121 Set(3, Get(1) == Get(2) ? 1 : 0);
122 _i += 4;
123 break;
124 case 9:
125 _relativeBase += Get(1);
126 _i += 2;
127 break;
128 case 99:
129 return HaltType.Terminate;
130 default:
131 throw new($"unknown op {op} at {_i}");
132 }
133 }
134
135 return HaltType.Terminate;
136 }
137}