156 lines
2.8 KiB
Ruby
156 lines
2.8 KiB
Ruby
class Intcode
|
|
attr_reader :memory, :pc, :stopped, :input, :output
|
|
def initialize(memory)
|
|
@initial_memory = memory
|
|
@memory = memory.clone
|
|
@pc = 0
|
|
@stopped = true
|
|
@input = []
|
|
@output = []
|
|
end
|
|
|
|
def self.for(string)
|
|
new string.split(",").map(&:to_i)
|
|
end
|
|
|
|
def run!
|
|
@stopped = false
|
|
@pc = 0
|
|
step! until stopped
|
|
end
|
|
|
|
def reset!
|
|
@memory = @initial_memory.clone
|
|
@pc = 0
|
|
@output = []
|
|
@input = []
|
|
end
|
|
|
|
def step!
|
|
log "pc: #{pc}, instruction: #{instruction}"
|
|
case opcode
|
|
when 1
|
|
add!
|
|
when 2
|
|
multiply!
|
|
when 3
|
|
read_input!
|
|
when 4
|
|
write_output!
|
|
when 5
|
|
jump_if_true!
|
|
when 6
|
|
jump_if_false!
|
|
when 7
|
|
less_than!
|
|
when 8
|
|
equals!
|
|
when 99
|
|
halt!
|
|
else
|
|
raise "invalid opcode #{opcode} at #{pc}"
|
|
end
|
|
end
|
|
|
|
def parameters
|
|
parameter_modes.each_with_index.map do |mode, index|
|
|
case mode
|
|
when 0
|
|
peek(pc + 1 + index)
|
|
when 1
|
|
pc + 1 + index
|
|
else
|
|
raise "Invalid parameter mode #{mode}"
|
|
end
|
|
end
|
|
end
|
|
|
|
def add!
|
|
log "adding #{peek(parameters[0])} and #{(parameters[1])}"
|
|
poke(parameters[2], peek(parameters[0]) + peek(parameters[1]))
|
|
@pc += 4
|
|
end
|
|
|
|
def multiply!
|
|
log "multiplying #{peek(parameters[0])} and #{peek(parameters[1])}"
|
|
poke(parameters[2], peek(parameters[0]) * peek(parameters[1]))
|
|
@pc += 4
|
|
end
|
|
|
|
def read_input!
|
|
i = input.shift
|
|
log "reading input #{i} into #{parameters[0]}"
|
|
poke(parameters[0], i)
|
|
@pc += 2
|
|
end
|
|
|
|
def write_output!
|
|
o = peek(parameters[0])
|
|
log "outputting #{o} from #{parameters[0]}"
|
|
output.push o
|
|
@pc += 2
|
|
end
|
|
|
|
def jump_if_true!
|
|
predicate = peek(parameters[0])
|
|
log "jumping to #{peek(parameters[1])} if #{predicate}"
|
|
@pc = predicate > 0 ? peek(parameters[1]) : @pc + 3
|
|
end
|
|
|
|
def jump_if_false!
|
|
predicate = peek(parameters[0])
|
|
log "jumping to #{peek(parameters[1])} unless #{predicate}"
|
|
@pc = predicate == 0 ? peek(parameters[1]) : @pc + 3
|
|
end
|
|
|
|
def less_than!
|
|
lt = peek(parameters[0]) < peek(parameters[1])
|
|
poke(parameters[2], lt ? 1 : 0)
|
|
@pc += 4
|
|
end
|
|
|
|
def equals!
|
|
eq = peek(parameters[0]) == peek(parameters[1])
|
|
poke(parameters[2], eq ? 1 : 0)
|
|
@pc += 4
|
|
end
|
|
|
|
def halt!
|
|
log "halting!"
|
|
@stopped = true
|
|
end
|
|
|
|
def instruction
|
|
peek pc
|
|
end
|
|
|
|
def opcode
|
|
instruction % 100
|
|
end
|
|
|
|
def parameter_modes
|
|
[
|
|
instruction / 100 % 10,
|
|
instruction / 1000 % 10,
|
|
instruction / 10000 % 10,
|
|
]
|
|
end
|
|
|
|
def peek(address)
|
|
memory[address]
|
|
end
|
|
|
|
def poke(address, value)
|
|
log "poking #{value} into #{address}"
|
|
memory[address] = value
|
|
end
|
|
|
|
def log(message)
|
|
puts message if logging?
|
|
end
|
|
|
|
def logging?
|
|
false
|
|
end
|
|
end
|