AoC 2015 Puzzle 7

Info

I solved this puzzle. You can find the code here, but I have yet to write a good explanation of the solution here. I will do this shortly, thank you for your patience.

Day 7: Some Assembly Required

This year, Santa brought little Bobby Tables a set of wires and bitwise logic gates! Unfortunately, little Bobby is a little under the recommended age range, and he needs help assembling the circuit.

Each wire has an identifier (some lowercase letters) and can carry a 16-bit signal (a number from 0 to 65535). A signal is provided to each wire by a gate, another wire, or some specific value. Each wire can only get a signal from one source, but can provide its signal to multiple destinations. A gate provides no signal until all of its inputs have a signal.

The included instructions booklet describes how to connect the parts together: x AND y -> z means to connect wires x and y to an AND gate, and then connect its output to wire z.

For example:

  • 123 -> x means that the signal 123 is provided to wire x.
  • x AND y -> z means that the bitwise AND of wire x and wire y is provided to wire z.
  • p LSHIFT 2 -> q means that the value from wire p is left-shifted by 2 and then provided to wire q.
  • NOT e -> f means that the bitwise complement of the value from wire e is provided to wire f.

Other possible gates include OR (bitwise OR) and RSHIFT (right-shift). If, for some reason, you'd like to emulate the circuit instead, almost all programming languages (for example, C, JavaScript, or Python) provide operators for these gates.

For example, here is a simple circuit:

123 -> x 456 -> y x AND y -> d x OR y -> e x LSHIFT 2 -> f y RSHIFT 2 -> g NOT x -> h NOT y -> i

After it is run, these are the signals on the wires:

d: 72 e: 507 f: 492 g: 114 h: 65412 i: 65079 x: 123 y: 456

In little Bobby's kit's instructions booklet (provided as your puzzle input), what signal is ultimately provided to wire a?

Part 2

Now, take the signal you got on wire a, override wire b to that signal, and reset the other wires (including wire a). What new signal is ultimately provided to wire a?

Complete Solution

public enum Op {
    AND((x, y) -> (x & y)), OR((x, y) -> (x | y)), NOT((x, y) -> (~x & 0xffff)), RSHIFT((x, y) -> (x >> y)),
    LSHIFT((x, y) -> (x << y)), ASSIGNX((x, y) -> (x));

    private final IntBinaryOperator func;

    Op(IntBinaryOperator func) {
        this.func = func;
    }
}

@AllArgsConstructor
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
public class Gate {
    @NonNull
    String name;
    Wire left;
    Wire right;
    Wire result;
    Op op;

    public boolean allWiresHaveValue() {
        boolean valid = true;
        if (left != null && left.value == -1)
            valid = false;
        if (right != null && right.value == -1)
            valid = false;
        return valid;
    }

    public void perform() {
        if (op == null)
            return;
        result.value = op.func.applyAsInt(left != null ? left.value : 0, right != null ? right.value : 0);
    }

    public void fire() {
        if (allWiresHaveValue()) {
            perform();
        } else {
            if (left != null && !left.name.equals("")) {
                Gate g = getGate(left.name);
                g.fire();
            }
            if (right != null && !right.name.equals("")) {
                Gate g = getGate(right.name);
                g.fire();
            }
        }
    }
}

@AllArgsConstructor
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
public class Wire {
    @NonNull
    String name;
    int value = -1;
    //Gate gate = null;
}

Map<String, Wire> wires = new HashMap<>();
Map<String, Gate> gates = new HashMap<>();

public Wire getWire(String name) {
    Wire result = isDigit(name) ? new Wire("", Integer.parseInt(name))
        : wires.getOrDefault(name, new Wire(name));
    return result;
}

public Gate getGate(String resultName) {
    Gate result = isDigit(resultName) ? new Gate(resultName)
        : gates.getOrDefault(resultName, new Gate(resultName));
    return result;
}

public boolean isDigit(String input) {
    return Character.isDigit(input.charAt(0));
}

public void storeWires(Wire... wires) {
    for (Wire w : wires) {
        if (w != null) {
            this.wires.put(w.name, w);
        }
    }
}

public void createCircuit(List<String> input) {

    for (String i : input) {
        String[] inst = i.split(" ");

        Wire left = null, right = null, result = null;
        Op op = null;

        switch (inst.length) {
        case 3: // ASSIGN
            left = getWire(inst[0]);
            result = getWire(inst[2]);

            op = Op.ASSIGNX;
            break;
        case 4: // NOT
            left = getWire(inst[1]);
            result = getWire(inst[3]);
            op = Op.NOT;
            break;
        case 5: // operation
            left = getWire(inst[0]);
            right = getWire(inst[2]);
            op = Op.valueOf(inst[1]);
            result = getWire(inst[4]);
            break;
        default:
            System.out.println("Unknown case detected!");
        }

        // Should always return a new Gate
        Gate g = getGate(result.name);

        if (g.op != null) {
            System.out.println("Gate returned an existing gate? " + g);
        }

        g.left = left;
        g.right = right;
        g.result = result;
        g.op = op;

        gates.put(g.result.name, g);
        storeWires(left, right, result);
    }

}

protected int fireWire(String name) {
    Gate g = getGate(name);
    while (g.result.value == -1) {
        g.fire();
    }
    return g.result.value;
}

@Override
public String part1(List<String> input) {
    Day07 app = new Day07();
    app.wires.clear();
    app.gates.clear();
    app.createCircuit(input);
    return String.valueOf(app.fireWire("a"));
}

@Override
public String part2(List<String> input) {
    Day07 app = new Day07();
    app.wires.clear();
    app.gates.clear();
    app.createCircuit(input);
    int value = app.fireWire("a");

    app.wires.clear();
    app.gates.clear();
    app.createCircuit(input);

    Gate b = app.getGate("b");
    b.result.value = value;
    b.op = null;

    return String.valueOf(app.fireWire("a"));
}