Compare commits
No commits in common. "9821836b6c617cc2329c44892e33c08de9646a32" and "d8ed53244232c7394bc87f8df6194228aadb62b4" have entirely different histories.
9821836b6c
...
d8ed532442
180
18/main.go
180
18/main.go
@ -1,180 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluation starts at the root then descends into the sub-expressions.
|
|
||||||
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)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user