diff --git a/2021/day18/main.go b/2021/day18/main.go new file mode 100644 index 0000000..efb970b --- /dev/null +++ b/2021/day18/main.go @@ -0,0 +1,222 @@ +package main + +import ( + "fmt" + + "github.com/jdkaplan/advent-of-code/aoc" +) + +func main() { + lines := aoc.Input().ReadLines("day18.txt") + fmt.Println(part1(lines)) +} + +func part1(lines []string) int { + var snails []*Node + for _, line := range lines { + snails = append(snails, Tree(line)) + } + n := snails[0] + for _, x := range snails[1:] { + n = add(n, x) + for n.Reduce() { + } + } + return n.Magnitude() +} + +func add(a, b *Node) *Node { + n := Pair(a, b) + link(a.B().Right(), b.A().Left()) + return n +} + +type Node struct { + v *int + a, b *Node + l, r *Node +} + +func Tree(line string) *Node { + root, extra := parseNode(chars(line)) + if len(extra) > 0 { + panic(extra) + } + + q := queue(&root) + for i := 0; i < len(q)-1; i++ { + l, r := q[i], q[i+1] + link(l, r) + } + + return &root +} + +func Leaf(v int) *Node { + return &Node{v: &v} +} + +func Pair(a, b *Node) *Node { + return &Node{a: a, b: b} +} + +func (n *Node) Magnitude() int { + if n.IsLeaf() { + return *n.v + } + return 3*n.a.Magnitude() + 2*n.b.Magnitude() +} + +func (n *Node) A() *Node { + for !n.IsLeaf() { + n = n.a + } + return n +} + +func (n *Node) B() *Node { + for !n.IsLeaf() { + n = n.b + } + return n +} + +func (n *Node) Left() *Node { + for n.l != nil { + n = n.l + } + return n +} + +func (n *Node) Right() *Node { + for n.r != nil { + n = n.r + } + return n +} + +func (n Node) IsLeaf() bool { + return n.v != nil +} + +func (n Node) String() string { + if n.IsLeaf() { + return fmt.Sprintf("%d", *n.v) + } + return fmt.Sprintf("[%s,%s]", n.a, n.b) +} + +func (n *Node) Reduce() bool { + if n.Explode(0) { + return true + } + return n.Split() +} + +func (n *Node) Split() bool { + if !n.IsLeaf() { + if n.a.Split() { + return true + } + if n.b.Split() { + return true + } + return false + } + + v := *(n.v) + if v < 10 { + return false + } + + a := v / 2 + b := (v + 1) / 2 + n.a, n.b, n.v = Leaf(a), Leaf(b), nil + link(n.l, n.a) + link(n.a, n.b) + link(n.b, n.r) + return true +} + +func (n *Node) Explode(depth int) bool { + if n.IsLeaf() { + return false + } + + if n.a.Explode(depth + 1) { + return true + } + if n.b.Explode(depth + 1) { + return true + } + + if depth < 4 { + return false + } + + if n.a.l != nil { + *n.a.l.v += *n.a.v + } + if n.b.r != nil { + *n.b.r.v += *n.b.v + } + zero := 0 + l, r := n.a.l, n.b.r + n.a, n.b, n.v = nil, nil, &zero + link(l, n) + link(n, r) + return true +} + +func link(l, r *Node) { + if l != nil { + l.r = r + } + if r != nil { + r.l = l + } +} + +func parseNode(line []rune) (Node, []rune) { + if line[0] == '[' { + return parsePair(line) + } + return parseLeaf(line) +} + +func parsePair(line []rune) (Node, []rune) { + _, line = consume('[', line) + a, line := parseNode(line) + _, line = consume(',', line) + b, line := parseNode(line) + _, line = consume(']', line) + return Node{a: &a, b: &b}, line +} + +func parseLeaf(line []rune) (Node, []rune) { + d, line := line[0], line[1:] + v := int(d - '0') + return Node{v: &v}, line +} + +func queue(n *Node) []*Node { + if n.IsLeaf() { + return []*Node{n} + } + return append(queue(n.a), queue(n.b)...) +} + +func chars(s string) (runes []rune) { + for _, r := range s { + runes = append(runes, r) + } + return +} + +func consume(expected rune, rs []rune) (rune, []rune) { + r, rs := rs[0], rs[1:] + if r != expected { + panic(fmt.Sprintf("Expected %s, got %s", string(expected), string(r))) + } + return r, rs +}