const std = @import("std"); const Span = @import("lib/zig-regex/src/regex.zig").Span; const Regex = @import("lib/zig-regex/src/regex.zig").Regex; const path = "data/day18/input.txt"; const RetType = u12; const SnailType = u6; const Str = []const u8; const explode_limit = 5; const RegExp = struct { number: Regex, last_number: Regex, exp_number: Regex, }; var reg_exp: RegExp = undefined; pub fn main() !void { var timer = try std.time.Timer.start(); const ret = try first(); const t = timer.lap() / 1000; try std.testing.expectEqual(@as(RetType, 3675), ret); std.debug.print("Day 18a result: {d} \t\ttime: {d}us\n", .{ ret, t }); } pub fn first() !RetType { const input = @embedFile(path); var in = std.mem.tokenize(u8, input, "\n"); const allocator = std.testing.allocator; var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); reg_exp.number = try Regex.compile(arena.allocator(), "(\\d+)"); reg_exp.last_number = try Regex.compile(arena.allocator(), "(\\d+)[^\\d+]+$"); reg_exp.exp_number = try Regex.compile(arena.allocator(), "^\\[(\\d+)[^\\d]+(\\d+)"); var ret: []const u8 = ""; while (in.next()) |line| { if (ret.len == 0) { ret = try reduce(arena.allocator(), &line[0..]); } else { var buf: [150]u8 = undefined; const next = try std.fmt.bufPrint(&buf, "[{s},{s}]", .{ ret, line }); ret = try reduce(arena.allocator(), &next[0..]); } } var pos: usize = 0; return (try magnitude(&ret, &pos)); } fn magnitude(input: *Str, pos: *usize) anyerror!RetType { var left: isize = -1; var right: isize = -1; var right_side: bool = false; while (pos.* < input.len) : (pos.* += 1) { if (input.*[pos.*] == ']') { break; } if (input.*[pos.*] == '[') { pos.* += 1; if (right_side) { right = try magnitude(input, pos); } else { left = try magnitude(input, pos); } continue; } switch (input.*[pos.*]) { ',' => right_side = true, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', => { if (right_side) { right = try std.fmt.parseUnsigned(isize, input.*[pos.* .. pos.* + 1], 10); } else { left = try std.fmt.parseUnsigned(isize, input.*[pos.* .. pos.* + 1], 10); } }, else => unreachable, } } if (left == -1 or right == -1) { return @intCast(RetType, left + right + 1); } return @intCast(RetType, 3 * left + 2 * right); } fn reduce(a: std.mem.Allocator, input: *Str) anyerror!Str { if (checkExplode(a, input)) { _ = try reduce(a, input); } else if (checkSplit(a, input)) { _ = try reduce(a, input); } return input.*; } fn checkExplode(a: std.mem.Allocator, input: *Str) bool { var count: u5 = 0; for (input.*) |ch, i| { if (ch == '[') { count += 1; if (count == explode_limit) { explode(a, input, i) catch unreachable; return true; } } else if (ch == ']') { count -= 1; } } return false; } fn checkSplit(a: std.mem.Allocator, input: *Str) bool { return split(a, input) catch unreachable; } fn split(a: std.mem.Allocator, input: *Str) !bool { // std.debug.print("split: {s}\n", .{input.*}); var two_digit = try Regex.compile(a, "\\d\\d+"); defer two_digit.deinit(); const two = (try two_digit.captures(input.*)) orelse return false; const split_coords = two.boundsAt(0).?; var ret = std.ArrayList(u8).init(a); try ret.appendSlice(input.*[0..split_coords.lower]); var buf: [2]u8 = undefined; const split_num = try std.fmt.parseUnsigned(SnailType, input.*[split_coords.lower..split_coords.upper], 10); try ret.append('['); try ret.appendSlice(try std.fmt.bufPrint(&buf, "{d}", .{split_num / 2})); try ret.append(','); try ret.appendSlice(try std.fmt.bufPrint(&buf, "{d}", .{std.math.ceil(@intToFloat(f32, split_num) / 2)})); try ret.append(']'); try ret.appendSlice(input.*[split_coords.upper..]); input.* = ret.toOwnedSlice(); return true; } fn explode(a: std.mem.Allocator, input: *Str, pivot: usize) !void { // std.debug.print("explode: {s} {d}\n", .{ input.*, pivot }); const exp_nums = (try reg_exp.exp_number.captures(input.*[pivot..])).?; // Exploding left item const exp_left = [2]usize{ pivot + exp_nums.boundsAt(1).?.lower, pivot + exp_nums.boundsAt(1).?.upper }; // Exploding right item const exp_right = [2]usize{ pivot + exp_nums.boundsAt(2).?.lower, pivot + exp_nums.boundsAt(2).?.upper }; // std.debug.print("expLeft: {s} expRight: {s}\n", .{ input.*[exp_left[0]..exp_left[1]], input.*[exp_right[0]..exp_right[1]] }); var new_string = std.ArrayList(u8).init(a); if (try reg_exp.last_number.captures(input.*[0..pivot])) |lefts| { if (lefts.boundsAt(1)) |l| { // std.debug.print("{any} {s}\n", .{ l, input.*[l.lower..l.upper] }); try new_string.appendSlice(input.*[0..l.lower]); const left_num = try std.fmt.parseUnsigned(SnailType, input.*[l.lower..l.upper], 10); const exp_left_num = try std.fmt.parseUnsigned(SnailType, input.*[exp_left[0]..exp_left[1]], 10); var buf: [2]u8 = undefined; try new_string.appendSlice(try std.fmt.bufPrint(&buf, "{d}", .{left_num + exp_left_num})); try new_string.appendSlice(input.*[l.upper .. exp_left[0] - 1]); } } else { try new_string.appendSlice(input.*[0 .. exp_left[0] - 1]); } try new_string.append('0'); if (try reg_exp.number.captures(input.*[exp_right[1] + 1 ..])) |rights| { if (rights.boundsAt(0)) |r_raw| { const r = Span{ .lower = r_raw.lower + exp_right[1] + 1, .upper = r_raw.upper + exp_right[1] + 1 }; // std.debug.print("{any} {s}\n", .{ r, input.*[r.lower..r.upper] }); try new_string.appendSlice(input.*[exp_right[1] + 1 .. r.lower]); const right_num = try std.fmt.parseUnsigned(SnailType, input.*[r.lower..r.upper], 10); const exp_left_num = try std.fmt.parseUnsigned(SnailType, input.*[exp_right[0]..exp_right[1]], 10); var buf: [2]u8 = undefined; try new_string.appendSlice(try std.fmt.bufPrint(&buf, "{d}", .{right_num + exp_left_num})); try new_string.appendSlice(input.*[r.upper..]); } } else { try new_string.appendSlice(input.*[exp_right[1] + 1 ..]); } input.* = new_string.toOwnedSlice(); } test "day18a" { try std.testing.expectEqual(@as(RetType, 3675), try first(std.testing.allocator)); }