KHVGYFXXJZY762NIUJXIMLYMKIZC3IKKAVLTA3A6FO2LWEIW26KQC const SnailType = u6;const SnailItem = union(enum) {num: SnailType,pair: *SnailNumber,};const SnailNumber = struct {left: ?SnailItem = null,right: ?SnailItem = null,parent: ?*SnailNumber = null,fn add(self: *@This(), a: std.mem.Allocator, input: *Str) !*SnailNumber {var pos: usize = 1;const next = try parseSnail(a, input, null, &pos);
const root = try a.create(SnailNumber);root.left = SnailItem{ .pair = self };root.right = SnailItem{ .pair = next };root.parent = null; // segfaults without this line :-(self.parent = root;next.parent = root;return root;}};
fn reduce(a: std.mem.Allocator, sn: *SnailNumber) anyerror!void {if (checkExplode(a, sn, 0)) |node| {explode(a, node);try reduce(a, sn);} else if (checkSplit(a, sn)) |node| {try split(a, node);try reduce(a, sn);}}
const SnailType = u6;
explodeLeft(node);explodeRight(node);
const SnailNumber = struct {left: ?SnailItem = null,right: ?SnailItem = null,parent: ?*SnailNumber = null,fn parse(a: std.mem.Allocator, in: *Str, parent: ?*SnailNumber, pos: *usize) anyerror!*@This() {// allocate and set up new nodeconst node = try a.create(SnailNumber);node.parent = parent;var right: bool = false;
const par = node.parent.?;if (par.left.? == .pair and par.left.?.pair == node) {par.left = SnailItem{ .num = 0 };} else {par.right = SnailItem{ .num = 0 };
while (pos.* < in.len) : (pos.* += 1) {var ch = in.*[pos.*];switch (ch) {']' => {break;},'[' => {pos.* += 1;if (right) {node.right = SnailItem{ .pair = try parse(a, in, node, pos) };} else {node.left = SnailItem{ .pair = try parse(a, in, node, pos) };}},',' => {right = true;},else => {if (right) {node.right = SnailItem{ .num = try std.fmt.parseUnsigned(SnailType, in.*[pos.* .. pos.* + 1], 10) };} else {node.left = SnailItem{ .num = try std.fmt.parseUnsigned(SnailType, in.*[pos.* .. pos.* + 1], 10) };}},}}return node;
}
fn add(self: *@This(), a: std.mem.Allocator, input: *Str) !*SnailNumber {var pos: usize = 1;const next = try parse(a, input, null, &pos);const root = try a.create(SnailNumber);root.left = SnailItem{ .pair = self };root.right = SnailItem{ .pair = next };root.parent = null; // segfaults without this line :-(
var prev = node;var it = node.parent;while (it) |par| : ({prev = par;it = par.parent;}) {if (par.left) |l| {// check if it is not the exploding nodeif (l == .pair and l.pair == prev) continue;break;}
return root;
target: while (it) |target| {// These must be _pointers_! Removing the & makes them local, so the changes// do not happen when they should.for ([2]*?SnailItem{ &target.right, &target.left }) |t| {if (t.*) |*child| {switch (child.*) {SnailItem.pair => |c| {if (c != prev) {it = c;continue :target;}},SnailItem.num => |*c| {c.* += left;return;},}}
fn reduce(self: *@This(), a: std.mem.Allocator) anyerror!void {if (self.checkExplode(a, 0)) |node| {node.explode(a);try self.reduce(a);} else if (self.checkSplit(a)) |node| {try node.split(a);try self.reduce(a);
var prev = node;var it = node.parent;while (it) |par| : ({prev = par;it = par.parent;}) {if (par.right) |r| {// check if it is not the exploding nodeif (r == .pair and r.pair == prev) continue;break;
if (self.left) |l| {if (l == .pair) {const ret = l.pair.checkExplode(a, depth + 1);if (ret != null) return ret;}
target: while (it) |target| {// These must be _pointers_! Removing the & makes them local, so the changes// do not happen when they should.for ([2]*?SnailItem{ &target.left, &target.right }) |t| {if (t.*) |*child| {switch (child.*) {SnailItem.pair => |c| {if (c != prev) {it = c;continue :target;}},SnailItem.num => |*c| {c.* += right;return;},}
if (self.right) |r| {if (r == .pair) {const ret = r.pair.checkExplode(a, depth + 1);if (ret != null) return ret;
fn checkExplode(a: std.mem.Allocator, node: *SnailNumber, depth: u3) ?*SnailNumber {if (depth == explode_limit) return node;if (node.left) |l| {if (l == .pair) {const ret = checkExplode(a, l.pair, depth + 1);if (ret != null) return ret;
fn checkSplit(self: *@This(), a: std.mem.Allocator) ?*SnailNumber {if (self.left) |l| {switch (l) {SnailItem.pair => |n| {const ret = n.checkSplit(a);if (ret != null) return ret;},SnailItem.num => |val| if (val > 9) return self,}
if (node.right) |r| {if (r == .pair) {const ret = checkExplode(a, r.pair, depth + 1);if (ret != null) return ret;
if (self.right) |r| {switch (r) {SnailItem.pair => |n| {const ret = n.checkSplit(a);if (ret != null) return ret;},SnailItem.num => |val| if (val > 9) return self,}
fn parseSnail(a: std.mem.Allocator, in: *Str, parent: ?*SnailNumber, pos: *usize) anyerror!*SnailNumber {// allocate and set up new nodeconst node = try a.create(SnailNumber);node.parent = parent;
// explode the left side first, then the right tooinline for ([_]Str{ "left", "right" }) |child_str| {const lr = @field(self, child_str).?.num;
var right: bool = false;while (pos.* < in.len) : (pos.* += 1) {var ch = in.*[pos.*];switch (ch) {']' => {break;},'[' => {pos.* += 1;if (right) {node.right = SnailItem{ .pair = try parseSnail(a, in, node, pos) };} else {node.left = SnailItem{ .pair = try parseSnail(a, in, node, pos) };
// Check parents of the exploding SnailNumber.var prev = self;var it = self.parent;while (it) |par| : ({prev = par;it = par.parent;}) {if (@field(par, child_str)) |kid| {// check if it is not the exploding nodeif (kid == .pair and kid.pair == prev) continue;break;
},',' => {right = true;},else => {if (right) {node.right = SnailItem{ .num = try std.fmt.parseUnsigned(SnailType, in.*[pos.* .. pos.* + 1], 10) };} else {node.left = SnailItem{ .num = try std.fmt.parseUnsigned(SnailType, in.*[pos.* .. pos.* + 1], 10) };}},}}
}
fn split(a: std.mem.Allocator, node: *SnailNumber) !void {if (node.left) |l| {if (l == .num and l.num > 9) {const new = try a.create(SnailNumber);new.left = SnailItem{ .num = l.num / 2 };new.right = SnailItem{ .num = l.num - l.num / 2 };new.parent = node;
target: while (it) |target| {// These must be _pointers_! Removing the & makes them local, so the changes// do not happen when they should.for ([2]*?SnailItem{ &@field(target, target_str[0]), &@field(target, target_str[1]) }) |t| {if (t.*) |*child| {switch (child.*) {.pair => |c| {if (c != prev) {it = c;continue :target;}},.num => |*c| {c.* += lr;break :target;},}}}}}
node.right = SnailItem{ .pair = new };return;}}}
fn split(self: *@This(), a: std.mem.Allocator) !void {if (self.left) |l| {if (l == .num and l.num > 9) {const new = try a.create(SnailNumber);new.left = SnailItem{ .num = l.num / 2 };new.right = SnailItem{ .num = l.num - l.num / 2 };new.parent = self;
fn checkSplit(a: std.mem.Allocator, node: *SnailNumber) ?*SnailNumber {if (node.left) |l| {switch (l) {SnailItem.pair => |n| {const ret = checkSplit(a, n);if (ret != null) return ret;},SnailItem.num => |val| if (val > 9) return node,
self.left = SnailItem{ .pair = new };return;}
return null;}fn printSnailNumber(node: *SnailNumber) void {std.debug.print("[", .{});if (node.left) |l| {switch (l) {SnailItem.num => |value| std.debug.print("{d}", .{value}),SnailItem.pair => |n| printSnailNumber(n),
fn print(self: @This()) void {std.debug.print("[", .{});if (self.left) |l| {switch (l) {SnailItem.num => |value| std.debug.print("{d}", .{value}),SnailItem.pair => |n| n.print(),}
}std.debug.print(",", .{});if (node.right) |r| {switch (r) {SnailItem.num => |value| std.debug.print("{d}", .{value}),SnailItem.pair => |n| printSnailNumber(n),
std.debug.print(",", .{});if (self.right) |r| {switch (r) {SnailItem.num => |value| std.debug.print("{d}", .{value}),SnailItem.pair => |n| n.print(),}