package main import ( "bufio" "fmt" "os" "regexp" "sort" "strconv" "strings" ) type CircuitState map[string]int const ( AND = "AND" OR = "OR" XOR = "XOR" ) // parseCircuitInstructions reads the input file and splits it into gate definitions and connection instructions. func parseCircuitInstructions() [][]string { file, err := os.Open("input.txt") if err != nil { panic(err) } defer file.Close() scanner := bufio.NewScanner(file) var sections [][]string currentSection := "" for scanner.Scan() { line := scanner.Text() if line == "" { sections = append(sections, strings.Split(strings.TrimSpace(currentSection), "\n")) currentSection = "" } else { currentSection += line + "\n" } } if currentSection != "" { sections = append(sections, strings.Split(strings.TrimSpace(currentSection), "\n")) } return sections } // initializeCircuitState initializes the circuit state from the gate definitions. func initializeCircuitState(gateDefinitions []string) CircuitState { state := make(CircuitState) for _, gateDefinition := range gateDefinitions { parts := strings.Split(gateDefinition, ": ") value, _ := strconv.Atoi(parts[1]) state[parts[0]] = value } return state } // processGateInstruction processes a single gate instruction and updates the state if possible. func processGateInstruction(instruction string, state CircuitState, gateRegex *regexp.Regexp) bool { match := gateRegex.FindStringSubmatch(instruction) if match != nil { input1, operator, input2, outputGate := match[1], match[2], match[3], match[4] input1Value, input1Exists := state[input1] input2Value, input2Exists := state[input2] // Skip this instruction if either input gate value is not yet computed. if !input1Exists || !input2Exists { return false } // Compute the output gate value based on the operator. switch operator { case AND: state[outputGate] = input1Value & input2Value case OR: state[outputGate] = input1Value | input2Value case XOR: state[outputGate] = input1Value ^ input2Value } return true } return false } // simulateCircuit simulates the circuit and computes the final gate states. func simulateCircuit(sections [][]string) CircuitState { state := initializeCircuitState(sections[0]) gateRegex := regexp.MustCompile(`^(.*) (AND|OR|XOR) (.*) -> (.*)$`) remainingInstructions := make([]string, 0) remainingInstructions = append(remainingInstructions, sections[1]...) for len(remainingInstructions) > 0 { nextRemainingInstructions := make([]string, 0) for _, instruction := range remainingInstructions { if !processGateInstruction(instruction, state, gateRegex) { nextRemainingInstructions = append(nextRemainingInstructions, instruction) } } remainingInstructions = nextRemainingInstructions } return state } // calculateGateValueFromZWires computes a decimal number based on the binary values of gates with IDs starting with "z". func calculateGateValueFromZWires() int { sections := parseCircuitInstructions() finalState := simulateCircuit(sections) // Collect and sort gate IDs starting with "z". var zGateKeys []string for gateID := range finalState { if strings.HasPrefix(gateID, "z") { zGateKeys = append(zGateKeys, gateID) } } sort.Slice(zGateKeys, func(i, j int) bool { return zGateKeys[i] > zGateKeys[j] }) // Concatenate binary values of sorted gates. var binaryBuilder strings.Builder for _, gateKey := range zGateKeys { binaryBuilder.WriteString(strconv.Itoa(finalState[gateKey])) } // Convert the concatenated binary string to a decimal number. binaryString := binaryBuilder.String() result, _ := strconv.ParseInt(binaryString, 2, 64) return int(result) } // findGate searches for a connection matching the given inputs and operator. func findGate(a, b, operator string, instructions []string) string { for _, instruction := range instructions { if strings.HasPrefix(instruction, fmt.Sprintf("%s %s %s", a, operator, b)) || strings.HasPrefix(instruction, fmt.Sprintf("%s %s %s", b, operator, a)) { parts := strings.Split(instruction, " -> ") return parts[len(parts)-1] } } return "" } // analyzeGateConnections processes gate connections to diagnose and repair the malfunctioning circuit. func analyzeGateConnections() string { data := parseCircuitInstructions() instructions := data[1] var swappedConnections []string var previousCarry string // Helper to swap and append gates when a "z" prefix condition is met swapAndTrack := func(a, b string) (string, string) { if strings.HasPrefix(a, "z") { a, b = b, a swappedConnections = append(swappedConnections, a, b) } return a, b } for i := 0; i < 45; i++ { n := fmt.Sprintf("%02d", i) var sumGate, carryGate, andGate, xorGate, nextCarry string // Half adder logic for the circuit sumGate = findGate("x"+n, "y"+n, "XOR", instructions) carryGate = findGate("x"+n, "y"+n, "AND", instructions) if previousCarry != "" { andGate = findGate(previousCarry, sumGate, "AND", instructions) if andGate == "" { // Swap logic if the AND connection is missing sumGate, carryGate = carryGate, sumGate swappedConnections = append(swappedConnections, sumGate, carryGate) andGate = findGate(previousCarry, sumGate, "AND", instructions) } xorGate = findGate(previousCarry, sumGate, "XOR", instructions) // Ensure the gates are in the correct order with respect to "z" prefix sumGate, xorGate = swapAndTrack(sumGate, xorGate) carryGate, xorGate = swapAndTrack(carryGate, xorGate) andGate, xorGate = swapAndTrack(andGate, xorGate) nextCarry = findGate(andGate, carryGate, "OR", instructions) } // Final swap check for next carry gate if strings.HasPrefix(nextCarry, "z") && nextCarry != "z45" { nextCarry, xorGate = swapAndTrack(nextCarry, xorGate) } // Update the carry for the next iteration if previousCarry == "" { previousCarry = carryGate } else { previousCarry = nextCarry } } // Sort and join swapped gates sort.Strings(swappedConnections) return strings.Join(swappedConnections, ",") } func main() { fmt.Println("Part 1: Decimal value from z gates:", calculateGateValueFromZWires()) fmt.Println("Part 2: Analyzed gate connections:", analyzeGateConnections()) }