package day24 import ( "adventofcode2024/utils" "fmt" "regexp" "sort" "strings" ) type Network struct { gates []*Gate wires []*Wire } type State int const ( NA State = iota - 1 ZERO ONE ) type Wire struct { name string state State } type Operation int const ( AND Operation = iota OR XOR ) type Gate struct { in [2]*Wire out *Wire op Operation } func Part1(input string) int { network := &Network{} network.wires = make([]*Wire, 0) data := strings.Split(input, "\n\n") network.AddWires(data[0]) network.AddGates(data[1]) for { if network.OutputKnown() { break } for _, gate := range network.gates { // fmt.Printf("%d: %v\n", i, gate) if gate.out.state == NA && gate.in[0].state != NA && gate.in[1].state != NA { switch gate.op { case AND: gate.out.state = And(gate.in[0].state, gate.in[1].state) case OR: gate.out.state = Or(gate.in[0].state, gate.in[1].state) case XOR: gate.out.state = Xor(gate.in[0].state, gate.in[1].state) } } } } return int(network.Output()) } func Part2(input string) int64 { data := strings.Split(input, "\n\n") inputs := strings.Split(data[0], "\n") num_inputs := len(inputs) / 2 fmt.Println(num_inputs) var x_value int64 var y_value int64 var z_value int64 y_value = 0 for i := 0; i < num_inputs; i++ { // y_value = 1 << i x_value = 1 << i network := &Network{} network.wires = make([]*Wire, 0) network.AddWires2("x", num_inputs, x_value) network.AddWires2("y", num_inputs, y_value) network.AddGates(data[1]) // for i, gate := range network.gates { // fmt.Printf("%d: %v\n", i, gate) // } network.SwapOutputs("z07", "rts") network.SwapOutputs("z12", "jpj") network.SwapOutputs("z26", "kgj") network.SwapOutputs("vvw", "chv") // for i, gate := range network.gates { // fmt.Printf("%d: %v\n", i, gate) // } for { if network.OutputKnown() { break } for _, gate := range network.gates { // fmt.Printf("%d: %v\n", i, gate) if gate.out.state == NA && gate.in[0].state != NA && gate.in[1].state != NA { switch gate.op { case AND: gate.out.state = And(gate.in[0].state, gate.in[1].state) case OR: gate.out.state = Or(gate.in[0].state, gate.in[1].state) case XOR: gate.out.state = Xor(gate.in[0].state, gate.in[1].state) } } } } z_value = network.Output() if x_value + y_value != z_value { fmt.Printf("(%d):\t x:%4d\t: y:%4d z:%4d\n", i, x_value, y_value, z_value) } } ans := []string{"z07", "rts", "z12", "jpj", "z26", "kgj", "vvw", "chv"} sort.Strings(ans) fmt.Println(strings.Join(ans, ",")) return z_value } func (n *Network) AddWires(data string) { for _, line := range strings.Split(data, "\n") { v := strings.Split(line, ": ") n.AddWire(&Wire{name: v[0], state: to_state(v[1])}) } } func (n *Network) AddWire(wire *Wire) { n.wires = append(n.wires, wire) } func (n *Network) AddWires2(w string, count int, value int64) { for x := 0; x < count; x++ { name := fmt.Sprintf("%s%02d", w, x) v := fmt.Sprintf("%d", (value>>x)&1) n.AddWire(&Wire{name: name, state: to_state(v)}) } } func (n *Network) AddGates(data string) { // ntg XOR fgs -> mjb pattern := `^([a-z0-9]{3}) (AND|OR|XOR) ([a-z0-9]{3}) -> ([a-z[0-9]{3})$` re := regexp.MustCompile(pattern) for _, line := range strings.Split(data, "\n") { matches := re.FindAllStringSubmatch(line, -1) for _, match := range matches { n.AddGate(match[1], match[2], match[3], match[4], &n.wires) } } } func (n *Network) SwapOutputs(a, b string) { _, wire1 := to_wire(a, n.wires) _, wire2 := to_wire(b, n.wires) for _, gate := range n.gates { if gate.out.name == a { gate.out = wire2 } else if gate.out.name == b { gate.out = wire1 } } } func (n *Network) AddGate(in0, op, in1, out string, wires *[]*Wire) { var in [2]*Wire var outw *Wire *wires, in[0] = to_wire(in0, *wires) *wires, in[1] = to_wire(in1, *wires) *wires, outw = to_wire(out, *wires) n.gates = append(n.gates, &Gate{in: in, op: to_operation(op), out: outw}) } func (n *Network) OutputKnown() bool { for _, gate := range n.gates { if gate.out.name[0] != 'z' { continue } if gate.out.state == NA { return false } } return true } func (n *Network) Output() int64 { var ret int64 ret = 0 for _, gate := range n.gates { if gate.out.name[0] != 'z' { continue } addr := utils.MustAtoi(gate.out.name[1:]) if gate.out.state == ONE { ret += 1 << addr } } return ret } func (n Gate) String() string { return n.in[0].name + "(" + n.in[0].state.String() + ")\t" + n.op.String() + " " + n.in[1].name + "(" + n.in[1].state.String() + ")\t ->\t" + n.out.name + " (" + n.out.state.String() + ")" } func to_state(in string) State { switch in { case "1": return ONE case "0": return ZERO } return NA } func to_operation(in string) Operation { var val Operation switch in { case "AND": val = AND case "OR": val = OR case "XOR": val = XOR } return val } func to_wire(in string, wires []*Wire) ([]*Wire, *Wire) { for _, v := range wires { if in == v.name { return wires, v } } wire := Wire{name: in, state: NA} wires = append(wires, &wire) return wires, &wire } func And(a, b State) State { if a == ONE && b == ONE { return ONE } return ZERO } func Or(a, b State) State { if a == ONE || b == ONE { return ONE } return ZERO } func Xor(a, b State) State { if (a == ONE && b == ZERO) || (a == ZERO && b == ONE) { return ONE } return ZERO } func (state State) String() string { switch state { case NA: return "NA" case ZERO: return "0" case ONE: return "1" default: return "Invalid" } } func (op Operation) String() string { switch op { case OR: return "OR" case AND: return "AND" case XOR: return "XOR" default: return "Invalid" } }