Part 1
The input today is a series of columns, containing numbers until the final row, which is a mathematic operator (multiplication or addition). Each column represents a problem.
Solution
Our only data structure today is a problem, which has an operator and a slice of numbers:
const (
OP_PRODUCT = iota
OP_SUM
)
type Problem struct {
operation int
numbers []int
}We then need to read the input and convert it into problems. The input format is slightly inconvenient as the problems are represented as columns - if they were rows then we could simply read one line at a time and directly convert it by creating one Problem at a time. With columns, we need to find the number of columns first, create that many Problems, and then populate them. However, this is straightforward as we can count the number of columns in the first line and then use make to create a slice of Problems that is the right size.
func getProblems(input string) []Problem {
lines := strings.Split(input, "\n")
// Number of problems is equal to the number of columns of the first line
firstLineColumns := strings.Fields(lines[0])
problems := make([]Problem, len(firstLineColumns))
for lineIndex := range lines {
columns := strings.Fields(lines[lineIndex])
for c := range columns {
switch columns[c] {
case "*":
problems[c].operation = OP_PRODUCT
case "+":
problems[c].operation = OP_SUM
default:
number, _ := strconv.Atoi(columns[c])
problems[c].numbers = append(problems[c].numbers, number)
}
}
}
return problems
}Solving an individual problem is easy because the samber/lo library provides functions to calculate the sum or product of a slice of integers. All we need to do is decide which function to call based on the operator field.
func solve(problem Problem) int {
switch problem.operation {
case OP_PRODUCT:
return lo.Product(problem.numbers)
case OP_SUM:
return lo.Sum(problem.numbers)
default:
return -1
}
}Finally the grand total is the sum of all the problem solutions. Again we can use samber/lo to do most of the work for us.
func grandTotal(problems []Problem) int {
return lo.SumBy(problems, func(problem Problem) int {
return solve(problem)
})
}Part 2
Overall thoughts
Part 1 was possibly the easiest puzzle so far, the only slighty inconvenience was having the input in columns instead of rows. Even though we used samber/lo to avoid having to write our own sum and product functions, these would have been straightforward for loops.