const std = @import("std");
const builtin = @import("builtin");
fn readInputFile(allocator: std.mem.Allocator, filename: []const u8) ![]u32 {
var result = std.ArrayList(u32).init(allocator);
const file = try std.fs.cwd().openFile(filename, .{ .read = true });
defer file.close();
const reader = file.reader();
while (true) {
var line: []u8 = undefined;
if (builtin.os.tag == .windows) {
// NOTE: Read another byte on windows due to two-byte eol.
line = reader.readUntilDelimiterAlloc(allocator, '\r', 512) catch break;
_ = try reader.readByte();
} else {
line = reader.readUntilDelimiterAlloc(allocator, '\n', 512) catch break;
}
defer allocator.free(line);
for (line) |char| {
try result.append(@as(u32, char - 48));
}
}
return result.items;
}
pub fn task1(width: u32, height: u32, input: []u32) u32 {
var result: u32 = 0;
var y: u32 = 0;
while (y < height) : (y += 1) {
var i = y * width;
var x: u32 = 0;
while (x < width) : (x += 1) {
var j = i + x;
var depth = input[j];
var is_low_point: bool = true;
if (y > 0 and input[j - width] <= depth) {
is_low_point = false;
}
if (y < height - 1 and input[j + width] <= depth) {
is_low_point = false;
}
if (x > 0 and input[j - 1] <= depth) {
is_low_point = false;
}
if (x < width - 1 and input[j + 1] <= depth) {
is_low_point = false;
}
if (is_low_point) {
// std.log.debug("low point->{}", .{depth});
result += depth + 1;
}
}
}
return result;
}
fn drawGrid(width: u32, height: u32, grid: []u32) void {
var y: u32 = 0;
std.log.debug("grid", .{});
while (y < height) : (y += 1) {
std.log.debug("{any}", .{grid[(y * width)..((y + 1) * width)]});
}
}
fn flood(x: u32, y: u32, width: u32, height: u32, grid: []u32) u32 {
var result: u32 = 1;
const j = (y * width) + x;
grid[j] = 9;
if (y > 0 and grid[j - width] != 9) {
result += flood(x, y - 1, width, height, grid);
}
if (y < height - 1 and grid[j + width] != 9) {
result += flood(x, y + 1, width, height, grid);
}
if (x > 0 and grid[j - 1] != 9) {
result += flood(x - 1, y, width, height, grid);
}
if (x < width - 1 and grid[j + 1] != 9) {
result += flood(x + 1, y, width, height, grid);
}
return result;
}
const desc_u32 = std.sort.desc(u32);
pub fn task2(allocator: std.mem.Allocator, width: u32, height: u32, input: []u32) !u32 {
var result: u32 = 1;
var basins = std.ArrayList(u32).init(allocator);
defer basins.clearAndFree();
var y: u32 = 0;
while (y < height) : (y += 1) {
var i = y * width;
var x: u32 = 0;
while (x < width) : (x += 1) {
if (input[i + x] != 9) {
const basin = flood(x, y, width, height, input);
// drawGrid(width, height, input);
try basins.append(basin);
}
}
}
std.sort.sort(u32, basins.items, {}, desc_u32);
for (basins.items[0..3]) |i| {
result *= i;
}
return result;
}
pub fn main() !void {
var buffer: [2000000]u8 = undefined;
var fixed_buffer = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fixed_buffer.allocator();
const input = try readInputFile(allocator, "input.txt");
const task_1_result = task1(100, 100, input);
std.log.info("Task 1 result: {}", .{task_1_result});
const task_2_result = task2(allocator, 100, 100, input);
std.log.info("Task 2 result: {}", .{task_2_result});
}