diff --git a/day10.zig b/day10.zig new file mode 100644 index 0000000..656012a --- /dev/null +++ b/day10.zig @@ -0,0 +1,361 @@ +const std = @import("std"); + +const utils = @import("lib/utils.zig"); + +const spice = @import("lib/spice.zig"); + +const filename = "inputs/day10.txt"; + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const alloc = gpa.allocator(); + + { + const file_reader = try utils.FileReader.init(alloc, filename); + defer file_reader.deinit(); + + const result = try part1(alloc, file_reader.reader()); + + try std.io.getStdOut().writer().print("Day 10, part 1: {}\n", .{result}); + } + + { + const file_reader = try utils.FileReader.init(alloc, filename); + defer file_reader.deinit(); + + const result = try part2(alloc, file_reader.reader()); + + try std.io.getStdOut().writer().print("Day 10, part 2: {}\n", .{result}); + } +} + +const Pos = struct { + x: i32, + y: i32, +}; + +const Map = struct { + size_x: u32, + size_y: u32, + + map: []const []const u8, + + pub fn format(self: Map, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + _ = fmt; + _ = options; + + for (self.map) |line| { + for (line) |x| { + try writer.print("{} ", .{x}); + } + + _ = try writer.write("\n"); + } + } + + fn get(self: Map, x: i32, y: i32) ?u8 { + if (x < 0 or x >= @as(i32, @intCast(self.size_x)) or y < 0 or y >= @as(i32, @intCast(self.size_y))) { + return null; + } + + return self.map[@intCast(x)][@intCast(y)]; + } +}; + +fn findAllEnds(ends: *std.AutoHashMap(Pos, void), map: Map, start_pos: Pos) !void { + const findAllEndsRec = struct { + fn findAllEndsRec(_ends: *std.AutoHashMap(Pos, void), _map: Map, cur_pos: Pos, height: u8) !void { + const cur_x = cur_pos.x; + const cur_y = cur_pos.y; + + std.debug.assert(_map.get(cur_x, cur_y).? == height); + + if (height == 9) { + try _ends.put(cur_pos, {}); + + return; + } + + { + const new_x = cur_x - 1; + const new_y = cur_y; + + if (_map.get(new_x, new_y)) |new_height| { + if (new_height == height + 1) { + try findAllEndsRec(_ends, _map, .{ .x = new_x, .y = new_y }, new_height); + } + } + } + + { + const new_x = cur_x + 1; + const new_y = cur_y; + + if (_map.get(new_x, new_y)) |new_height| { + if (new_height == height + 1) { + try findAllEndsRec(_ends, _map, .{ .x = new_x, .y = new_y }, new_height); + } + } + } + + { + const new_x = cur_x; + const new_y = cur_y - 1; + + if (_map.get(new_x, new_y)) |new_height| { + if (new_height == height + 1) { + try findAllEndsRec(_ends, _map, .{ .x = new_x, .y = new_y }, new_height); + } + } + } + + { + const new_x = cur_x; + const new_y = cur_y + 1; + + if (_map.get(new_x, new_y)) |new_height| { + if (new_height == height + 1) { + try findAllEndsRec(_ends, _map, .{ .x = new_x, .y = new_y }, new_height); + } + } + } + } + }.findAllEndsRec; + + try findAllEndsRec(ends, map, start_pos, 0); +} + +fn countAllTrails(map: Map, start_pos: Pos) !u64 { + const countAllTrailsRec = struct { + fn countAllTrailsRec(_map: Map, cur_pos: Pos, height: u8) !u64 { + const cur_x = cur_pos.x; + const cur_y = cur_pos.y; + + std.debug.assert(_map.get(cur_x, cur_y).? == height); + + if (height == 9) { + return 1; + } + + var count: u64 = 0; + + { + const new_x = cur_x - 1; + const new_y = cur_y; + + if (_map.get(new_x, new_y)) |new_height| { + if (new_height == height + 1) { + count += try countAllTrailsRec(_map, .{ .x = new_x, .y = new_y }, new_height); + } + } + } + + { + const new_x = cur_x + 1; + const new_y = cur_y; + + if (_map.get(new_x, new_y)) |new_height| { + if (new_height == height + 1) { + count += try countAllTrailsRec(_map, .{ .x = new_x, .y = new_y }, new_height); + } + } + } + + { + const new_x = cur_x; + const new_y = cur_y - 1; + + if (_map.get(new_x, new_y)) |new_height| { + if (new_height == height + 1) { + count += try countAllTrailsRec(_map, .{ .x = new_x, .y = new_y }, new_height); + } + } + } + + { + const new_x = cur_x; + const new_y = cur_y + 1; + + if (_map.get(new_x, new_y)) |new_height| { + if (new_height == height + 1) { + count += try countAllTrailsRec(_map, .{ .x = new_x, .y = new_y }, new_height); + } + } + } + + return count; + } + }.countAllTrailsRec; + + return try countAllTrailsRec(map, start_pos, 0); +} + +fn part1(alloc: std.mem.Allocator, reader: anytype) !u64 { + var map_arena = std.heap.ArenaAllocator.init(alloc); + defer map_arena.deinit(); + + var map_list = std.ArrayList([]const u8).init(map_arena.allocator()); + defer map_list.deinit(); + + var line_reader = utils.lineReader(alloc, reader); + defer line_reader.deinit(); + + while (try line_reader.next()) |line| { + const map_line = try map_arena.allocator().alloc(u8, line.len); + + for (line, map_line) |c, *x| { + std.debug.assert('0' <= c and c <= '9'); + + x.* = c - '0'; + } + + try map_list.append(map_line); + } + + const size_x = map_list.items.len; + const size_y = map_list.items[0].len; + + for (map_list.items) |line| { + std.debug.assert(line.len == size_y); + } + + const map = Map{ .size_x = @intCast(size_x), .size_y = @intCast(size_y), .map = map_list.items }; + + var count: u64 = 0; + + var ends = std.AutoHashMap(Pos, void).init(alloc); + defer ends.deinit(); + + for (0..size_x) |x| { + for (0..size_y) |y| { + if (map.get(@intCast(x), @intCast(y)) != 0) { + continue; + } + + ends.clearRetainingCapacity(); + + try findAllEnds(&ends, map, .{ .x = @intCast(x), .y = @intCast(y) }); + + count += @intCast(ends.count()); + } + } + + return count; +} + +fn part2(alloc: std.mem.Allocator, reader: anytype) !u64 { + var map_arena = std.heap.ArenaAllocator.init(alloc); + defer map_arena.deinit(); + + var map_list = std.ArrayList([]const u8).init(map_arena.allocator()); + defer map_list.deinit(); + + var line_reader = utils.lineReader(alloc, reader); + defer line_reader.deinit(); + + while (try line_reader.next()) |line| { + const map_line = try map_arena.allocator().alloc(u8, line.len); + + for (line, map_line) |c, *x| { + std.debug.assert('0' <= c and c <= '9'); + + x.* = c - '0'; + } + + try map_list.append(map_line); + } + + const size_x = map_list.items.len; + const size_y = map_list.items[0].len; + + for (map_list.items) |line| { + std.debug.assert(line.len == size_y); + } + + const map = Map{ .size_x = @intCast(size_x), .size_y = @intCast(size_y), .map = map_list.items }; + + var count: u64 = 0; + + var ends = std.AutoHashMap(Pos, void).init(alloc); + defer ends.deinit(); + + for (0..size_x) |x| { + for (0..size_y) |y| { + if (map.get(@intCast(x), @intCast(y)) != 0) { + continue; + } + + ends.clearRetainingCapacity(); + + const num_trails = try countAllTrails(map, .{ .x = @intCast(x), .y = @intCast(y) }); + + count += num_trails; + } + } + + return count; +} + +test "part1 example" { + const alloc = std.testing.allocator; + + const example = + \\89010123 + \\78121874 + \\87430965 + \\96549874 + \\45678903 + \\32019012 + \\01329801 + \\10456732 + ; + + var stream = std.io.fixedBufferStream(example); + + const result = try part1(alloc, stream.reader()); + + try std.testing.expectEqual(36, result); +} + +test "part1 input" { + const alloc = std.testing.allocator; + + const file_reader = try utils.FileReader.init(alloc, filename); + defer file_reader.deinit(); + + const result = try part1(alloc, file_reader.reader()); + + try std.testing.expectEqual(629, result); +} + +test "part2 example" { + const alloc = std.testing.allocator; + + const example = + \\89010123 + \\78121874 + \\87430965 + \\96549874 + \\45678903 + \\32019012 + \\01329801 + \\10456732 + ; + + var stream = std.io.fixedBufferStream(example); + + const result = try part2(alloc, stream.reader()); + + try std.testing.expectEqual(81, result); +} + +test "part2 input" { + const alloc = std.testing.allocator; + + const file_reader = try utils.FileReader.init(alloc, filename); + defer file_reader.deinit(); + + const result = try part2(alloc, file_reader.reader()); + + try std.testing.expectEqual(1242, result); +} diff --git a/day9.zig b/day9.zig index 89ea072..506c47b 100644 --- a/day9.zig +++ b/day9.zig @@ -16,7 +16,7 @@ pub fn main() !void { const result = try part1(alloc, file_reader.reader()); - try std.io.getStdOut().writer().print("Day 8, part 1: {}\n", .{result}); + try std.io.getStdOut().writer().print("Day 9, part 1: {}\n", .{result}); } { @@ -25,7 +25,7 @@ pub fn main() !void { const result = try part2(alloc, file_reader.reader()); - try std.io.getStdOut().writer().print("Day 8, part 2: {}\n", .{result}); + try std.io.getStdOut().writer().print("Day 9, part 2: {}\n", .{result}); } } diff --git a/inputs/day10.txt b/inputs/day10.txt new file mode 100644 index 0000000..d03803e --- /dev/null +++ b/inputs/day10.txt @@ -0,0 +1,50 @@ +56760198543405456770107687012987873265430701234123 +47891087612514320889298596543236982176321878985054 +30932912503423411978321436760145453089490965076567 +21043803498598502565450345897896334569584324105498 +43256754387687643445601231056787276578675013212389 +50105620896014553238782322348996189432106522301210 +60234211205423469109695210567345023456987101232569 +71454301312345678100556501051269010787832198543478 +82365985407856743291447892340178732696949037643323 +99878876556989856782332309650065341054658743252110 +12389565445845698943221218761234456565789650167087 +03458890330532787892100321052442787074656782108996 +12766721221621056521098478934321692183245891210105 +00875437876521245430167569035630013290194300121234 +21980356985430130101456122128754324389980215432101 +32341245234561221212340033439961015671271326990123 +41239832101676678703121542345872343760362347887654 +50146701098787569254035671096112654854454558943210 +69655410889693452164549389987003569943003967656787 +78789324989582543076678432176124578752112876545490 +49670123476451001289986581065437669801198743454321 +34565404560302654345677893034458954321034652345210 +21670313401212701238766732123361056700125601216787 +10781223304343870349676678901272341811098700305891 +87690433210154901234587565078980110925643212456730 +98521049873269100448996652169765223434756965569821 +83430656794378234567385543258894304589807874321034 +12345690185123247855434789043210113676212981012125 +04396783276030110982329872178901923494345891234596 +65287654896543225671012763561032876587436780989687 +70132108987652334430101654432945214306525891078765 +89945345456701498521012346547876305211014342569854 +67876276789876567677890107236521456523210213410743 +56940189678930438988743298101430567894765104323212 +45434328509821321089654340122334567765894101298701 +50125613410030012127763019831021998568903210345692 +23498701322147897898892123742340867478912653210789 +10567654213456786721089054654356789302801743105678 +01678123402110995437658766789210676211010892234109 +67569016567021874378941043212101245432346781103201 +58454323898134565632332456701234306011015490321232 +49323454234012906701454327890965412102367305410147 +31012365125643812898765410378876543233458216568758 +21009876034756763019034101269989100146569325679669 +21898745129829654323129654352123299656478012784578 +30743231065018760563238765543034788798329983693056 +45656189654323011056769894672145632347012674212147 +01218088745896522349850123283034701456981065100238 +14309893236787631438943210190129892123672578921109 +25456782101896540127654301012010181034543465433212