adventofcode/2021/day18/main.go

233 lines
4.5 KiB
Go

package main
import (
"fmt"
"math"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
var build *SNum
for i := range inp {
wrk := ParseSNum(inp[i])
if build == nil {
build = wrk
} else {
build = build.add(wrk)
}
}
fmt.Println()
fmt.Println("# Part 1")
fmt.Println("Magnitude:", build.magnitude())
fmt.Println()
fmt.Println("# Part 2")
highest := h.MIN_INT
for i := 0; i < len(inp)-1; i++ {
for j := i + 1; j < len(inp); j++ {
w1 := ParseSNum(inp[i]).add(ParseSNum(inp[j]))
w2 := ParseSNum(inp[j]).add(ParseSNum(inp[i]))
m1, m2 := w1.magnitude(), w2.magnitude()
if m1 >= m2 && m1 > highest {
highest = m1
} else if m2 > highest {
highest = m2
}
}
}
fmt.Println("Highest magnitude from 2:", highest)
//3225 is too low
}
type SNum struct {
parent, left, right *SNum
regular bool
value int
}
func ParseSNum(inp string) *SNum {
s, _ := rParseSNum(inp)
return s
}
// ParseSNum returns an SNum and what's left of inp
// after it was produced
func rParseSNum(inp string) (*SNum, string) {
// inp should always start with either a number or a '['
var rest string
s := SNum{}
if inp[0] == '[' {
s.regular = false
s.left, rest = rParseSNum(inp[1:])
s.right, rest = rParseSNum(rest[1:])
s.left.parent = &s
s.right.parent = &s
return &s, rest[1:]
} else {
s.regular = true
var num []byte
for i := range inp {
if inp[i] >= '0' && inp[i] <= '9' {
num = append(num, inp[i])
} else {
s.value = h.Atoi(string(num))
return &s, inp[i:]
}
}
}
return &s, rest
}
// When exploding or splitting, we need to find the leftmost pair that matches the condition
func (s *SNum) needsExplode(depth int) *SNum {
if s.regular {
return nil
}
if depth >= 4 {
if !s.left.regular {
return s.left.needsExplode(depth + 1)
} else if !s.right.regular {
return s.right.needsExplode(depth + 1)
} else {
return s
}
}
if tst := s.left.needsExplode(depth + 1); tst != nil {
return tst
} else if tst := s.right.needsExplode(depth + 1); tst != nil {
return tst
}
return nil
}
func (s *SNum) explode() {
if s.parent == nil {
return
}
s.parent.addToRegularLeftOf(s.left.value, s)
s.parent.addToRegularRightOf(s.right.value, s)
// Replace s with a regular number that just equals 0
s.regular, s.value = true, 0
s.right, s.left = nil, nil
}
func (s *SNum) needsSplit() *SNum {
if s == nil || (s.regular && s.value >= 10) {
return s
}
if tst := s.left.needsSplit(); tst != nil {
return tst
} else if tst := s.right.needsSplit(); tst != nil {
return tst
}
return nil
}
func (s *SNum) split() {
if s.parent == nil {
return
}
l := int(math.Floor(float64(s.value) / 2))
r := int(math.Ceil(float64(s.value) / 2))
n := SNum{
parent: s.parent,
left: &SNum{
regular: true,
value: int(l),
},
right: &SNum{
regular: true,
value: int(r),
},
}
n.left.parent = &n
n.right.parent = &n
if s.parent.left == s {
s.parent.left = &n
} else if s.parent.right == s {
s.parent.right = &n
}
}
func (s *SNum) addToRegularLeftOf(v int, c *SNum) {
if s == nil || v == 0 {
return
}
if s.right != nil && s.right == c {
s.left.addToRightmostRegular(v)
} else if s.left != nil && s.left == c {
s.parent.addToRegularLeftOf(v, s)
}
}
func (s *SNum) addToRegularRightOf(v int, c *SNum) {
if s == nil || v == 0 {
return
}
if s.left == c {
s.right.addToLeftmostRegular(v)
} else if s.right == c {
s.parent.addToRegularRightOf(v, s)
}
}
func (s *SNum) contains(n *SNum) bool {
return s == n || s.left.contains(n) || s.right.contains(n)
}
func (s *SNum) addToRightmostRegular(v int) {
if s.regular {
s.value += v
} else if s.right != nil {
s.right.addToRightmostRegular(v)
}
}
func (s *SNum) addToLeftmostRegular(v int) {
if s.regular {
s.value += v
} else if s.left != nil {
s.left.addToLeftmostRegular(v)
}
}
func (s *SNum) add(n *SNum) *SNum {
wrk := SNum{
left: s,
right: n,
}
s.parent = &wrk
n.parent = &wrk
changed := true
for changed {
changed = false
if v := wrk.needsExplode(0); v != nil {
v.explode()
changed = true
} else if v := wrk.needsSplit(); v != nil {
v.split()
changed = true
}
}
return &wrk
}
func (s *SNum) magnitude() int {
if s.regular {
return s.value
}
return s.left.magnitude()*3 + s.right.magnitude()*2
}
func (s SNum) String() string {
if s.regular {
return fmt.Sprintf("%d", s.value)
}
return fmt.Sprintf("[%s,%s]", s.left, s.right)
}