diff --git a/src/day12.zig b/src/day12.zig index e9b20c9..ddee97c 100644 --- a/src/day12.zig +++ b/src/day12.zig @@ -14,21 +14,117 @@ const Solution = struct { b: usize, }; -fn solve(alloc: mem.Allocator, input: []const u8) !Solution { - _ = alloc; +fn hash(alloc: mem.Allocator, pattern: []const u8, groups: []const u8, curr_group: u8) ![]const u8 { + var a = try std.ArrayList(u8).initCapacity(alloc, pattern.len + groups.len + curr_group); + try a.appendSlice(pattern); + try a.appendSlice(groups); + try a.append(curr_group); + return try a.toOwnedSlice(); +} +// Mystery records to whether they fit. +var memo: std.StringHashMap(u32) = undefined; + +fn copy(comptime T: type, alloc: mem.Allocator, s: []const T) ![]T { + var a = try std.ArrayList(T).initCapacity(alloc, s.len); + try a.insertSlice(0, s); + return try a.toOwnedSlice(); +} + +fn solvePattern(alloc: mem.Allocator, pattern: []const u8, groups: []const u8, curr_group: u8) !u32 { + std.debug.print("{s}, {any}, {}\n", .{ pattern, groups, curr_group }); + + const h = try hash(alloc, pattern, groups, curr_group); + if (memo.get(h)) |m| { + alloc.free(h); + std.debug.print("==> short-circuit {}\n", .{m}); + return m; + } + + if (pattern.len == 0) { + std.debug.print("=> done ok: {}\n", .{groups.len == 0}); + return if (groups.len == 0) 1 else 0; + } + + const res: u32 = b: { + switch (pattern[0]) { + '#' => { + // Too many # + if (groups.len == 0 or curr_group >= groups[0]) { + std.debug.print("== bad fit\n", .{}); + break :b 0; + } + // Special-case last # + if (pattern.len == 1 and groups.len == 1 and groups[0] == curr_group + 1) { + break :b 1; + } + break :b try solvePattern(alloc, pattern[1..], groups, curr_group + 1); + }, + '.' => { + if (curr_group > 0) { + if (groups.len == 0 or curr_group != groups[0]) { + // Too many # + std.debug.print("== bad fit\n", .{}); + break :b 0; + } else { + // Group done + std.debug.print("=> group {} ok\n", .{groups[0]}); + break :b try solvePattern(alloc, pattern[1..], groups[1..], 0); + } + } else { + // Not growing a group, skip. + break :b try solvePattern(alloc, pattern[1..], groups, 0); + } + }, + '?' => { + const p1 = try copy(u8, alloc, pattern); + defer alloc.free(p1); + const p2 = try copy(u8, alloc, pattern); + defer alloc.free(p2); + + p1[0] = '#'; + p2[0] = '.'; + + std.debug.print("=> test ?\n", .{}); + break :b try solvePattern(alloc, p1, groups, curr_group) + + try solvePattern(alloc, p2, groups, curr_group); + }, + else => unreachable, + } + }; + + std.debug.print("<= {s}, {any}, {} = {}\n", .{ pattern, groups, curr_group, res }); + try memo.put(h, res); + + return res; +} + +fn solve(alloc: mem.Allocator, input: []const u8) !Solution { + memo = std.StringHashMap(u32).init(alloc); + defer memo.deinit(); + + var sum: u32 = 0; var it = util.splitLines(input); while (it.next()) |line| { if (line.len == 0) continue; var lit = util.splitSpace(line); - const record = lit.next().?; - _ = record; - const groups = lit.next().?; - _ = groups; + const pattern = lit.next().?; + const raw_groups = lit.next().?; + const groups = try util.parseIntsScalar(u8, alloc, raw_groups, .{ .sep = ',' }); + defer alloc.free(groups); + + std.debug.print("-----------------------------------------------\n", .{}); + sum += try solvePattern(alloc, pattern, groups, 0); + std.debug.print("{}\n", .{sum}); } - return .{ .a = 0, .b = 0 }; + var mkeys = memo.keyIterator(); + while (mkeys.next()) |k| { + alloc.free(k.*); + } + + return .{ .a = sum, .b = 0 }; } test "silver" { @@ -46,8 +142,13 @@ test "silver" { test "gold" { const input = - \\ + \\???.### 1,1,3 + \\.??..??...?##. 1,1,3 + \\?#?#?#?#?#?#?#? 1,3,1,6 + \\????.#...#... 4,1,1 + \\????.######..#####. 1,6,5 + \\?###???????? 3,2,1 ; const sln = try solve(std.testing.allocator, input); - try std.testing.expectEqual(@as(usize, 0), sln.b); + try std.testing.expectEqual(@as(usize, 525152), sln.b); }