|
| 1 | +setup, outputs = INPUT.split("\n\n") |
| 2 | + |
| 3 | +WIRE_MATCHER = /\A(?<wire>.+): (?<value>0|1)\z/ |
| 4 | +WIRES = setup.split("\n").each_with_object({}) do |line, wires| |
| 5 | + match = line.match(WIRE_MATCHER) |
| 6 | + wires[match[:wire]] = match[:value].to_i(2) |
| 7 | +end |
| 8 | + |
| 9 | +GATE_MATCHER = /\A(?<w1>.+) (?<gate>.+) (?<w2>.+) -> (?<output>.+)\z/ |
| 10 | +GATES = outputs.split("\n").each_with_object({}) do |line, gates| |
| 11 | + match = line.match(GATE_MATCHER) |
| 12 | + gates[match[:output]] = [match[:w1], match[:w2], match[:gate]] |
| 13 | +end |
| 14 | + |
| 15 | +def value_for(wire, wires, gates) |
| 16 | + wires[wire] ||= begin |
| 17 | + w1, w2, gate = gates[wire] |
| 18 | + |
| 19 | + v1 = value_for(w1, wires, gates) |
| 20 | + v2 = value_for(w2, wires, gates) |
| 21 | + |
| 22 | + case gate |
| 23 | + when "XOR" then v1 ^ v2 |
| 24 | + when "AND" then v1 & v2 |
| 25 | + when "OR" then v1 | v2 |
| 26 | + end |
| 27 | + end |
| 28 | +end |
| 29 | + |
| 30 | +def value_on(wires, prefix) |
| 31 | + keys = wires.keys.select { |w| w.start_with?(prefix) } |
| 32 | + keys.sort.reverse.map { |k| wires[k] }.join.to_i(2) |
| 33 | +end |
| 34 | + |
| 35 | +def value(wires, gates) |
| 36 | + wires = wires.dup |
| 37 | + |
| 38 | + gates.keys.each do |output| |
| 39 | + value_for(output, wires, gates) |
| 40 | + end |
| 41 | + |
| 42 | + value_on(wires, "z") |
| 43 | +end |
| 44 | + |
| 45 | +solve!("The final value on the Z wires is:", value(WIRES, GATES)) |
| 46 | + |
| 47 | +def used_for?(wire, gates, &block) |
| 48 | + gates.any? { |_, (w1, w2, g)| (w1 == wire || w2 == wire) && block.call(g) } |
| 49 | +end |
| 50 | + |
| 51 | +HIGHEST_OUTPUT_BIT = GATES.keys.select { |k| k.start_with?("z") }.max |
| 52 | + |
| 53 | +invalid_outputs = GATES.each_with_object([]) do |(output, (w1, w2, gate)), invalid| |
| 54 | + invalid_operations = [ |
| 55 | + output.start_with?("z") && gate != "XOR" && output != HIGHEST_OUTPUT_BIT, |
| 56 | + gate == "AND" && w1 != "x00" && w2 != "x00" && used_for?(output, GATES) { |op| op != "OR" }, |
| 57 | + gate == "XOR" && [output, w1, w2].none? { |w| w.start_with?("x", "y", "z") }, |
| 58 | + gate == "XOR" && used_for?(output, GATES) { |op| op == "OR" } |
| 59 | + ] |
| 60 | + |
| 61 | + invalid << output if invalid_operations.any? |
| 62 | +end |
| 63 | + |
| 64 | +solve!("The swapped outputs are:", invalid_outputs.sort.join(",")) |
0 commit comments