// Note: Do NOT use this solution as an example. I hate it. It's a horrible mess // of brute force, workarounds, and shortcuts, and is generally really not a // good idea. A syntax tree would be a better idea and what I should have done // from the beginning but I tried to save time by not thinking|I mean using // recursion instead. I've fought overflows, indexing errors, recursion loops, // general stubborn stupidity, and I'm even two days behind. // But I guess I've learnt what _not_ to do as well. // Next year AoC, next year.. package main import ( "bufio" "fmt" "os" "strings" ) func reduce(exp []rune, debug bool) int { if debug { fmt.Printf("iter ") for _, r := range exp { if r < ' ' { fmt.Printf("%d", r) } else { fmt.Printf("%c", r) } } fmt.Println() } var a, b int var op rune if len(exp) == 1 { return int(exp[0]) } if len(exp) <= 3 { a = int(exp[0]) b = int(exp[2]) op = exp[1] if debug { fmt.Printf("\t? %d %c %d\n", a, op, b) } } else { if exp[len(exp)-1] == ')' { count := 1 for i := len(exp) - 2; i >= 0; i-- { if exp[i] == ')' { count++ } else if exp[i] == '(' { count-- if count == 0 { if i == 0 { return reduce(exp[i+1:len(exp)-1], debug) } a = reduce(exp[:i-1], debug) b = reduce(exp[i+1:len(exp)-1], debug) op = exp[i-1] break } } } } else { a = reduce(exp[:len(exp)-2], debug) b = int(exp[len(exp)-1]) op = exp[len(exp)-2] } if debug { fmt.Printf("\t# %d %c %d\n", a, op, b) } } var result int switch op { case '*': result = a * b case '+': result = a + b } if debug { fmt.Printf("%d %c %d => %d\n", a, op, b, result) } return result } func reduceAdditions(exp []int, debug bool) []int { newExp := make([]int, 0, len(exp)) for i := 0; i < len(exp); i++ { switch exp[i] { case '(': var count int for j := i; j < len(exp); j++ { if exp[j] == '(' { count++ } else if exp[j] == ')' { count-- if count == 0 { if debug { fmt.Printf("mm ") for _, r := range exp[i+1 : j] { if r == '*' || r == '+' || r == '(' || r == ')' { fmt.Printf("%c", r) } else { fmt.Printf("%d", r-255) } } fmt.Println() } e := reduceAdditions(exp[i+1:j], debug) for { prev := e e = reduceAdditions(e, debug) if len(prev) == len(e) { break } } if debug { fmt.Printf("++ ") for _, r := range e { if r == '*' || r == '+' || r == '(' || r == ')' { fmt.Printf("%c", r) } else { fmt.Printf("%d", r-255) } } fmt.Println() } e = reduceMultiplications(e, debug) for { prev := e if len(prev) == len(e) { break } } if debug { fmt.Printf("** ") for _, r := range e { if r == '*' || r == '+' || r == '(' || r == ')' { fmt.Printf("%c", r) } else { fmt.Printf("%d", r-255) } } fmt.Println() } if len(e) == 1 { newExp = append(newExp, e...) } else { newExp = append(newExp, '(') newExp = append(newExp, e...) newExp = append(newExp, ')') } i = j break } } } case '*': newExp = append(newExp, exp[i]) case '+': if exp[i-1] >= 255 && exp[i+1] >= 255 { if debug { fmt.Printf("add %d %d\n", newExp[len(newExp)-1]-255, exp[i+1]-255) } newExp[len(newExp)-1] = ((newExp[len(newExp)-1] - 255) + (exp[i+1] - 255)) + 255 i++ } else { newExp = append(newExp, exp[i]) } default: newExp = append(newExp, exp[i]) } } return newExp } func reduceMultiplications(exp []int, debug bool) []int { newExp := make([]int, 0, len(exp)) for i := 0; i < len(exp); i++ { switch exp[i] { case '*': if exp[i-1] >= 255 && exp[i+1] >= 255 { newExp[len(newExp)-1] = ((newExp[len(newExp)-1] - 255) * (exp[i+1] - 255)) + 255 i++ } else { newExp = append(newExp, exp[i]) } default: newExp = append(newExp, exp[i]) } } return newExp } func reduce2(exp []int, debug bool) int { exp = reduceAdditions(exp, debug) for { prev := exp exp = reduceAdditions(exp, debug) if debug { fmt.Printf("additions ") for _, r := range exp { if r == '*' || r == '+' || r == '(' || r == ')' { fmt.Printf("%c", r) } else { fmt.Printf("%d", r-255) } } fmt.Println() } if len(prev) == len(exp) { break } } exp = reduceMultiplications(exp, debug) for { prev := exp exp = reduceMultiplications(exp, debug) if debug { fmt.Printf("multiplications ") for _, r := range exp { if r == '*' || r == '+' || r == '(' || r == ')' { fmt.Printf("%c", r) } else { fmt.Printf("%d", r-255) } } fmt.Println() } if len(prev) == len(exp) { break } } return exp[0] - 255 } func prepareInput(str string, fudge bool) []rune { line := []rune(strings.ReplaceAll(str, " ", "")) for i, r := range line { if r != '(' && r != ')' && r != '*' && r != '+' { if fudge { line[i] = 255 + r - '0' } else { line[i] = r - '0' } } } return line } // Task1 ... func Task1(input [][]rune) int { var result int for i := range input { result += reduce(input[i], false) } return result } // Task2 ... func Task2(input [][]rune) int { var result int for i := range input { in := make([]int, len(input[i])) for i, j := range input[i] { in[i] = int(j) } r := reduce2(in, false) result += r } return result } func main() { file, _ := os.Open("input.txt") scanner := bufio.NewScanner(file) input := make([]string, 0, 100) for scanner.Scan() { input = append(input, scanner.Text()) } file.Close() in := make([][]rune, len(input)) for i, line := range input { in[i] = prepareInput(line, false) } result := Task1(in) fmt.Printf("Task 1: %d\n", result) in = make([][]rune, len(input)) for i, line := range input { in[i] = prepareInput(line, true) } result = Task2(in) fmt.Printf("Task 2: %d\n", result) }