require "set" execute = ->(r, op, arg) { delta = case op when :acc { acc: arg, pc: 1 } when :jmp { pc: arg } when :nop { pc: 1 } else fail end 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 } 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 p registers[:acc]