#!/usr/bin/env ruby require "debug" DEBUG = false input = (ARGV.first.nil? ? DATA : ARGF) .readlines(chomp: true) split = input.index("") registers = input[...split] .map { _1.split(": ") } .map(&:last) .map(&:to_i) .zip(%i[A B C]) .map(&:reverse) .to_h program = input[split+1] .split(" ") .last .split(",") .map(&:to_i) def execute(registers, program) stdout, commands = Array.new, Array.new operands = [ ->() { 0 }, ->() { 1 }, ->() { 2 }, ->() { 3 }, ->() { registers[:A] }, ->() { registers[:B] }, ->() { registers[:C] }, ->() { :noop }, ] ic = 0 while ic < program.size opcode = program[ic] literal_operand = program[ic+1] combo_operand = operands[literal_operand].call ic += 2 case opcode when 0 numerator = registers[:A] denominator = 2 ** combo_operand registers[:A] = numerator / denominator commands << [:adv, numerator, denominator] when 1 registers[:B] = registers[:B] ^ literal_operand commands << [:bxl, literal_operand] when 2 registers[:B] = combo_operand % 8 commands << [:bst, combo_operand % 8] when 3 if registers[:A] == 0 commands << [:noop] else ic = literal_operand commands << [:jnz, literal_operand] end when 4 registers[:B] = registers[:B] ^ registers[:C] commands << [:bxc, registers[:B], registers[:C]] when 5 stdout << combo_operand % 8 commands << [:out, combo_operand % 8] when 6 numerator = registers[:A] denominator = 2 ** combo_operand registers[:B] = numerator / denominator commands << [:bdv, numerator, denominator] when 7 numerator = registers[:A] denominator = 2 ** combo_operand registers[:C] = numerator / denominator commands << [:cdv, numerator, denominator] end end if DEBUG commands.each { puts _1.join(" ") } end [registers, program, stdout.join(",")] end _, _, part_1 = execute(registers.dup, program) p part_1 def reverse_engineer(program, slot, i) candidates = (0..8).map do |i| [i, execute({A: i, B: 0, C: 0}, program).last.split(",").map(&:to_i) ] end.select { _1.last == program[slot..] } end part_2 = 0 (program.size - 1).downto(0) do |slot| candidates = [] (0..8).each do |i| candidate = part_2 + i _, _, output = execute({A: candidate, B: 0, C: 0}, program) new_program = output.split(",").map(&:to_i) if new_program == program[slot..] candidates << candidate unless [301,2566855701950, 20534845615581, 20534845615573, 20534845615583].include?(candidate) part_2 = candidate end end end #p candidates if slot == 0 p candidates.first end part_2 <<= 3 end __END__ Register A: 2024 Register B: 0 Register C: 0 Program: 0,3,5,4,3,0