package main import ( "os" "fmt" "strings" "strconv" "math" ) // data types type Equation struct { result int params []int } // general util func check(e error) { if e != nil { panic(e) } } func newEquations(data string) []*Equation { /* Reads the puzzle input, return a list of Equation structs. */ lines := strings.Split(data, "\n") equations := make([]*Equation, 0, len(lines)) for _, line := range lines { split_line := strings.Split(line, ":") // get result/ number before the colon result, err := strconv.Atoi(split_line[0]) check(err) // parse paramter/ numbers after the colon str_params := strings.Split(strings.TrimSpace(split_line[1]), " ") params := make([]int, 0, len(str_params)) for _, num := range str_params { param, err := strconv.Atoi(num) check(err) params = append(params, param) } equations = append(equations, &Equation{ result: result, params: params }) } return equations } func incr(arr []int) []int { /* least significant bit first increment, base 3 */ over := 1 for i := range arr { if over == 0 { break } arr[i] += over over = arr[i] / 3 arr[i] = arr[i] % 3 } return arr } func isValid(eq *Equation) bool { /* Checks whether the equation could be right by combining the paramters in * all possible ways and comparing to the result. */ operators := make([]int, len(eq.params) - 1) for range int(math.Pow(3.0, float64(len(operators)))) { // calc possible result acc := eq.params[0] for i, op := range operators { if op == 0 { acc += eq.params[i + 1] } else if op == 1 { acc *= eq.params[i + 1] } else if op == 2 { num, err := strconv.Atoi(strconv.Itoa(acc) + strconv.Itoa(eq.params[i + 1])) check(err) acc = num } } // compare to actual result if acc == eq.result { return true } operators = incr(operators) } return false } // main loop func main() { // read in file dat, err := os.ReadFile("data.txt") check(err) dat_str := strings.TrimSpace(string(dat)) equations := newEquations(dat_str) result := 0 for _, eq := range equations { if isValid(eq) { result += eq.result } } fmt.Println(result) }