aoc-2024/day10.zig

363 lines
9.4 KiB
Zig
Raw Normal View History

2024-12-10 15:26:12 +01:00
const std = @import("std");
const utils = @import("lib/utils.zig");
const spice = @import("lib/spice.zig");
2024-12-11 22:22:04 +01:00
const day = "10";
const filename = "inputs/day" ++ day ++ ".txt";
2024-12-10 15:26:12 +01:00
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());
2024-12-11 22:22:04 +01:00
try std.io.getStdOut().writer().print("Day " ++ day ++ ", part 1: {}\n", .{result});
2024-12-10 15:26:12 +01:00
}
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader());
2024-12-11 22:22:04 +01:00
try std.io.getStdOut().writer().print("Day " ++ day ++ ", part 2: {}\n", .{result});
2024-12-10 15:26:12 +01:00
}
}
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);
}