#!/usr/bin/env ruby input = (ARGV.first.nil? ? DATA : ARGF) .readlines(chomp: true) split = input.index("") gates = input[split+1..] wires = input.join(" ").scan(/([a-z0-9]{3})/).flatten.uniq.map { [_1, nil] }.to_h input[...split] .map { _1.split(": ") } .each { wires[_1] = _2.to_i } flipped = Set.new gates.select { _1.match?(/XOR/) }.each do |gate| in1, op, in2, out = gate.scan(/([a-z0-9]{3}) ([A-Z]+) ([a-z0-9]{3}) -> ([a-z0-9]{3})/).first if op == "XOR" && [in1, in2].map { _1[0] }.sort != %w[x y] if !out.start_with?("z") flipped << out end end end # 45 is a half adder (0..44).each do |num| z = "z#{'%02d' % num}" gate = gates.find { _1.match?(/#{z}$/) } in1, op, in2, out = gate.scan(/([a-z0-9]{3}) ([A-Z]+) ([a-z0-9]{3}) -> ([a-z0-9]{3})/).first if op != "XOR" flipped << z end end gates.select { _1.match?(/AND/) }.each do |gate| in1, op, in2, out = gate.scan(/([a-z0-9]{3}) ([A-Z]+) ([a-z0-9]{3}) -> ([a-z0-9]{3})/).first if [in1, in2].map { _1[0] }.sort == %w[x y] && in1 != "x00" if gates.find { _1.match?(/#{out}/) && _1.match?(/ OR /) }.nil? flipped << out end end end gates.select { _1.match?(/XOR/) }.each do |gate| in1, op, in2, out = gate.scan(/([a-z0-9]{3}) ([A-Z]+) ([a-z0-9]{3}) -> ([a-z0-9]{3})/).first if [in1, in2].map { _1[0] }.sort == %w[x y] && in1 != "x00" && in1 != "y00" if gates.count { _1.match?(/#{out}/) && _1.match?(/ XOR /) } != 2 flipped << out end end end i = 0 until gates.empty? if i >= gates.size i = 0 end gate = gates[i] in1, op, in2, out = gate.scan(/([a-z0-9]{3}) ([A-Z]+) ([a-z0-9]{3}) -> ([a-z0-9]{3})/).first if wires[in1].nil? || wires[in2].nil? i += 1 next end case op when "AND" wires[out] = wires[in1] & wires[in2] when "OR" wires[out] = wires[in1] | wires[in2] when "XOR" wires[out] = wires[in1] ^ wires[in2] end gates.delete(gate) end x = wires.select { _1.match?(/^x/) }.sort_by(&:first).map { _1.last.to_s }.reverse.join xi = x.to_i(2) y = wires.select { _1.match?(/^y/) }.sort_by(&:first).map { _1.last.to_s }.reverse.join yi = y.to_i(2) z = wires.select { _1.match?(/^z/) }.sort_by(&:first).map { _1.last.to_s }.reverse.join zi = z.to_i(2) p zi p flipped.sort.join(",") __END__ x00: 0 x01: 1 x02: 0 x03: 1 x04: 0 x05: 1 y00: 0 y01: 0 y02: 1 y03: 1 y04: 0 y05: 1 x00 AND y00 -> z05 x01 AND y01 -> z02 x02 AND y02 -> z01 x03 AND y03 -> z03 x04 AND y04 -> z04 x05 AND y05 -> z00