144 lines
4.4 KiB
Zig
144 lines
4.4 KiB
Zig
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,
|
|
};
|