[2020][ruby][8.x] refactor
change nsmrtmvlwunzmtwnnvmvwzrrmrystlwn
commit cf1fe48bb97b5241e7ed016addd42712ac6878de
author Alpha Chen <alpha@kejadlen.dev>
date
parent qutoyouu
diff --git a/2020/ruby/day_08.rb b/2020/ruby/day_08.rb
index 494ac94..a52e3df 100644
--- a/2020/ruby/day_08.rb
+++ b/2020/ruby/day_08.rb
@@ -1,55 +1,55 @@
 require "set"
 
-class Program
-  attr_reader :accumulator
+execute = ->(r, op, arg) {
+  delta = case op
+          when :acc
+            { acc: arg, pc: 1 }
+          when :jmp
+            { pc: arg }
+          when :nop
+            { pc: 1 }
+          else
+            fail
+          end
 
-  def initialize(instructions)
-    @instructions = instructions
-    @accumulator = 0
-    @pc = 0
+  r.merge(delta) {|_, old, new| old + new }
+}
+
+# part 1
+seen = Set.new
+detect_loop = ->(r, op, arg) {
+  throw :halt if seen.include?(r[:pc])
+  seen << r[:pc]
+
+  execute[r, op, arg]
+}
+
+instructions = ARGF.read.scan(/(\w+) ([-+]\d+)/).map {|op,arg| [op.to_sym, arg.to_i] }
+registers = Hash.new(0)
+
+swaps = { nop: :jmp, jmp: :nop }
+candidates = instructions.filter_map.with_index {|(op, arg), i|
+  swap = swaps[op]
+  if swap.nil?
+    nil
+  else
+    [i, [swap, arg]]
   end
+}.map {|i, swap|
+  candidate = instructions.clone
+  candidate[i] = swap
+  candidate
+}
 
-  def run
-    seen = Set.new
-
-    loop do
-      break if @pc == @instructions.size
-
-      fail if seen.include?(@pc)
-      seen << @pc
-
-      op, arg = @instructions.fetch(@pc)
-      case op
-      when :acc
-        @accumulator += arg
-        @pc += 1
-      when :jmp
-        @pc += arg
-      when :nop
-        @pc += 1
-      else
-        fail
-      end
+candidates.each do |candidate|
+  seen = Set.new
+  registers = Hash.new(0)
+  catch(:halt) do
+    while instruction = candidate[registers[:pc]]
+      registers = detect_loop[registers, *instruction]
     end
   end
+  break if registers[:pc] == candidate.size
 end
 
-instructions = ARGF.read.scan(/(\w+) ([-+]\d+)/).map {|op,arg| [op.to_sym, arg.to_i] }
-uncorrupt = { nop: :jmp, jmp: :nop }
-(0...instructions.size).select {|i|
-  op, _ = instructions.fetch(i)
-  uncorrupt.has_key?(op)
-}.map {|i|
-  attempt = instructions.clone
-  instruction = attempt.fetch(i)
-  attempt[i] = [uncorrupt.fetch(instruction[0]), instruction[1]]
-  attempt
-}.each do |i|
-  program = Program.new(i)
-  begin
-    program.run
-  rescue
-    next
-  end
-  puts "accumulator: #{program.accumulator}"
-end
+p registers[:acc]