aoc2019/lib/IntCodeVM.cs

139 lines
3.9 KiB
C#
Raw Normal View History

2019-12-09 08:58:22 +00:00
using System;
using System.Collections.Generic;
2019-12-07 08:57:17 +00:00
using System.Linq;
namespace aoc2019.lib
{
public class IntCodeVM
{
2019-12-09 08:58:22 +00:00
private long i;
private long relbase;
public long[] memory;
private readonly long[] program;
public Queue<long> input, output;
2019-12-07 08:57:17 +00:00
2019-12-09 19:10:13 +00:00
public IntCodeVM(string tape)
2019-12-07 08:57:17 +00:00
{
i = 0;
2019-12-09 08:58:22 +00:00
relbase = 0;
2019-12-09 19:10:13 +00:00
program = tape.Split(',').Select(long.Parse).ToArray();
memory = program;
2019-12-09 08:58:22 +00:00
input = new Queue<long>();
output = new Queue<long>();
2019-12-07 08:57:17 +00:00
}
public enum HaltType
{
Terminate,
Waiting
}
public void Reset()
{
i = 0;
2019-12-09 08:58:22 +00:00
relbase = 0;
memory = program;
2019-12-07 08:57:17 +00:00
input.Clear();
output.Clear();
}
2019-12-09 08:58:22 +00:00
public long Result => output.Dequeue();
2019-12-07 08:57:17 +00:00
2019-12-09 08:58:22 +00:00
private long MemGet(long addr)
2019-12-07 08:57:17 +00:00
{
2019-12-09 08:58:22 +00:00
return addr < memory.Length ? memory[addr] : 0;
2019-12-07 08:57:17 +00:00
}
2019-12-09 08:58:22 +00:00
private void MemSet(long addr, long value)
2019-12-07 08:57:17 +00:00
{
2019-12-09 08:58:22 +00:00
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))
2019-12-07 08:57:17 +00:00
{
2019-12-09 08:58:22 +00:00
case 0: return MemGet(param);
case 1: return param;
case 2: return MemGet(relbase + param);
default: throw new Exception("invalid parameter mode");
}
}
2019-12-07 08:57:17 +00:00
2019-12-09 08:58:22 +00:00
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");
}
}
2019-12-07 08:57:17 +00:00
2019-12-09 08:58:22 +00:00
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)
2019-12-07 08:57:17 +00:00
{
2019-12-09 08:58:22 +00:00
case 1:
Set(3, Get(1) + Get(2));
2019-12-07 08:57:17 +00:00
i += 4; break;
2019-12-09 08:58:22 +00:00
case 2:
Set(3, Get(1) * Get(2));
2019-12-07 08:57:17 +00:00
i += 4; break;
2019-12-09 08:58:22 +00:00
case 3:
2019-12-07 08:57:17 +00:00
if (!input.Any())
return HaltType.Waiting;
2019-12-09 08:58:22 +00:00
Set(1, input.Dequeue());
2019-12-07 08:57:17 +00:00
i += 2; break;
2019-12-09 08:58:22 +00:00
case 4:
output.Enqueue(Get(1));
2019-12-07 08:57:17 +00:00
i += 2; break;
2019-12-09 08:58:22 +00:00
case 5:
i = Get(1) == 0 ? i + 3 : Get(2);
2019-12-07 08:57:17 +00:00
break;
2019-12-09 08:58:22 +00:00
case 6:
i = Get(1) != 0 ? i + 3 : Get(2);
2019-12-07 08:57:17 +00:00
break;
2019-12-09 08:58:22 +00:00
case 7:
Set(3, Get(1) < Get(2) ? 1 : 0);
2019-12-07 08:57:17 +00:00
i += 4; break;
2019-12-09 08:58:22 +00:00
case 8:
Set(3, Get(1) == Get(2) ? 1 : 0);
2019-12-07 08:57:17 +00:00
i += 4; break;
2019-12-09 08:58:22 +00:00
case 9:
relbase += Get(1);
i += 2; break;
case 99:
2019-12-07 08:57:17 +00:00
return HaltType.Terminate;
2019-12-09 08:58:22 +00:00
default:
throw new Exception($"unknown op {op} at {i}");
2019-12-07 08:57:17 +00:00
}
}
return HaltType.Terminate;
}
}
}