2020-12-19 14:40:55 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
start := time.Now()
|
|
|
|
partOne()
|
|
|
|
duration := time.Since(start)
|
|
|
|
partTwo()
|
|
|
|
duration2 := time.Since(start)
|
|
|
|
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
|
|
|
}
|
|
|
|
|
|
|
|
// an expression is a list of expressions and a list of operators that intersperse those expressions, OR a number.
|
|
|
|
// as a sanity check, the length of the body list should always be one more than operators, unless both are 0,
|
|
|
|
// in which case number must be set. Luckily, we don't have to worry about 0 (the "empty" state for numbers).
|
|
|
|
type expression struct {
|
|
|
|
body []expression
|
|
|
|
operators []op
|
|
|
|
number int
|
|
|
|
}
|
|
|
|
|
|
|
|
// op can be one of two things.
|
|
|
|
type op struct {
|
|
|
|
plus bool
|
|
|
|
multiply bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1 + 2 should parse to
|
|
|
|
// expression{
|
|
|
|
// body:[
|
|
|
|
// expression{number: 1},
|
|
|
|
// expression{number: 2},
|
|
|
|
// ],
|
|
|
|
// operators:[
|
|
|
|
// op{plus: true},
|
|
|
|
// ],
|
|
|
|
// }
|
|
|
|
|
|
|
|
// (2 * 2) + 2 should parse to:
|
|
|
|
// expression{
|
|
|
|
// body:[
|
|
|
|
// expression{
|
|
|
|
// body: [
|
|
|
|
// expression{number: 2},
|
|
|
|
// expression{number: 2},
|
|
|
|
// ],
|
|
|
|
// operators: [
|
|
|
|
// op{multiply: true},
|
|
|
|
// ],
|
|
|
|
// },
|
|
|
|
// expression{number: 2},
|
|
|
|
// ],
|
|
|
|
// operators:[
|
|
|
|
// op{plus:true}
|
|
|
|
// ]
|
|
|
|
// }
|
|
|
|
|
|
|
|
func parseExpression(e expression, s string) (expression, string) {
|
|
|
|
for i := 0; i < len(s); {
|
|
|
|
switch s[i] {
|
|
|
|
case '(':
|
|
|
|
sub, remainder := parseExpression(expression{}, s[i+1:])
|
|
|
|
e.body = append(e.body, sub)
|
|
|
|
i = len(s) - len(remainder) + 1 // skip everything we just parsed in the subexpression
|
|
|
|
case ')':
|
|
|
|
return e, s[i:]
|
|
|
|
case '*':
|
|
|
|
e.operators = append(e.operators, op{multiply: true})
|
|
|
|
i = i + 1
|
|
|
|
case '+':
|
|
|
|
e.operators = append(e.operators, op{plus: true})
|
|
|
|
i = i + 1
|
|
|
|
case ' ':
|
|
|
|
i = i + 1
|
|
|
|
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
|
|
// luckily, it's all single-digit numbers and I don't need to track parser state
|
|
|
|
// an ASCII byte number minus the rune value of 0 equals the integer itself: '1' - '0' == 49 - 48;
|
|
|
|
// rune math returns a platform-specific int, so cast to generic int.
|
|
|
|
e.body = append(e.body, expression{number: int(s[i] - '0')})
|
|
|
|
i = i + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return e, ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func printExpression(e expression) string {
|
|
|
|
result := ""
|
|
|
|
for i := 0; i < len(e.body); i = i + 1 {
|
|
|
|
if e.body[i].number != 0 {
|
|
|
|
result = result + fmt.Sprintf("%d ", e.body[i].number)
|
|
|
|
} else {
|
|
|
|
result = result + "(" + printExpression(e.body[i]) + ") "
|
|
|
|
}
|
|
|
|
if i < len(e.operators) {
|
|
|
|
if e.operators[i].multiply {
|
|
|
|
result = result + "* "
|
|
|
|
} else if e.operators[i].plus {
|
|
|
|
result = result + "+ "
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return strings.TrimSpace(result)
|
|
|
|
}
|
|
|
|
|
2020-12-19 14:45:31 +00:00
|
|
|
// evaluation starts at the root then descends into the sub-expressions.
|
2020-12-19 14:40:55 +00:00
|
|
|
func evalLeftToRight(e expression) int {
|
|
|
|
var result int
|
|
|
|
if e.number != 0 {
|
|
|
|
result = e.number
|
|
|
|
} else {
|
|
|
|
result = evalLeftToRight(e.body[0])
|
|
|
|
}
|
|
|
|
for i := 0; i < len(e.operators); i = i + 1 {
|
|
|
|
if e.operators[i].multiply {
|
|
|
|
result = result * evalLeftToRight(e.body[i+1])
|
|
|
|
}
|
|
|
|
if e.operators[i].plus {
|
|
|
|
result = result + evalLeftToRight(e.body[i+1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
// previous approach might not work...
|
|
|
|
func evalPlusThenMultiplication(e expression) int {
|
|
|
|
var result int
|
|
|
|
if e.number != 0 {
|
|
|
|
result = e.number
|
|
|
|
} else {
|
|
|
|
result = evalPlusThenMultiplication(e.body[0])
|
|
|
|
}
|
|
|
|
for i := 0; i < len(e.operators); i = i + 1 {
|
|
|
|
if e.operators[i].multiply {
|
|
|
|
result = result * evalPlusThenMultiplication(e.body[i+1])
|
|
|
|
}
|
|
|
|
if e.operators[i].plus {
|
|
|
|
result = result + evalPlusThenMultiplication(e.body[i+1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func partOne() {
|
|
|
|
f, _ := os.Open("input")
|
|
|
|
reader := bufio.NewReader(f)
|
|
|
|
scanner := bufio.NewScanner(reader)
|
|
|
|
|
|
|
|
total := 0
|
|
|
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
|
|
|
e, _ := parseExpression(expression{}, line)
|
|
|
|
total = total + evalLeftToRight(e)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(total)
|
|
|
|
}
|
|
|
|
|
|
|
|
func partTwo() {
|
|
|
|
f, _ := os.Open("testinput")
|
|
|
|
reader := bufio.NewReader(f)
|
|
|
|
scanner := bufio.NewScanner(reader)
|
|
|
|
|
|
|
|
total := 0
|
|
|
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
|
|
|
e, _ := parseExpression(expression{}, line)
|
|
|
|
fmt.Println(printExpression(e), "=", evalPlusThenMultiplication(e))
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(total)
|
|
|
|
}
|