diff --git a/2021/18/go.mod b/2021/18/go.mod new file mode 100644 index 0000000..13013cf --- /dev/null +++ b/2021/18/go.mod @@ -0,0 +1,9 @@ +module AoC/2021/18 + +require AoC/2021/common v0.0.0 + +require github.com/joho/godotenv v1.4.0 // indirect + +replace AoC/2021/common v0.0.0 => ../common + +go 1.17 diff --git a/2021/18/go.sum b/2021/18/go.sum new file mode 100644 index 0000000..8c9f290 --- /dev/null +++ b/2021/18/go.sum @@ -0,0 +1,2 @@ +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= diff --git a/2021/18/main.go b/2021/18/main.go new file mode 100644 index 0000000..9ad2799 --- /dev/null +++ b/2021/18/main.go @@ -0,0 +1,290 @@ +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 + }) +}