const std = @import("std");
const Str = []const u8;
const PATH = "input/day12.txt";
pub fn first(allocator: std.mem.Allocator) !isize {
_ = allocator;
return sumNumbers(@embedFile(PATH));
}
pub fn second(allocator: std.mem.Allocator) !isize {
const input = @embedFile(PATH);
var sum = try sumNumbers(input);
sum -= try ignoreObject(allocator, input);
return sum;
}
test "day12a" {
try std.testing.expectEqual(@as(isize, 156366), try first(std.testing.allocator));
}
test "day12b" {
try std.testing.expectEqual(@as(isize, 96852), try second(std.testing.allocator));
}
fn sumNumbers(in: Str) !isize {
var sum: isize = 0;
var start_idx: usize = 0;
var in_number: bool = false;
var idx: usize = 0;
while (idx < in.len) : (idx += 1) {
switch (in[idx]) {
'0'...'9' => {
if (!in_number) {
start_idx = idx;
in_number = true;
}
},
else => {
if (in_number) {
if (in[start_idx - 1] == '-') {
sum += try std.fmt.parseInt(isize, in[start_idx - 1 .. idx], 10);
} else {
sum += try std.fmt.parseUnsigned(isize, in[start_idx..idx], 10);
}
in_number = false;
}
},
}
}
return sum;
}
test "sumNumbers" {
try std.testing.expectEqual(@as(isize, 6), try sumNumbers("[1,2,3]"));
try std.testing.expectEqual(@as(isize, 6), try sumNumbers("{\"a\":2,\"b\":4}"));
try std.testing.expectEqual(@as(isize, 3), try sumNumbers("{\"a\":{\"b\":4},\"c\":-1}"));
try std.testing.expectEqual(@as(isize, 0), try sumNumbers("[-1,{\"a\":1}]"));
}
fn ignoreObject(allocator: std.mem.Allocator, in: Str) !isize {
var ranges = std.ArrayList([2]usize).init(allocator);
defer ranges.deinit();
var idx: usize = 0;
red: while (idx < in.len) : (idx += 1) {
if (in[idx] == 'r' and in[idx + 1] == 'e' and in[idx + 2] == 'd') {
var curly_left: usize = 0;
var bracket: usize = 0;
var rstart: usize = idx - 1; // red
while (rstart >= 0) : (rstart -= 1) {
switch (in[rstart]) {
// handle arrays (brackets)
']' => bracket += 1,
'[' => {
if (bracket == 0) break;
bracket -= 1;
},
// handle objects
'{' => {
if (curly_left == 0) {
var curly_right: usize = 0;
var rstop: usize = idx + 3; // red
while (rstop < in.len) : (rstop += 1) {
switch (in[rstop]) {
'}' => {
if (curly_right == 0) {
// std.debug.print("{s}\n", .{in[rstart .. rstop + 1]});
// remove overlapping ranges
var i: usize = ranges.items.len;
while (i > 0) : (i -= 1) {
if (ranges.items[i - 1][0] > rstart and ranges.items[i - 1][1] < rstop + 1) {
// std.debug.print("remove: {}-{}\n", .{ ranges.items[i - 1][0], ranges.items[i - 1][1] });
_ = ranges.swapRemove(i - 1);
}
}
try ranges.append(.{ rstart, rstop + 1 });
idx = rstop + 1;
continue :red;
} else curly_right -= 1;
},
'{' => curly_right += 1,
else => {},
}
}
} else curly_left -= 1;
},
'}' => curly_left += 1,
else => {},
}
}
}
}
var sum: isize = 0;
for (ranges.items) |item| {
sum += try sumNumbers(in[item[0]..item[1]]);
}
return sum;
}