const std = @import("std"); pub fn main() anyerror!void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const alloc = &arena.allocator; // Read our input var f = try std.fs.cwd().openFile("input", .{}); defer f.close(); var contents = try f.readToEndAlloc(alloc, std.math.maxInt(u32)); defer alloc.free(contents); var x: usize = 0; var y: usize = 0; var it = std.mem.tokenize(contents, "\n"); while (it.next()) |l| { x = l.len; y += 1; } std.log.debug("Map is {}, {}", .{x, y}); const width = x; const height = y; var map = try alloc.alloc(u8, x * y); defer alloc.free(map); it = std.mem.tokenize(contents, "\n"); y = 0; while (it.next()) |line| { x = 0; for (line) |c, i| { map[(width * y) + x] = try std.fmt.parseInt(u8, line[i..i+1], 10); x += 1; } y += 1; } // Determine local minima var local_minima = std.AutoHashMap(Point, u8).init(alloc); defer local_minima.deinit(); for (map) |v, i| { var adjacents = get_adjacent_points(i, width, height); //std.log.debug("Index {}: adjacents {any}", .{i, adjacents}); var is_minima = true; for (adjacents) |adj| { if (adj) |a| { if (map[a] <= v) { // std.log.debug( // "Index {} (Point {},{}) not local minima due to index {} (Point {},{}): {} <= {}", // .{i, i % width, i / height, a, a % width, a / height, map[a], v}); is_minima = false; break; } } } if (is_minima) { try local_minima.put(.{.x = i % width, .y = i / height}, v); } } std.log.debug("Found {} local minima", .{local_minima.count()}); var mit = local_minima.iterator(); var danger_value: u16 = 0; while (mit.next()) |kv| { danger_value += kv.value_ptr.* + 1; } std.log.info("[Part 1] The risk level for all the low points on the map is {}", .{danger_value}); var basin_sizes = std.ArrayList(usize).init(alloc); defer basin_sizes.deinit(); mit = local_minima.iterator(); while (mit.next()) |kv| { var point = kv.key_ptr.*; var points_to_check = std.ArrayList(Point).init(alloc); defer points_to_check.deinit(); try points_to_check.append(point); var basin = std.AutoHashMap(Point, void).init(alloc); defer basin.deinit(); try basin.put(point, undefined); while (points_to_check.items.len > 0) { point = points_to_check.pop(); var i = point.x + point .y * width; var adjacents = get_adjacent_points(i, width, height); for (adjacents) |adj| { if (adj) |a| { if (map[a] != 9) { point = .{.x = a % width, .y = a / width}; if (basin.contains(point)) { continue; } else { try points_to_check.append(point); try basin.put(point, undefined); } } } } } try basin_sizes.append(basin.count()); //std.log.debug("Basin for {}, {} is of size {}", // .{kv.key_ptr.*.x, kv.key_ptr.*.y, basin.count()}); } std.sort.sort(usize, basin_sizes.items, {}, comptime std.sort.desc(usize)); std.log.info("[Part 2] Product of 3 largest basins is: {}", .{basin_sizes.items[0] * basin_sizes.items[1] * basin_sizes.items[2]}); } fn get_adjacent_points(i: usize, width: usize, height: usize) [4]?usize { var adjacents = [4] ?usize { null, null, null, null, }; var ai: usize = 0; if ((i % width) != 0) { // left side adjacents[ai] = i - 1; ai += 1; } if (i >= width) { // top side adjacents[ai] = i - width; ai += 1; } if ((i % width) != (width - 1)) { // right side adjacents[ai] = i + 1; ai += 1; } if (i < ((height - 1) * width)) { // bottom side adjacents[ai] = i + width; ai += 1; } return adjacents; } pub const Point = struct { x: usize, y: usize, };