291 lines
4.8 KiB
Go
291 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
aoc "AoC/2021/common"
|
|
"bufio"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
)
|
|
|
|
type snailNumber struct {
|
|
child [2]*snailNumber
|
|
value [2]int
|
|
|
|
parent *snailNumber
|
|
}
|
|
|
|
func (s *snailNumber) print() {
|
|
depth := s.depth()
|
|
for i := 0; i < depth-1; i++ {
|
|
fmt.Print(" ")
|
|
}
|
|
fmt.Printf("%d[\n", depth)
|
|
|
|
if s.child[0] == nil {
|
|
for i := 0; i < depth; i++ {
|
|
fmt.Print(" ")
|
|
}
|
|
fmt.Printf("%d", s.value[0])
|
|
} else {
|
|
s.child[0].print()
|
|
}
|
|
fmt.Print("\n")
|
|
if s.child[1] == nil {
|
|
for i := 0; i < depth; i++ {
|
|
fmt.Print(" ")
|
|
}
|
|
fmt.Printf("%d", s.value[1])
|
|
} else {
|
|
s.child[1].print()
|
|
}
|
|
|
|
fmt.Print("\n")
|
|
for i := 0; i < depth-1; i++ {
|
|
fmt.Print(" ")
|
|
}
|
|
fmt.Print("]")
|
|
|
|
// We have reached the top level
|
|
if s.parent == nil {
|
|
fmt.Print("\n")
|
|
}
|
|
}
|
|
|
|
func (s *snailNumber) depth() int {
|
|
depth := 1
|
|
{
|
|
number := s
|
|
for number.parent != nil {
|
|
number = number.parent
|
|
depth++
|
|
}
|
|
}
|
|
|
|
return depth
|
|
}
|
|
|
|
func (s *snailNumber) explode() bool {
|
|
depth := s.depth()
|
|
|
|
// Check if the number needs to explode
|
|
if depth > 4 {
|
|
// Set this entry in the paren to 0
|
|
side := -1
|
|
for i := 0; i < 2; i++ {
|
|
if s.parent.child[i] == s {
|
|
s.parent.child[i] = nil
|
|
s.parent.value[i] = 0
|
|
side = i
|
|
}
|
|
}
|
|
|
|
// We know that there is atleat one parent since we are >4 deep
|
|
if s.parent.child[1-side] == nil {
|
|
s.parent.value[1-side] += s.value[1-side]
|
|
} else {
|
|
num := s.parent.child[1-side]
|
|
for true {
|
|
if num.child[side] == nil {
|
|
num.value[side] += s.value[1-side]
|
|
break
|
|
}
|
|
num = num.child[side]
|
|
}
|
|
}
|
|
|
|
num := s.parent
|
|
for true {
|
|
if num.parent == nil {
|
|
break
|
|
}
|
|
|
|
ps := -1
|
|
for i := 0; i < 2; i++ {
|
|
if num.parent.child[i] == num {
|
|
ps = i
|
|
}
|
|
}
|
|
|
|
if ps == 1-side {
|
|
num = num.parent
|
|
|
|
if num.child[side] == nil {
|
|
num.value[side] += s.value[side]
|
|
} else {
|
|
num := num.child[side]
|
|
for true {
|
|
if num.child[1-side] == nil {
|
|
num.value[1-side] += s.value[side]
|
|
break
|
|
}
|
|
num = num.child[1-side]
|
|
}
|
|
}
|
|
break
|
|
}
|
|
|
|
|
|
num = num.parent
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
for i := 0; i < 2; i++ {
|
|
if s.child[i] != nil {
|
|
if s.child[i].explode() {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (s *snailNumber) split() bool {
|
|
for i := 0; i < 2; i++ {
|
|
if s.child[i] == nil {
|
|
if s.value[i] > 9 {
|
|
lhs := math.Floor(float64(s.value[i]) / 2)
|
|
rhs := math.Ceil(float64(s.value[i]) / 2)
|
|
s.value[i] = 0
|
|
s.child[i] = &snailNumber{value: [2]int{int(lhs), int(rhs)}, parent: s}
|
|
|
|
return true
|
|
}
|
|
} else {
|
|
if s.child[i].split() {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *snailNumber) magnitude() int {
|
|
var partial [2]int
|
|
for i := 0; i < 2; i++ {
|
|
if s.child[i] == nil {
|
|
partial[i] = s.value[i]
|
|
} else {
|
|
partial[i] = s.child[i].magnitude()
|
|
}
|
|
}
|
|
|
|
return 3 * partial[0] + 2 * partial[1]
|
|
}
|
|
|
|
func (s *snailNumber) reduce() {
|
|
for true {
|
|
if s.explode() {
|
|
// fmt.Println("After explode: "); top.print()
|
|
continue
|
|
}
|
|
|
|
if s.split() {
|
|
// fmt.Println("After split: "); top.print()
|
|
continue
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
func add(a *snailNumber, b *snailNumber) *snailNumber {
|
|
top := &snailNumber{}
|
|
|
|
top.child[0] = a
|
|
a.parent = top
|
|
|
|
top.child[1] = b
|
|
b.parent = top
|
|
|
|
top.reduce()
|
|
|
|
return top
|
|
}
|
|
|
|
func parseSnailNumber(line string) *snailNumber {
|
|
var top snailNumber
|
|
number := &top
|
|
side := 0
|
|
for _, c := range line {
|
|
switch c {
|
|
case '[':
|
|
number.child[side] = &snailNumber{parent: number}
|
|
number = number.child[side]
|
|
side = 0
|
|
case ']':
|
|
number = number.parent
|
|
case ',':
|
|
side = 1
|
|
default:
|
|
value, _ := strconv.Atoi(string(c))
|
|
number.value[side] = value
|
|
}
|
|
}
|
|
|
|
top.child[0].parent = nil
|
|
|
|
top.child[0].reduce()
|
|
|
|
return top.child[0]
|
|
}
|
|
|
|
func main() {
|
|
challenge := aoc.New(2021, 18)
|
|
|
|
challenge.Test(`[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]
|
|
[[[5,[2,8]],4],[5,[[9,9],0]]]
|
|
[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]
|
|
[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]
|
|
[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]]
|
|
[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]]
|
|
[[[[5,4],[7,7]],8],[[8,3],8]]
|
|
[[9,3],[[9,9],[6,[4,9]]]]
|
|
[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]
|
|
[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]`, []int{4140, 3993})
|
|
|
|
challenge.Solution(1, func (input *bufio.Scanner) int {
|
|
var top *snailNumber
|
|
for input.Scan() {
|
|
line := input.Text()
|
|
num := parseSnailNumber(line)
|
|
|
|
if top == nil {
|
|
top = num
|
|
} else {
|
|
top = add(top, num)
|
|
}
|
|
}
|
|
|
|
return top.magnitude()
|
|
})
|
|
|
|
challenge.Solution(2, func (input *bufio.Scanner) int {
|
|
var lines []string
|
|
for input.Scan() {
|
|
lines = append(lines, input.Text())
|
|
|
|
}
|
|
|
|
largest := 0
|
|
for _, li := range lines {
|
|
for _, lj := range lines {
|
|
if li != lj {
|
|
numi := parseSnailNumber(li)
|
|
numj := parseSnailNumber(lj)
|
|
|
|
magnitude := add(numi, numj).magnitude()
|
|
if magnitude > largest {
|
|
largest = magnitude
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return largest
|
|
})
|
|
}
|