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 node
const 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 node
if (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 node
if (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 node
const node = try a.create(SnailNumber);
node.parent = parent;
// explode the left side first, then the right too
inline 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 node
if (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(),
}