require "./common.cr" lines = File.read("day16_input").split "\n" lines.pop count = 0 end_index = 0 start_index = 0 lines.each_with_index do |line, i| if line.size == 0 count += 1 else count = 0 end if count == 3 end_index = i - 3 start_index = i + 1 end end registers = Array(Int32).new(4, 0) OPCODES = { "addr" => ->(regs : Array(Int32), a : Int32, b : Int32) { regs[a] + regs[b] }, "addi" => ->(regs : Array(Int32), a : Int32, b : Int32) { regs[a] + b }, "mulr" => ->(regs : Array(Int32), a : Int32, b : Int32) { regs[a] * regs[b] }, "muli" => ->(regs : Array(Int32), a : Int32, b : Int32) { regs[a] * b }, "banr" => ->(regs : Array(Int32), a : Int32, b : Int32) { regs[a] & regs[b] }, "bani" => ->(regs : Array(Int32), a : Int32, b : Int32) { regs[a] & b }, "borr" => ->(regs : Array(Int32), a : Int32, b : Int32) { regs[a] | regs[b] }, "bori" => ->(regs : Array(Int32), a : Int32, b : Int32) { regs[a] | b }, "setr" => ->(regs : Array(Int32), a : Int32, b : Int32) { regs[a] }, "seti" => ->(regs : Array(Int32), a : Int32, b : Int32) { a }, "gtir" => ->(regs : Array(Int32), a : Int32, b : Int32) { (a > regs[b]) ? 1 : 0 }, "gtri" => ->(regs : Array(Int32), a : Int32, b : Int32) { (regs[a] > b) ? 1 : 0 }, "gtrr" => ->(regs : Array(Int32), a : Int32, b : Int32) { (regs[a] > regs[b]) ? 1 : 0 }, "eqir" => ->(regs : Array(Int32), a : Int32, b : Int32) { (a == regs[b]) ? 1 : 0 }, "eqri" => ->(regs : Array(Int32), a : Int32, b : Int32) { (regs[a] == b) ? 1 : 0 }, "eqrr" => ->(regs : Array(Int32), a : Int32, b : Int32) { (regs[a] == regs[b]) ? 1 : 0 }, } def find_possible(instr, before, after) OPCODES.select do |name, code| sample = before.dup sample[instr[3]] = code.call(sample, instr[1], instr[2]) sample == after end end ARRAY_REGEX = /[^:]+:\s*\[([^\]]*)\]/ class String def extract_array self.match(ARRAY_REGEX).not_nil![1].split(", ").map &.to_i32 end end index = 0 count_more_three = 0 possibilities = Array(Set(String)).new(16) { Set(String).new } while index < end_index start_array = lines[index].extract_array end_array = lines[index + 2].extract_array instruction = lines[index + 1].split(" ").map &.to_i32 possible = find_possible(instruction, start_array, end_array) count_more_three += 1 if possible.keys.size >= 3 opcode = instruction[0] old_set = possibilities[opcode] if old_set.empty? old_set.concat possible.keys else old_set.each do |it| old_set.delete it unless possible.has_key? it end end index += 4 end puts count_more_three sure_possibilities = {} of Int32 => Proc(Array(Int32), Int32, Int32, Int32) while sure_possibilities.keys.size != 16 possibilities.each_with_index do |set, index| next unless set.size == 1 first = set.first sure_possibilities[index] = OPCODES[first] possibilities.each &.delete(first) end end registers = [0, 0, 0, 0] index = start_index while index < lines.size instruction = lines[index].split(" ").map &.to_i32 registers[instruction[3]] = sure_possibilities[instruction[0]].call(registers, instruction[1], instruction[2]) index += 1 end puts registers[0] # opcodes = {} of Int32 => Proc(Int32, Int32, Int32) # 0 => ->(x : Int32, y : Int32) { x + y }