Day 18 solution
This commit is contained in:
338
eighteen/snail.go
Normal file
338
eighteen/snail.go
Normal file
@@ -0,0 +1,338 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user