package day21 import ( "bufio" "fmt" "os" "strconv" ) const ( OpAdd int = iota OpMul OpSub OpDiv OpTarget ) func opString(v int) string { switch v { case OpAdd: return "+" case OpMul: return "*" case OpSub: return "-" case OpDiv: return "/" case OpTarget: return "==" default: return "???????" } } type MonkeyState struct { valueSet bool value int operation int left string right string } func step(monkeys map[string]MonkeyState) bool { changedSomething := false for monkey, state := range monkeys { if !state.valueSet && monkeys[state.left].valueSet && monkeys[state.right].valueSet { switch state.operation { case OpAdd: state.value = monkeys[state.left].value + monkeys[state.right].value case OpSub: state.value = monkeys[state.left].value - monkeys[state.right].value case OpMul: state.value = monkeys[state.left].value * monkeys[state.right].value case OpDiv: state.value = monkeys[state.left].value / monkeys[state.right].value } state.valueSet = true monkeys[monkey] = state changedSomething = true } } return changedSomething } func contains(ls []string, x string) bool { for _, tester := range ls { if tester == x { return true } } return false } func Run(filename string) { file, err := os.Open(filename) if err != nil { fmt.Println("Error opening file:", err) return } scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) monkeys := map[string]MonkeyState{} for scanner.Scan() { line := scanner.Text() monkey := line[0:4] data := line[6:] value, err := strconv.Atoi(data) if err == nil { monkeys[monkey] = MonkeyState{true, value, 0, "", ""} } else { left := data[0:4] right := data[7:] switch data[5] { case '+': monkeys[monkey] = MonkeyState{false, 0, OpAdd, left, right} case '-': monkeys[monkey] = MonkeyState{false, 0, OpSub, left, right} case '*': monkeys[monkey] = MonkeyState{false, 0, OpMul, left, right} case '/': monkeys[monkey] = MonkeyState{false, 0, OpDiv, left, right} default: fmt.Println("Bad operation", string(data[5])) return } } } file.Close() for !monkeys["root"].valueSet { step(monkeys) } fmt.Println("Root monkey value, part 1:", monkeys["root"].value) humn := monkeys["humn"] humn.valueSet = false humn.operation = OpTarget monkeys["humn"] = humn unwoundMonkeys := []string{"humn"} updatedSomething := true for updatedSomething { updatedSomething = false for monkey, state := range monkeys { if contains(unwoundMonkeys, state.left) || contains(unwoundMonkeys, state.right) { if state.valueSet { state.valueSet = false monkeys[monkey] = state unwoundMonkeys = append(unwoundMonkeys, monkey) updatedSomething = true } } } } fmt.Println("monkeys now", monkeys) var targetValue int var nextNode string if monkeys[monkeys["root"].left].valueSet { targetValue = monkeys[monkeys["root"].left].value nextNode = monkeys["root"].right fmt.Println("Root's right is the unknown one", nextNode) } else { targetValue = monkeys[monkeys["root"].right].value nextNode = monkeys["root"].left fmt.Println("Root's left is the unknown one", nextNode) } for !monkeys["humn"].valueSet { nextState := monkeys[nextNode] if nextState.valueSet { fmt.Println("PANIC: Grounded in a node with the value set") return } else if nextState.operation == OpTarget { nextState.value = targetValue nextState.valueSet = true monkeys[nextNode] = nextState } else if monkeys[nextState.left].valueSet { nextState.value = targetValue nextState.valueSet = true monkeys[nextNode] = nextState switch nextState.operation { case OpAdd: // T = V + X // T - V = X targetValue = targetValue - monkeys[nextState.left].value case OpMul: // T = V * X // T / V = X targetValue = targetValue / monkeys[nextState.left].value case OpSub: // T = V - X // T + X = V // X = V - T targetValue = monkeys[nextState.left].value - targetValue case OpDiv: // T = V / X // TX = V // X = V / T targetValue = monkeys[nextState.left].value / targetValue default: fmt.Println("PANIC: Bad operation in back compute") } nextNode = nextState.right } else if monkeys[nextState.right].valueSet { nextState.value = targetValue nextState.valueSet = true monkeys[nextNode] = nextState switch nextState.operation { case OpAdd: // T = X + V // T - V = X targetValue = targetValue - monkeys[nextState.right].value case OpMul: // T = X * V targetValue = targetValue / monkeys[nextState.right].value case OpSub: // T = X - V // T + V = X targetValue = targetValue + monkeys[nextState.right].value case OpDiv: // T = X / V // TV = X targetValue = targetValue * monkeys[nextState.right].value default: fmt.Println("PANIC: Bad operation in back compute") } nextNode = nextState.left } else { fmt.Println("PANIC: Trouble with a double split in search") return } } fmt.Println("Actually, my value should be", monkeys["humn"].value) }