got part one working, part 2 will be more work
This commit is contained in:
parent
d8ed532442
commit
0cd38cfd03
182
18/main.go
Normal file
182
18/main.go
Normal file
@ -0,0 +1,182 @@
|
||||
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, checks the list of operators,
|
||||
// then descends into the expressions as precedence demands apply operators to 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