package intcode // VirtualMachine is an IntCode virtual machine. type VirtualMachine struct { ip int modes uint8 opcodes [100]interface{} Memory []int Halted bool Input chan int Output chan int } // NewVirtualMachine creates a new IntCode virtual machine, initialised // to the given slice of memory. func NewVirtualMachine(memory []int) *VirtualMachine { return &VirtualMachine{ ip: 0, Memory: memory, Halted: false, Input: make(chan int, 1), Output: make(chan int, 1), opcodes: [100]interface{}{ 1: AddOpcode, 2: MulOpcode, 3: ReadOpCode, 4: WriteOpCode, 5: JumpIfTrueOpCode, 6: JumpIfFalseOpCode, 7: LessThanOpCode, 8: EqualsOpCode, 99: HaltOpcode, }, } } func (vm *VirtualMachine) arg(pos int) int { mask := uint8(1) << uint8(pos) if vm.modes&mask == mask { return vm.Memory[vm.ip+1+pos] } else { return vm.Memory[vm.Memory[vm.ip+1+pos]] } } // Run repeatedly executes instructions until the VM halts. func (vm *VirtualMachine) Run() { for !vm.Halted { instruction := vm.Memory[vm.ip] opcode := instruction % 100 vm.modes = 0 mask := uint8(1) for i := instruction / 100; i > 0; i /= 10 { if i%10 == 1 { vm.modes = vm.modes | mask } mask = mask << 1 } vm.opcodes[opcode].(OpcodeFunc)(vm) } close(vm.Input) close(vm.Output) } // Reset resets the memory to the given slice, and all other state back to its original value. func (vm *VirtualMachine) Reset(memory []int) { copy(vm.Memory, memory) vm.ip = 0 vm.Halted = false vm.Input = make(chan int, 1) vm.Output = make(chan int, 1) }