diff --git a/ruby/2019/2/bin/problem b/ruby/2019/2/bin/problem new file mode 100644 index 0000000..2591bf0 --- /dev/null +++ b/ruby/2019/2/bin/problem @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby + +require "intcode" + +intcode = Intcode.for STDIN.read.chomp +intcode.poke(1, 12) +intcode.poke(2, 2) +intcode.run! +puts "Part 1: #{intcode.peek(0)}" + +noun_verb = nil +0.upto(99).each do |noun| + 0.upto(99).each do |verb| + intcode.reset! + intcode.poke(1, noun) + intcode.poke(2, verb) + intcode.run! + noun_verb = noun * 100 + verb if intcode.peek(0) == 19690720 + break if noun_verb + end + break if noun_verb +end + +puts "Part 2: #{noun_verb}" diff --git a/ruby/2019/2/lib/intcode.rb b/ruby/2019/2/lib/intcode.rb new file mode 100644 index 0000000..4c5e1b4 --- /dev/null +++ b/ruby/2019/2/lib/intcode.rb @@ -0,0 +1,79 @@ +class Intcode + attr_reader :memory, :pc, :stopped + def initialize(memory) + @initial_memory = memory + @memory = memory.clone + @pc = 0 + @stopped = true + 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 + end + + def step! + case instruction + when 1 + add! + when 2 + multiply! + when 99 + halt! + else + raise "invalid instuction #{instruction} at #{pc}" + end + end + + def add! + v1 = peek(peek(pc + 1)) + v2 = peek(peek(pc + 2)) + log "adding #{v1} and #{v2}" + poke(peek(pc + 3), v1 + v2) + @pc += 4 + end + + def multiply! + v1 = peek(peek(pc + 1)) + v2 = peek(peek(pc + 2)) + log "multiplying #{v1} and #{v2}" + poke(peek(pc + 3), v1 * v2) + @pc += 4 + end + + def halt! + log "halting!" + @stopped = true + end + + def instruction + peek pc + 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 diff --git a/ruby/2019/2/main.rb b/ruby/2019/2/main.rb deleted file mode 100644 index eda0365..0000000 --- a/ruby/2019/2/main.rb +++ /dev/null @@ -1,92 +0,0 @@ -class Computer - attr_reader :data - def initialize(data) - @data = data - @program_counter = 0 - @halted = true - end - - def run - @halted = false - while !@halted - instruction_range = data[@program_counter..(@program_counter + 3)] - opcode = OpCode.for(self, *instruction_range) - opcode.execute - @program_counter += 4 - end - end - - def read(index) - data[index] - end - - def write(index, value) - data[index] = value - end - - def halt - @halted = true - end - - def self.from_string(string) - new(string.split(",").map(&:to_i)) - end - - def self.from_file(file_path) - from_string(File.read file_path) - end -end - -class OpCode - attr_reader :computer - def initialize(computer) - @computer = computer - end - - def self.for(computer, *args) - case args[0] - when 1 - OpCode1.new(computer, *args[1..]) - when 2 - OpCode2.new(computer, *args[1..]) - when 99 - OpCode99.new(computer) - end - end -end - -class OpCode1 < OpCode - attr_reader :destination, :first_position, :second_position - def initialize(computer, arg1, arg2, arg3) - super(computer) - @first_position = arg1 - @second_position = arg2 - @destination = arg3 - end - - def value_to_write - computer.read(first_position) + computer.read(second_position) - end - - def execute - computer.write destination, value_to_write - end -end - -class OpCode2 < OpCode1 - def value_to_write - computer.read(first_position) * computer.read(second_position) - end -end - -class OpCode99 < OpCode - def execute - computer.halt - end -end - -c = Computer.from_file("../data/2019/2/input.txt") -c.write(1, 12) -c.write(2, 2) -c.run -puts "Part 1: #{c.read(0)}" diff --git a/ruby/2019/2/test/test_intcode.rb b/ruby/2019/2/test/test_intcode.rb new file mode 100644 index 0000000..085261d --- /dev/null +++ b/ruby/2019/2/test/test_intcode.rb @@ -0,0 +1,21 @@ +require "minitest/autorun" +require "intcode" + +class TestIntcode < Minitest::Test + def test_addition + i = Intcode.for("1,0,0,3,99") + i.run! + assert_equal(1, i.peek(0)) + assert_equal(0, i.peek(1)) + assert_equal(0, i.peek(2)) + assert_equal(2, i.peek(3)) + assert_equal(99, i.peek(4)) + end + + def test_multiplication + i = Intcode.for("1,9,10,3,2,3,11,0,99,30,40,50") + i.run! + assert_equal(3500, i.peek(0)) + assert_equal(70, i.peek(3)) + end +end