You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

vm.go 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package intcode
  2. type parameterMode int
  3. const (
  4. position parameterMode = 0
  5. immediate parameterMode = 1
  6. relative parameterMode = 2
  7. )
  8. // VirtualMachine is an IntCode virtual machine.
  9. type VirtualMachine struct {
  10. ip int
  11. rb int
  12. Memory []int
  13. Halted bool
  14. Input chan int
  15. Output chan int
  16. input []int
  17. inputIndex int
  18. output *int
  19. }
  20. // NewVirtualMachine creates a new IntCode virtual machine, initialised to the given slice of memory.
  21. // The caller is responsible for initialising the VM's I/O channels if required.
  22. func NewVirtualMachine(memory []int) *VirtualMachine {
  23. vm := &VirtualMachine{
  24. ip: 0,
  25. Memory: memory,
  26. Halted: false,
  27. }
  28. return vm
  29. }
  30. // Clone returns a deep copy of this VM, with newly allocated memory and I/O channels.
  31. func (vm *VirtualMachine) Clone() *VirtualMachine {
  32. memory := make([]int, len(vm.Memory))
  33. copy(memory, vm.Memory)
  34. return &VirtualMachine{
  35. ip: vm.ip,
  36. rb: vm.rb,
  37. Memory: memory,
  38. Halted: vm.Halted,
  39. Input: make(chan int, 1),
  40. Output: make(chan int, 1),
  41. input: vm.input,
  42. inputIndex: vm.inputIndex,
  43. output: vm.output,
  44. }
  45. }
  46. // Run repeatedly executes instructions until the VM produces output or halts.
  47. //
  48. // The given values will be provided as input whenever a read opcode is executed;
  49. // any further reads before an output will fallback to reading from the Input
  50. // channel.
  51. //
  52. // If the machine halts instead of outputting a number, this function will return
  53. // nil.
  54. func (vm *VirtualMachine) RunForInput(input ...int) *int {
  55. vm.inputIndex = 0
  56. vm.input = input
  57. vm.Run()
  58. // Reset the halted state so we can carry on running, if required
  59. if vm.output != nil {
  60. vm.Halted = false
  61. }
  62. return vm.output
  63. }
  64. // Run repeatedly executes instructions until the VM halts.
  65. func (vm *VirtualMachine) Run() {
  66. var args [3]*int
  67. for !vm.Halted {
  68. instruction := vm.Memory[vm.ip]
  69. opcode := instruction % 100
  70. param := instruction / 100
  71. for i := 0; i < opcodeArity[opcode]; i++ {
  72. switch parameterMode(param % 10) {
  73. // The argument is the actual value
  74. case immediate:
  75. args[i] = &vm.Memory[vm.ip+1+i]
  76. // The argument is a memory reference
  77. case position:
  78. for vm.Memory[vm.ip+1+i] >= len(vm.Memory) {
  79. vm.Memory = append(vm.Memory, make([]int, 128)...)
  80. }
  81. args[i] = &vm.Memory[vm.Memory[vm.ip+1+i]]
  82. // The argument is a memory reference offset by the relative base
  83. case relative:
  84. for vm.rb+vm.Memory[vm.ip+1+i] >= len(vm.Memory) {
  85. vm.Memory = append(vm.Memory, make([]int, 128)...)
  86. }
  87. args[i] = &vm.Memory[vm.rb+vm.Memory[vm.ip+1+i]]
  88. }
  89. param /= 10
  90. }
  91. opcodes[opcode](vm, args[0], args[1], args[2])
  92. }
  93. if vm.Output != nil {
  94. close(vm.Output)
  95. }
  96. }
  97. // Reset resets the memory to the given slice, and all other state back to its original value.
  98. func (vm *VirtualMachine) Reset(memory []int) {
  99. copy(vm.Memory, memory)
  100. // We may previously have expanded our own memory, reset that to zero.
  101. for i := len(memory); i < len(vm.Memory)-1; i++ {
  102. vm.Memory[i] = 0
  103. }
  104. vm.ip = 0
  105. vm.rb = 0
  106. vm.input = nil
  107. vm.inputIndex = 0
  108. vm.output = nil
  109. vm.Halted = false
  110. }