adventofcode/2020/day18.go
2020-12-18 11:46:20 +00:00

152 lines
2.4 KiB
Go

package main
import (
"fmt"
"log"
"os"
"strconv"
"git.annabunch.es/annabunches/adventofcode/2020/lib/util"
)
// operators
const (
ADD = -1
MUL = -2
GRP = -3
)
type Stack struct {
data []int
}
func NewStack() *Stack {
return &Stack{
data: make([]int, 0),
}
}
func (s *Stack) Push(value int) {
s.data = append(s.data, value)
}
func (s *Stack) Pop() int {
if s.Empty() {
log.Panicf("Tried to pop from empty stack")
}
ret := s.Top()
s.data = s.data[:len(s.data)-1]
return ret
}
func (s *Stack) Top() int {
if s.Empty() {
return -3
}
return s.data[len(s.data)-1]
}
func (s *Stack) Empty() bool {
return len(s.data) == 0
}
// This only works when the starting expression is all positive, single-digit integers
// and where all operators are additive (i.e. we rely on being able to use negative numbers
// as sentinel values.
func convertRPN(exp string, precedence map[int]int) []int {
rpn := make([]int, 0)
ops := NewStack()
for _, i := range exp {
e := string(i)
x, err := strconv.Atoi(e)
if err == nil {
rpn = append(rpn, x)
continue
}
var opcode int
switch e {
case "+":
opcode = ADD
case "*":
opcode = MUL
case "(":
ops.Push(GRP)
continue
case ")":
for ops.Top() != GRP {
rpn = append(rpn, ops.Pop())
}
ops.Pop()
continue
default:
continue
}
if ops.Empty() {
ops.Push(opcode)
continue
}
if precedence[ops.Top()] >= precedence[opcode] {
rpn = append(rpn, ops.Pop())
}
ops.Push(opcode)
}
for !ops.Empty() {
rpn = append(rpn, ops.Pop())
}
return rpn
}
func evaluateExpression(rpn []int) int {
output := NewStack()
for _, value := range rpn {
if value >= 0 {
output.Push(value)
continue
}
switch value {
case ADD:
x := output.Pop()
y := output.Pop()
output.Push(x + y)
case MUL:
x := output.Pop()
y := output.Pop()
output.Push(x * y)
}
}
return output.Pop()
}
func main() {
step := os.Args[1]
values := util.InputParserStrings(os.Args[2])
total := 0
precedence := make(map[int]int)
switch step {
case "1":
precedence[ADD] = 0
precedence[MUL] = 0
precedence[GRP] = -1
case "2":
precedence[ADD] = 1
precedence[MUL] = 0
precedence[GRP] = -1
}
exps := make([][]int, 0)
for _, line := range values {
// parse the expression into RPN
exps = append(exps, convertRPN(line, precedence))
}
for _, exp := range exps {
total += evaluateExpression(exp)
}
fmt.Println(total)
}