123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- package intcode
-
- type parameterMode int
-
- const (
- position parameterMode = 0
- immediate parameterMode = 1
- relative parameterMode = 2
- )
-
- // VirtualMachine is an IntCode virtual machine.
- type VirtualMachine struct {
- ip int
- rb int
- Memory []int
- Halted bool
- Input chan int
- Output chan int
- input []int
- inputIndex int
- output *int
- }
-
- // NewVirtualMachine creates a new IntCode virtual machine, initialised to the given slice of memory.
- // The caller is responsible for initialising the VM's I/O channels if required.
- func NewVirtualMachine(memory []int) *VirtualMachine {
- vm := &VirtualMachine{
- ip: 0,
- Memory: memory,
- Halted: false,
- }
-
- return vm
- }
-
- // Clone returns a deep copy of this VM, with newly allocated memory and I/O channels.
- func (vm *VirtualMachine) Clone() *VirtualMachine {
- memory := make([]int, len(vm.Memory))
- copy(memory, vm.Memory)
-
- return &VirtualMachine{
- ip: vm.ip,
- rb: vm.rb,
- Memory: memory,
- Halted: vm.Halted,
- Input: make(chan int, 1),
- Output: make(chan int, 1),
- input: vm.input,
- inputIndex: vm.inputIndex,
- output: vm.output,
- }
- }
-
- // Run repeatedly executes instructions until the VM produces output or halts.
- //
- // The given values will be provided as input whenever a read opcode is executed;
- // any further reads before an output will fallback to reading from the Input
- // channel.
- //
- // If the machine halts instead of outputting a number, this function will return
- // nil.
- func (vm *VirtualMachine) RunForInput(input ...int) *int {
- vm.inputIndex = 0
- vm.input = input
- vm.Run()
-
- // Reset the halted state so we can carry on running, if required
- if vm.output != nil {
- vm.Halted = false
- }
-
- return vm.output
- }
-
- // Run repeatedly executes instructions until the VM halts.
- func (vm *VirtualMachine) Run() {
- var args [3]*int
- for !vm.Halted {
- instruction := vm.Memory[vm.ip]
- opcode := instruction % 100
-
- param := instruction / 100
- for i := 0; i < opcodeArity[opcode]; i++ {
-
- switch parameterMode(param % 10) {
-
- // The argument is the actual value
- case immediate:
- args[i] = &vm.Memory[vm.ip+1+i]
-
- // The argument is a memory reference
- case position:
- for vm.Memory[vm.ip+1+i] >= len(vm.Memory) {
- vm.Memory = append(vm.Memory, make([]int, 128)...)
- }
- args[i] = &vm.Memory[vm.Memory[vm.ip+1+i]]
-
- // The argument is a memory reference offset by the relative base
- case relative:
- for vm.rb+vm.Memory[vm.ip+1+i] >= len(vm.Memory) {
- vm.Memory = append(vm.Memory, make([]int, 128)...)
- }
- args[i] = &vm.Memory[vm.rb+vm.Memory[vm.ip+1+i]]
-
- }
-
- param /= 10
- }
-
- opcodes[opcode](vm, args[0], args[1], args[2])
- }
- if vm.Output != nil {
- 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)
-
- // We may previously have expanded our own memory, reset that to zero.
- for i := len(memory); i < len(vm.Memory)-1; i++ {
- vm.Memory[i] = 0
- }
-
- vm.ip = 0
- vm.rb = 0
- vm.input = nil
- vm.inputIndex = 0
- vm.output = nil
- vm.Halted = false
- }
|