diff --git a/inputs/19/real.txt b/inputs/19/real.txt index 82f4833..ee40b04 100644 --- a/inputs/19/real.txt +++ b/inputs/19/real.txt @@ -399,4 +399,4 @@ rbgruwuugbgbrgrgugrgrrrwruuggubbwrbruubbbr rrrrwugrrrrrubrbrgwgrrgwrubgwuuuburburburwrrgbwu ruwgugbguwuwgrwbrbgwruwurbugwwgbwwugbrrbwguur ugrrwbbwwbwwbbururwguuwwurwrguggggugrwbugwwgwgg -gbwurruuubuurrwurbuwwwubbwbwbbrgrbrwbbwrrrbruwrwubbrbgwbwb +gbwurruuubuurrwurbuwwwubbwbwbbrgrbrwbbwrrrbruwrwubbrbgwbwb \ No newline at end of file diff --git a/solutions/19.zig b/solutions/19.zig index 8e47202..2cabd0d 100644 --- a/solutions/19.zig +++ b/solutions/19.zig @@ -9,7 +9,7 @@ pub fn main() !void { defer _ = gpa.deinit(); const allocator = gpa.allocator(); - const response = try partOne(false, false, allocator); + const response = try partTwo(false, false, allocator); print("{}\n", .{response}); } @@ -26,6 +26,8 @@ fn partOne(is_test_case: bool, debug: bool, allocator: std.mem.Allocator) !u16 { var known_valid_designs = std.StringHashMap(void).init(allocator); defer known_valid_designs.deinit(); + // I added in this minor optimization because my code was running _super_ slow and I couldn't figure out why. + // Then I realized I'd acccidentally left in a `std.time.sleep(1000000000)` that I'd introduced while debugging :P var known_invalid_designs = std.StringHashMap(void).init(allocator); defer known_invalid_designs.deinit(); for (towels) |towel| { @@ -77,12 +79,68 @@ fn parseTowels(line: []const u8, allocator: std.mem.Allocator) [][]const u8 { return response.toOwnedSlice() catch unreachable; } -test "partOne" { +// test "partOne" { +// var gpa = std.heap.GeneralPurposeAllocator(.{}){}; +// defer _ = gpa.deinit(); +// const allocator = gpa.allocator(); + +// const response = try partOne(true, true, allocator); +// print("Part One response is {}\n", .{response}); +// try expect(response == 6); +// } + +fn partTwo(is_test_case: bool, debug: bool, allocator: std.mem.Allocator) !u128 { + const input_file = try util.getInputFile("19", is_test_case); + const data = try util.readAllInputWithAllocator(input_file, allocator); + defer allocator.free(data); + + var data_lines_it = std.mem.splitScalar(u8, data, '\n'); + const towels = parseTowels(data_lines_it.next().?, allocator); + defer allocator.free(towels); + + _ = data_lines_it.next(); + + var ways_to_make_designs = std.StringHashMap(u128).init(allocator); + defer ways_to_make_designs.deinit(); + // Not priming the "ways to make design" count, here, because this isn't as simple as the boolean yes-no in the + // previous case - e.g. if we have towels `rbr`, `r`, and `br`, then the count for `rbr` should be 2, not 1. + + var count: u128 = 0; + while (data_lines_it.next()) |design| { + const ways = waysToMakeDesign(design, towels, &ways_to_make_designs, debug); + print("Found {} ways to make {s}\n", .{ ways, design }); + count += ways; + } + return count; +} + +fn waysToMakeDesign(design: []const u8, towels: [][]const u8, ways_to_make_designs: *std.StringHashMap(u128), debug: bool) u128 { + if (design.len == 0) { + return 1; + } + if (!(ways_to_make_designs.contains(design))) { + var accum: u128 = 0; + for (towels) |towel| { + if (design.len >= towel.len and std.mem.eql(u8, towel, design[0..towel.len])) { + const remainder = design[towel.len..]; + const response = waysToMakeDesign(remainder, towels, ways_to_make_designs, debug); + if (response > 0) { + log("Got response {} to add to accum {} for remainder {s}\n", .{ response, accum, remainder }, debug); + accum += response; + } + } + } + ways_to_make_designs.put(design, accum) catch unreachable; + } + return ways_to_make_designs.get(design).?; +} + +test "partTwo" { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); - const response = try partOne(true, true, allocator); - print("Part One response is {}\n", .{response}); - try expect(response == 6); + const response = try partTwo(true, true, allocator); + print("Part Two response is {}\n", .{response}); + try expect(response == 16); }