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) }