--- /dev/null
+#load "core/std"
+
+PART :: 2
+
+use core
+use core.alloc {arena}
+
+num_allocator: Allocator;
+
+SnailNum :: struct {
+ parent: ^SnailNum;
+ left, right: ^SnailNum;
+
+ left_val, right_val: u32;
+}
+
+
+#inject SnailNum {
+ allocator: Allocator;
+
+ make :: () => {
+ return make(SnailNum, SnailNum.allocator);
+ }
+
+ make_pair :: (left, right: u32) => {
+ n := SnailNum.make();
+ n.left_val = left;
+ n.right_val = right;
+ return n;
+ }
+
+ clone :: (n: ^SnailNum) -> ^SnailNum {
+ if n == null do return null;
+
+ new_num := SnailNum.make();
+ new_num->set_left(SnailNum.clone(n.left));
+ new_num->set_right(SnailNum.clone(n.right));
+ new_num.left_val = n.left_val;
+ new_num.right_val = n.right_val;
+
+ return new_num;
+ }
+
+ add :: (a, b: ^SnailNum) => {
+ if a == null do return b;
+ if b == null do return a;
+
+ new_root := SnailNum.make();
+ new_root->set_left(a);
+ new_root->set_right(b);
+
+ while new_root->reduce() ---
+
+ return new_root;
+ }
+
+ reduce :: (use n: ^SnailNum) -> (reduced_something: bool) {
+ if r, _ := n->reduce_explodes(); r do return true;
+ if r := n->reduce_splits(); r do return true;
+ return false;
+ }
+
+ reduce_explodes :: (use n: ^SnailNum, depth := 0) -> (reduced_something: bool, zero_node: bool) {
+ if depth <= 3 {
+ did_reduce, zero_node := false, false;
+
+ if left != null {
+ if did_reduce, zero_node = left->reduce_explodes(depth + 1); zero_node {
+ left = null;
+ }
+ }
+
+ if right != null && !did_reduce {
+ if did_reduce, zero_node = right->reduce_explodes(depth + 1); zero_node {
+ right = null;
+ }
+ }
+
+ return did_reduce, false;
+ }
+
+ pleft := n->pair_to_left();
+ pright := n->pair_to_right();
+ if pleft != null do *pleft += left_val;
+ if pright != null do *pright += right_val;
+
+ left_val = 0;
+ right_val = 0;
+
+ return true, true;
+ }
+
+ reduce_splits :: (use n: ^SnailNum) -> (reduced_something: bool) {
+ if left != null {
+ if left->reduce_splits() {
+ return true;
+ }
+
+ } elseif left_val >= 10 {
+ l1, l2 := split_number(left_val);
+ n->set_left(SnailNum.make_pair(l1, l2));
+ left_val = 0;
+ return true;
+ }
+
+ if right != null {
+ if right->reduce_splits() {
+ return true;
+ }
+
+ } elseif right_val >= 10 {
+ r1, r2 := split_number(right_val);
+ n->set_right(SnailNum.make_pair(r1, r2));
+ right_val = 0;
+ return true;
+ }
+
+ return false;
+
+ split_number :: (n: u32) -> (u32, u32) {
+ h := n / 2;
+ return h, h + (0 if n % 2 == 0 else 1);
+ }
+ }
+
+ set_left :: (parent, new_left: ^SnailNum) {
+ parent.left_val = 0;
+ parent.left = new_left;
+ if new_left != null do new_left.parent = parent;
+ }
+
+ set_right :: (parent, new_right: ^SnailNum) {
+ parent.right_val = 0;
+ parent.right = new_right;
+ if new_right != null do new_right.parent = parent;
+ }
+
+ pair_to_left :: (n: ^SnailNum) -> ^u32 {
+ while n.parent != null && n.parent.left == n {
+ n = n.parent;
+ }
+
+ if n.parent == null do return null;
+
+ if n.parent.left == null do return ^n.parent.left_val;
+
+ n = n.parent.left;
+
+ while n.right != null {
+ n = n.right;
+ }
+
+ return ^n.right_val;
+ }
+
+ pair_to_right :: (n: ^SnailNum) -> ^u32 {
+ while n.parent != null && n.parent.right == n {
+ n = n.parent;
+ }
+
+ if n.parent == null do return null;
+
+ if n.parent.right == null do return ^n.parent.right_val;
+
+ n = n.parent.right;
+
+ while n.left != null {
+ n = n.left;
+ }
+
+ return ^n.left_val;
+ }
+
+ magnitude :: (use n: ^SnailNum) => {
+ if n == null {
+ return 0;
+ }
+
+ return 3 * (left_val + left->magnitude())
+ + 2 * (right_val + right->magnitude());
+ }
+
+ parse :: (line: ^str) -> ^SnailNum {
+ string.advance(line); // [
+
+ root := SnailNum.make();
+ if line.data[0] == #char "[" {
+ root->set_left(SnailNum.parse(line));
+ } else {
+ root.left_val = ~~ conv.str_to_i64(line);
+ }
+
+ string.advance(line); // ,
+
+ if line.data[0] == #char "[" {
+ root->set_right(SnailNum.parse(line));
+ } else {
+ root.right_val = ~~ conv.str_to_i64(line);
+ }
+
+ string.advance(line); // ]
+
+ return root;
+ }
+
+ format :: (output: ^conv.Format_Output, s: ^conv.Format, use n: ^SnailNum) {
+ if left == null && right == null {
+ conv.format(output, "[{},{}]", left_val, right_val);
+ }
+ elseif left == null && right != null {
+ conv.format(output, "[{},{*}]", left_val, right);
+ }
+ elseif left != null && right == null {
+ conv.format(output, "[{*},{}]", left, right_val);
+ }
+ elseif left != null && right != null {
+ conv.format(output, "[{*},{*}]", left, right);
+ }
+ }
+}
+
+
+
+main :: () {
+ num_arena := arena.make(context.allocator, 64 * 1024);
+ SnailNum.allocator = alloc.as_allocator(^num_arena);
+
+ conv.register_custom_formatter(SnailNum.format);
+
+ for file: os.with_file("./tests/aoc-2021/input/day18.txt") {
+ r := io.reader_make(file);
+
+ #if PART == 1 {
+ s: ^SnailNum = null;
+
+ for line: r->lines() {
+ n := SnailNum.parse(^line);
+ s = s->add(n);
+ }
+
+ printf("{*}\n", s->magnitude());
+ }
+
+ #if PART == 2 {
+ nums := make([..] ^SnailNum);
+ for line: r->lines() {
+ nums << SnailNum.parse(^line);
+ }
+
+ maximum := 0;
+ max_i, max_j : i32;
+
+ for i: nums.count {
+ for j: nums.count {
+ if i == j do continue;
+
+ n1 := nums[i]->clone();
+ n2 := nums[j]->clone();
+
+ mag := n1->add(n2)->magnitude();
+ if mag >= maximum {
+ maximum = mag;
+ max_i, max_j = i, j;
+ }
+ }
+ }
+
+ println(maximum);
+ printf("i: {*}\n", nums[max_i]);
+ printf("j: {*}\n", nums[max_j]);
+ }
+ }
+}
--- /dev/null
+[[[[7,1],[0,0]],[6,[8,2]]],[8,[3,8]]]
+[[[3,6],[9,4]],[[[5,9],5],[8,0]]]
+[[[2,2],2],[1,[[1,6],7]]]
+[[[[0,9],7],[[3,2],8]],[6,[7,9]]]
+[[[[4,1],6],[[7,6],[2,2]]],[[[1,1],9],4]]
+[[[8,[3,7]],3],[[4,4],[[9,1],[3,5]]]]
+[[4,[8,2]],[1,[0,5]]]
+[8,[8,7]]
+[[[[2,2],7],[3,[4,5]]],[[4,6],[[2,5],4]]]
+[[[5,5],[[5,1],3]],[[2,[8,2]],[[6,9],[1,5]]]]
+[0,7]
+[[[[5,1],3],[8,[5,3]]],7]
+[[5,[2,[0,6]]],[[[5,5],2],[9,[8,0]]]]
+[[[[3,4],2],0],4]
+[[[[5,3],[2,7]],6],[[4,0],[9,[7,2]]]]
+[[[3,[2,5]],[3,3]],7]
+[[[[5,1],1],[4,8]],[[5,[8,3]],2]]
+[[4,[[8,1],[8,5]]],[[[4,1],0],6]]
+[[[5,5],[5,9]],[0,[[6,8],[0,1]]]]
+[4,[[[7,9],4],0]]
+[[[[0,1],7],[[3,6],5]],[8,[5,[6,1]]]]
+[[[7,7],[8,0]],[6,[8,[7,9]]]]
+[[[9,2],1],6]
+[[[4,4],[2,[5,0]]],[[[2,6],6],[5,[4,3]]]]
+[[2,[[4,7],5]],1]
+[[8,7],[[[2,0],7],[1,[0,3]]]]
+[[9,[[9,3],[9,5]]],[[8,7],[[4,1],[6,5]]]]
+[[3,4],[[9,4],5]]
+[[5,[[8,3],5]],1]
+[[0,[[9,0],[3,2]]],[2,[7,[5,1]]]]
+[[9,[[9,5],[8,6]]],[[4,4],[[3,8],[1,6]]]]
+[[[1,[5,2]],9],[[4,6],[3,[8,0]]]]
+[[1,7],[[1,7],9]]
+[[[[3,4],3],[[7,5],[9,1]]],[[[5,0],[3,0]],[[7,9],6]]]
+[[[7,2],[[1,0],[5,6]]],[[[3,7],[8,9]],6]]
+[[[[1,1],1],[[8,6],[9,8]]],[[[1,8],4],[8,9]]]
+[[[8,9],0],3]
+[[[1,7],[1,[3,9]]],[6,[0,[8,5]]]]
+[[0,5],[6,5]]
+[[[[6,8],[4,5]],[[7,4],6]],[[3,6],5]]
+[[8,[[0,9],8]],[9,[7,[7,9]]]]
+[0,[[[7,1],2],[[0,4],4]]]
+[[0,[[9,1],5]],[1,4]]
+[3,4]
+[[[9,3],[1,3]],[[[4,8],3],[[1,3],[9,0]]]]
+[[[[5,1],7],[[9,2],8]],[[[6,8],[5,4]],[0,1]]]
+[8,[[1,[3,0]],[[7,9],4]]]
+[[[6,4],[[2,9],[9,0]]],[7,[[0,0],3]]]
+[[3,[[9,6],6]],2]
+[[5,[[3,1],[7,5]]],[[[6,7],9],[[4,6],[5,2]]]]
+[[[4,[6,5]],8],[[6,[8,0]],[[9,3],3]]]
+[[[[4,9],[2,8]],9],[[[5,0],0],[[3,4],[2,8]]]]
+[[3,[7,1]],[9,[[1,8],7]]]
+[[9,1],[0,[[0,7],[7,1]]]]
+[[7,[0,[7,6]]],[[[5,3],1],[6,[4,5]]]]
+[8,[[[2,1],[6,9]],[[3,3],[4,6]]]]
+[0,[7,[3,0]]]
+[[[[1,6],3],[5,[8,0]]],[[[6,6],7],1]]
+[[[7,[8,3]],3],[[[2,8],5],[0,[9,5]]]]
+[[[[5,1],4],[[1,2],1]],7]
+[[[3,[7,5]],7],3]
+[[9,[6,[1,1]]],[[[4,1],[2,2]],[[9,5],[7,7]]]]
+[2,7]
+[[[9,[8,6]],[[9,0],[6,5]]],[[[6,7],5],[[7,7],[2,3]]]]
+[[[0,[6,4]],2],[4,[7,[7,5]]]]
+[[[[6,1],[9,1]],[[6,1],9]],[[2,6],0]]
+[[0,[[1,8],[3,5]]],[4,[[8,2],[4,2]]]]
+[[[[9,3],[4,2]],2],[[[2,1],[7,1]],[4,8]]]
+[[[3,[0,2]],3],8]
+[[[4,[4,9]],9],[[[4,4],5],9]]
+[[[[8,2],7],9],[[[1,0],[3,8]],[[7,7],0]]]
+[[[3,2],[9,7]],[[9,[8,2]],[[5,5],3]]]
+[[[7,[3,1]],[[8,3],1]],[[[8,6],[7,0]],4]]
+[[9,[[9,1],5]],[[4,[1,1]],2]]
+[[[[7,4],[0,3]],7],[8,[6,[3,3]]]]
+[5,5]
+[[6,7],[1,[7,[8,1]]]]
+[[1,[0,4]],7]
+[[[4,0],[[0,1],[2,2]]],[9,[[9,9],[3,0]]]]
+[[[6,0],[[8,6],3]],[[5,1],[[8,1],[2,7]]]]
+[[[[8,3],7],5],[9,[[5,1],8]]]
+[[[[4,0],[5,2]],[[0,0],7]],2]
+[[[[0,1],6],2],[[8,2],6]]
+[[[[2,4],1],[[6,7],9]],[[[1,6],9],3]]
+[[5,5],[[8,[7,7]],[5,8]]]
+[[6,[[9,2],[9,7]]],[[[8,5],[4,4]],7]]
+[[[9,[7,7]],[6,0]],[7,[[8,7],[1,2]]]]
+[[7,[6,2]],[[9,[5,2]],[1,4]]]
+[[[7,[5,9]],[[3,9],[4,5]]],[0,6]]
+[[9,[8,[2,2]]],[[9,7],[1,1]]]
+[[[[2,3],4],[[4,8],9]],[[9,[8,6]],[[0,9],0]]]
+[[0,[[9,3],0]],[8,8]]
+[[[[2,9],6],[[2,8],9]],[[[0,5],6],[[6,1],7]]]
+[[9,[[8,3],[5,8]]],[[7,[3,0]],3]]
+[[[4,[4,2]],0],1]
+[[[[9,6],[5,8]],[6,2]],[[[8,0],[7,0]],[[5,6],4]]]
+[[[8,0],[[4,3],[7,4]]],[[3,[7,9]],[[7,3],6]]]
+[[3,[5,[0,3]]],[5,4]]
+[[[[1,2],[6,3]],1],[[7,[5,2]],[[8,8],7]]]
+[[4,[[8,0],[7,1]]],[[8,[8,0]],[[1,5],3]]]