aoc_omni/ruby/2019/lib/intcode.rb
2025-12-06 10:19:08 -05:00

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