registers, program = DATA.read.split("\n\n") def run(a, b, c, program) return enum_for(__method__, a, b, c, program) unless block_given? combo = ->(operand) { case operand when 0, 1, 2, 3 then operand when 4 then a when 5 then b when 6 then c else fail end } ip = 0 while (0...program.size).cover?(ip) opcode = program.fetch(ip) operand = program.fetch(ip+1) case opcode when 0 a = (a / 2**combo[operand]).to_i ip += 2 when 1 b = b ^ operand ip += 2 when 2 b = combo[operand] % 8 ip += 2 when 3 if a.zero? ip += 2 else ip = operand end when 4 b = b ^ c ip += 2 when 5 yield combo[operand] % 8 ip += 2 when 6 b = (a / 2**combo[operand]).to_i ip += 2 when 7 c = (a / 2**combo[operand]).to_i ip += 2 else fail end end end a, b, c = registers.scan(/\d+/).map(&:to_i) program = program.scan(/\d+/).map(&:to_i) # puts (211000000..).find {|a| # pp a if a % 100_000 == 0 # program.each.lazy.zip(run(a, b, c, program)).all? { _1 == _2 } # } def run(a) return enum_for(__method__, a) unless block_given? # 14: 3 0 # jnz until a.zero? # 0: 2 4 # bst # 2: 1 2 # bxl # b = ((a & 0b111) ^ 0b010) # 4: 7 5 # cdv # c = a >> ((a & 0b111) ^ 0b010) # 8: 1 7 # bxl # 10: 4 1 # bxc # b = ((a & 0b111) ^ 0b010) ^ c ^ 0b111 # b = ((a & 0b111) ^ 0b010) ^ (a >> ((a & 0b111) ^ 0b010)) ^ 0b111 b = (a & 0b111) ^ 0b101 ^ (a >> ((a & 0b111) ^ 0b010)) # 12: 5 5 # out yield b & 0b111 # 6: 0 3 # adv a >>= 3 end end def run(a) return enum_for(__method__, a) unless block_given? a.to_s(8).chars.map(&:to_i).reverse.map.with_index {|x,i| yield (x ^ 0b101 ^ (a >> 3*i >> (x ^ 0b010))) & 0b111 } end pp run(27334280).to_a.join(?,) def solve(a, target) return [a] if a.size == 16 (0..7) .map { a + [_1] } .select {|a| target.reverse[0,a.size].zip( run(a.join.ljust(14, ?0).to_i(8)).to_a.reverse ).all? { _1 == _2 } }.flat_map { solve(_1, target) } end a = solve([], program).map(&:join).map { _1.to_i(8) }.min pp a pp run(a).to_a == program __END__ Register A: 27334280 Register B: 0 Register C: 0 Program: 2,4,1,2,7,5,0,3,1,7,4,1,5,5,3,0