my advent of code solutions
at main 137 lines 3.6 kB view raw
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}