197 lines
4.2 KiB
Go
197 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const mockInput = "190: 10 19\n3267: 81 40 27\n83: 17 5\n156: 15 6\n7290: 6 8 6 15\n161011: 16 10 13\n192: 17 8 14\n21037: 9 7 18 13\n292: 11 6 16 20"
|
|
|
|
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))
|
|
|
|
}
|