[2019][ruby][2.x] refactor a computer class out of day 2
change syvxurnrqyoltvrnssommlwlzrvpkwtq
commit 79220a1cf605113289286974ba09fe8ad78259e3
author Alpha Chen <alpha@kejadlen.dev>
date
parent mlnlnonx
diff --git a/2019/ruby/computer.rb b/2019/ruby/computer.rb
new file mode 100644
index 0000000..cbec2d2
--- /dev/null
+++ b/2019/ruby/computer.rb
@@ -0,0 +1,63 @@
+OPCODES = {
+  1 => ->(m, a, b, c) { m[c] = m[a] + m[b] },
+  2 => ->(m, a, b, c) { m[c] = m[a] * m[b] },
+  99 => ->(*) { throw :halt },
+}
+
+class Computer
+  def self.from(input)
+    new(input.split(?,).map(&:to_i))
+  end
+
+  def initialize(program)
+    @memory = program
+    @pc = 0
+  end
+
+  def run
+    each.inject(nil) {|_,i| i }
+  end
+
+  def each
+    return enum_for(__method__) unless block_given?
+
+    catch(:halt) do
+      loop do
+        opcode = OPCODES.fetch(@memory.fetch(@pc))
+        @pc += 1
+
+        n = opcode.arity - 1
+        args = (0...n).map {|i| @memory.fetch(@pc + i) }
+        @pc += n
+
+        opcode.call(@memory, *args)
+
+        yield @memory
+      end
+    end
+  end
+end
+
+require "minitest"
+
+class TestComputer < Minitest::Test
+  def test_samples
+    c = Computer.from("1,9,10,3,2,3,11,0,99,30,40,50").each
+
+    assert_equal 70, c.next.fetch(3)
+    assert_equal 3500, c.next.fetch(0)
+  end
+
+  def test_more_samples
+    run = ->(p) { Computer.from(p).run }
+
+    assert_equal 2, run.call("1,0,0,0,99").fetch(0)
+    assert_equal 6, run.call("2,3,0,3,99").fetch(3)
+    assert_equal 9801, run.call("2,4,4,5,99,0").fetch(5)
+    assert_equal 30, run.call("1,1,1,4,99,5,6,0,99").fetch(0)
+  end
+end
+
+if __FILE__ == $0
+  require "minitest/autorun"
+end
diff --git a/2019/ruby/day_02.rb b/2019/ruby/day_02.rb
index 35dc040..014ac98 100644
--- a/2019/ruby/day_02.rb
+++ b/2019/ruby/day_02.rb
@@ -1,47 +1,20 @@
-OPCODES = ARGF.read.split(?,).map(&:to_i)
+require_relative "computer"
 
-# opcodes[1] = 12
-# opcodes[2] = 2
+program = ARGF.read.split(?,).map(&:to_i)
 
-# pc = 0
-# loop do
-#   p opcodes
-#   case opcodes[pc]
-#   when 1
-#     opcodes[opcodes[pc+3]] = opcodes[opcodes[pc+1]] + opcodes[opcodes[pc+2]]
-#     pc += 4
-#   when 2
-#     opcodes[opcodes[pc+3]] = opcodes[opcodes[pc+1]] * opcodes[opcodes[pc+2]]
-#     pc += 4
-#   when 99
-#     puts opcodes[0]
-#     exit
-#   end
-# end
+# modified = program.dup
+# modified[1] = 12
+# modified[2] = 2
+# c = Computer.new(modified)
+# puts c.each.inject(nil) {|_,i| i }.fetch(0)
 
-def run(noun, verb)
-  memory = OPCODES.dup
-  memory[1] = noun
-  memory[2] = verb
-
-  pc = 0
-  loop do
-    case memory[pc]
-    when 1
-      memory[memory[pc+3]] = memory[memory[pc+1]] + memory[memory[pc+2]]
-      pc += 4
-    when 2
-      memory[memory[pc+3]] = memory[memory[pc+1]] * memory[memory[pc+2]]
-      pc += 4
-    when 99
-      return memory[0]
-    end
-  end
-end
-
-(0..99).each do |noun|
-  (0..99).each do |verb|
-    value = run(noun, verb)
-    puts 100 * noun + verb and exit if value == 19690720
-  end
-end
+noun, verb = (0..99).flat_map {|noun| (0..99).map {|verb| [noun, verb] } }
+  .find {|(noun, verb)|
+    modified = program.dup
+    modified[1] = noun
+    modified[2] = verb
+    c = Computer.new(modified)
+    final = c.run
+    final.fetch(0) == 19690720
+  }
+puts 100 * noun + verb