package main import ( "fmt" "strconv" "strings" ) type ProblemRow struct { res int numbers []int } func parseRow(s string) *ProblemRow { colonIdx := strings.Index(s, ":") res, _ := strconv.Atoi(s[0:colonIdx]) numbers := toSliceOfInt(strings.Split(s[colonIdx+2:], " ")) return &ProblemRow{res, numbers} } func parseProblemInput(input string) []*ProblemRow { ret := make([]*ProblemRow, 0) for _, row := range strings.Split(input, "\n") { ret = append(ret, parseRow(row)) } return ret } func toSliceOfInt(split []string) []int { ret := make([]int, len(split)) for i := 0; i < len(split); i++ { ret[i], _ = strconv.Atoi(split[i]) } return ret } type Operand int8 const ( PRODUCT Operand = iota SUM CONCAT ) type OperandNode struct { value Operand nextNode *NumberNode } func (n *OperandNode) String() string { opp := "+" if n.value == PRODUCT { opp = "*" } if n.value == CONCAT { opp = "||" } return fmt.Sprintf("%s %s", opp, n.nextNode.String()) } type NumberNode struct { value int nextNode *OperandNode } func (n *NumberNode) String() string { rest := "" if n.nextNode != nil { rest = n.nextNode.String() } return fmt.Sprintf("%d %s", n.value, rest) } func process(node *NumberNode) int { if node.nextNode == nil { return node.value } if node.nextNode.value == SUM { newValue := node.value + node.nextNode.nextNode.value nextNext := node.nextNode.nextNode.nextNode newNode := &NumberNode{newValue, nextNext} return process(newNode) } if node.nextNode.value == PRODUCT { newValue := node.value * node.nextNode.nextNode.value nextNext := node.nextNode.nextNode.nextNode newNode := &NumberNode{newValue, nextNext} return process(newNode) } if node.nextNode.value == CONCAT { newValue := concatInts(node.value, node.nextNode.nextNode.value) nextNext := node.nextNode.nextNode.nextNode newNode := &NumberNode{newValue, nextNext} return process(newNode) } panic("Unknown Operand") } func concatInts(value, value2 int) int { n := 1 for n <= value2 { n = n * 10 } return value2 + n*value } func createNodes(numbers []int, operands []Operand) *NumberNode { if len(numbers)-1 != len(operands) { panic("Number of operands must be 1 minus number of numbers") } var firstVNode *NumberNode = nil var previousONode *OperandNode = nil for i := 0; i < len(operands); i++ { oNode := &OperandNode{operands[i], nil} vNode := &NumberNode{numbers[i], oNode} if firstVNode == nil { firstVNode = vNode } if previousONode != nil { previousONode.nextNode = vNode } previousONode = oNode } previousONode.nextNode = &NumberNode{numbers[len(numbers)-1], nil} return firstVNode } func createAllPossibleChain(numbers []int, operands ...Operand) <-chan *NumberNode { out := make(chan *NumberNode) go func() { for operators := range allPossibleOperators(len(numbers)-1, operands...) { out <- createNodes(numbers, operators) } close(out) }() return out } func allPossibleOperators(n int, operands ...Operand) <-chan []Operand { out := make(chan []Operand) go func() { defer close(out) if n == 0 { out <- []Operand{} return } for _, operand := range operands { for prefix := range allPossibleOperators(n-1, operands...) { out <- append(prefix, operand) } } }() return out } func problem7A(input string) int { ret := 0 for _, problemRow := range parseProblemInput(input) { if isSolvable(problemRow, SUM, PRODUCT) { ret += problemRow.res } } return ret } func problem7B(input string) int64 { var ret int64 = 0 for _, problemRow := range parseProblemInput(input) { if isSolvable(problemRow, PRODUCT, SUM, CONCAT) { ret += int64(problemRow.res) } } return ret } func isSolvable(row *ProblemRow, operands ...Operand) bool { for node := range createAllPossibleChain(row.numbers, operands...) { if process(node) == row.res { return true } } return false } func main() { fmt.Printf("The answer to question 7A is %d \n", problem7A(realInput)) fmt.Printf("The answer to question 7B is %d \n", problem7B(realInput)) }