339 lines
5.2 KiB
Go
339 lines
5.2 KiB
Go
package eighteen
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
type value struct {
|
|
p *pair
|
|
v int
|
|
}
|
|
|
|
type pair struct {
|
|
left value
|
|
right value
|
|
}
|
|
|
|
type snail struct {
|
|
input []value
|
|
}
|
|
|
|
func (s *snail) load(filename string) error {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
s.input = append(s.input, parse(scanner.Text()))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *snail) largestMagnitude() int {
|
|
max := 0
|
|
|
|
for i := 0; i < len(s.input) - 1; i++ {
|
|
for j := i + 1; j < len(s.input); j++ {
|
|
// i + j
|
|
sum1 := s.input[i].add(s.input[j])
|
|
mag := sum1.magnitude()
|
|
if mag > max {
|
|
max = mag
|
|
}
|
|
|
|
// j + i
|
|
sum2 := s.input[j].add(s.input[i])
|
|
mag = sum2.magnitude()
|
|
if mag > max {
|
|
max = mag
|
|
}
|
|
}
|
|
}
|
|
|
|
return max
|
|
}
|
|
|
|
func (s *snail) sum() value {
|
|
v := value{}
|
|
for i, n := range s.input {
|
|
if i == 0 {
|
|
v = n
|
|
continue
|
|
}
|
|
v = v.add(n)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func (v *value) add(vb value) value {
|
|
sum := value{
|
|
p: &pair{
|
|
left: *v,
|
|
right: vb,
|
|
},
|
|
}
|
|
|
|
reduced := sum.reduce()
|
|
for reduced != nil {
|
|
r := reduced.reduce()
|
|
if r == nil {
|
|
return *reduced
|
|
}
|
|
reduced = r
|
|
}
|
|
|
|
return sum
|
|
}
|
|
|
|
func (v *value) reduce() *value {
|
|
reduced := v.copy()
|
|
// Check for nests deeper than 4
|
|
if reduced.p != nil {
|
|
_, exploded, _, _ := reduced.p.explode(1)
|
|
if exploded {
|
|
return &reduced
|
|
}
|
|
}
|
|
|
|
// Check for values greater than 10
|
|
_, split := reduced.split()
|
|
if split {
|
|
return &reduced
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (v *value) magnitude() int {
|
|
if v.p == nil {
|
|
return v.v
|
|
}
|
|
|
|
return (3 * v.p.left.magnitude()) + (2 * v.p.right.magnitude())
|
|
}
|
|
|
|
func (p *pair) explode(depth int) (int, bool, bool, bool) {
|
|
if p == nil || (p.left.p == nil && p.right.p == nil) {
|
|
return 0, false, false, false
|
|
}
|
|
|
|
if depth == 4 {
|
|
if p.left.p != nil {
|
|
if p.right.p != nil {
|
|
p.right.p.increaseLeft(p.left.p.right.v, false)
|
|
} else {
|
|
p.right.v += p.left.p.right.v
|
|
}
|
|
rightVal := p.left.p.left.v
|
|
p.left.v = 0
|
|
p.left.p = nil
|
|
|
|
return rightVal, true, true, false
|
|
}
|
|
|
|
if p.right.p != nil {
|
|
if p.left.p != nil {
|
|
p.left.p.increaseRight(p.right.p.left.v, false)
|
|
} else {
|
|
p.left.v += p.right.p.left.v
|
|
}
|
|
leftVal := p.right.p.right.v
|
|
p.right.v = 0
|
|
p.right.p = nil
|
|
|
|
return leftVal, true, false, true
|
|
}
|
|
|
|
return 0, false, false, false
|
|
}
|
|
|
|
// Check if left has nested pair
|
|
if p.left.p != nil {
|
|
offender, exploded, left, right := p.left.p.explode(depth + 1)
|
|
|
|
if exploded {
|
|
if right {
|
|
p.increaseRight(offender, false)
|
|
return offender, exploded, left, false
|
|
}
|
|
|
|
return offender, exploded, left, right
|
|
}
|
|
}
|
|
|
|
// Check if right has nested pair
|
|
if p.right.p != nil {
|
|
offender, exploded, left, right := p.right.p.explode(depth + 1)
|
|
|
|
if exploded {
|
|
if left {
|
|
p.increaseLeft(offender, false)
|
|
return 0, true, false, right
|
|
}
|
|
return offender, true, left, right
|
|
}
|
|
}
|
|
|
|
return 0, false, false, false
|
|
}
|
|
|
|
func (p *pair) increaseLeft(amount int, swapped bool) {
|
|
if p.left.p != nil {
|
|
if !swapped {
|
|
p.left.p.increaseRight(amount, true)
|
|
} else {
|
|
p.left.p.increaseLeft(amount, true)
|
|
}
|
|
} else {
|
|
p.left.v += amount
|
|
}
|
|
}
|
|
|
|
func (p *pair) increaseRight(amount int, swapped bool) {
|
|
if p.right.p != nil {
|
|
if !swapped {
|
|
p.right.p.increaseLeft(amount, true)
|
|
} else {
|
|
p.right.p.increaseRight(amount, swapped)
|
|
}
|
|
} else {
|
|
p.right.v += amount
|
|
}
|
|
}
|
|
|
|
func (v *value) split() (*pair, bool) {
|
|
if v.p == nil {
|
|
if v.v < 10 {
|
|
return nil, false
|
|
}
|
|
|
|
left := v.v / 2
|
|
right := v.v / 2
|
|
if v.v%2 == 1 {
|
|
right++
|
|
}
|
|
|
|
return &pair{
|
|
left: value{
|
|
v: left,
|
|
},
|
|
right: value{
|
|
v: right,
|
|
},
|
|
}, true
|
|
}
|
|
|
|
// Check left
|
|
newPair, split := v.p.left.split()
|
|
if split {
|
|
if newPair != nil {
|
|
v.p.left.p = newPair
|
|
v.p.left.v = 0
|
|
}
|
|
return nil, split
|
|
}
|
|
|
|
// Check left
|
|
newPair, split = v.p.right.split()
|
|
if split {
|
|
if newPair != nil {
|
|
v.p.right.p = newPair
|
|
v.p.right.v = 0
|
|
}
|
|
return nil, split
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
func (p *pair) copy() *pair {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
|
|
return &pair{
|
|
left: p.left.copy(),
|
|
right: p.right.copy(),
|
|
}
|
|
}
|
|
|
|
func (v *value) copy() value {
|
|
return value{
|
|
p: v.p.copy(),
|
|
v: v.v,
|
|
}
|
|
}
|
|
|
|
func (v *value) print() string {
|
|
if v == nil {
|
|
return "No value to print"
|
|
}
|
|
|
|
if v.p == nil {
|
|
return fmt.Sprintf("%d", v.v)
|
|
}
|
|
|
|
s := "["
|
|
s += v.p.left.print()
|
|
s += ","
|
|
s += v.p.right.print()
|
|
s += "]"
|
|
|
|
return s
|
|
}
|
|
|
|
func parse(input string) value {
|
|
v := value{}
|
|
rawValue := ""
|
|
foundPair := false
|
|
bracketCount := 0
|
|
// Look for the comma in outermost pair
|
|
commaIndex := 0
|
|
for i, r := range input {
|
|
if bracketCount == 0 && r == '[' {
|
|
foundPair = true
|
|
rawValue = ""
|
|
}
|
|
|
|
switch r {
|
|
case '[':
|
|
bracketCount++
|
|
if bracketCount != 1 {
|
|
rawValue += string(r)
|
|
}
|
|
case ']':
|
|
bracketCount--
|
|
if bracketCount != 0 {
|
|
rawValue += string(r)
|
|
}
|
|
case ',':
|
|
if bracketCount == 1 {
|
|
commaIndex = i
|
|
}
|
|
rawValue += string(r)
|
|
default:
|
|
rawValue += string(r)
|
|
}
|
|
}
|
|
|
|
if foundPair {
|
|
v.p = &pair{
|
|
left: parse(rawValue[:commaIndex-1]),
|
|
right: parse(rawValue[commaIndex:]),
|
|
}
|
|
} else {
|
|
number, err := strconv.Atoi(rawValue)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
v.v = number
|
|
}
|
|
|
|
return v
|
|
}
|