const std = @import("std");
const PATH = "inputs/day10.txt";
const GRID_ROWS = 140;
const GRID_COLS = 140;
const CostType = u13;
const Pipes = struct {
grid: [GRID_ROWS][GRID_COLS]u8,
start: [2]usize,
};
pub fn first(allocator: std.mem.Allocator) !usize {
const input = try std.fs.cwd().readFileAlloc(allocator, PATH, 512 * 512);
defer allocator.free(input);
var pipes = try parseInput(input);
var cost_grid = [_][GRID_COLS]CostType{[_]CostType{std.math.maxInt(CostType)} ** GRID_COLS} ** GRID_ROWS;
cost_grid[pipes.start[0]][pipes.start[1]] = 0;
try dijkstra(std.heap.page_allocator, &pipes, &cost_grid);
var max: usize = 0;
for (cost_grid) |row| {
for (row) |item| {
if (item != std.math.maxInt(CostType) and item > max)
max = item;
}
}
std.debug.assert(max != std.math.maxInt(CostType) - 1);
return max;
}
pub fn second(allocator: std.mem.Allocator) !usize {
const input = try std.fs.cwd().readFileAlloc(allocator, PATH, 512 * 512);
defer allocator.free(input);
return 1;
}
test "day10a" {
try std.testing.expectEqual(@as(usize, 6875), try first(std.testing.allocator));
}
test "day10b" {
try std.testing.expectEqual(@as(usize, 1), try second(std.testing.allocator));
}
fn parseInput(input: []const u8) !Pipes {
var lines = std.mem.splitScalar(u8, input, '\n');
var pipes: Pipes = undefined;
var row: usize = 0;
while (lines.next()) |line| : (row += 1) {
if (line.len == 0) break;
for (line, 0..) |ch, col| {
pipes.grid[row][col] = ch;
if (ch == 'S') {
pipes.start[0] = row;
pipes.start[1] = col;
}
}
}
return pipes;
}
const QueueType = struct {
state: [2]usize,
cost: usize,
fn lessThan(context: void, a: @This(), b: @This()) std.math.Order {
_ = context;
if (a.cost < b.cost) return .lt else if (a.cost == b.cost) return .eq else return .gt;
}
};
fn dijkstra(
allocator: std.mem.Allocator,
pipes: *Pipes,
cost_grid: *[GRID_ROWS][GRID_COLS]CostType,
) !void {
var queue = std.PriorityQueue(QueueType, void, comptime QueueType.lessThan).init(allocator, {});
defer queue.deinit();
{ // Handle start position and its neighbors
const start_directions = enum { north, south, west, east };
for (std.enums.values(start_directions)) |dir| {
const d: [2]i2 = switch (dir) {
.north => .{ -1, 0 },
.south => .{ 1, 0 },
.west => .{ 0, -1 },
.east => .{ 0, 1 },
};
const diffrow = @as(isize, @intCast(pipes.start[0])) + d[0];
if (diffrow < 0 or diffrow >= GRID_ROWS) continue;
const dr = @as(usize, @intCast(diffrow));
const diffcol = @as(isize, @intCast(pipes.start[1])) + d[1];
if (diffcol < 0 or diffcol >= GRID_COLS) continue;
const dc = @as(usize, @intCast(diffcol));
switch (dir) {
.north => {
switch (pipes.grid[dr][dc]) {
'|', '7', 'F' => {},
'.', '-', 'L', 'J' => continue,
else => unreachable,
}
},
.south => {
switch (pipes.grid[dr][dc]) {
'|', 'L', 'J' => {},
'.', '-', '7', 'F' => continue,
else => unreachable,
}
},
.west => {
switch (pipes.grid[dr][dc]) {
'-', 'L', 'F' => {},
'.', '|', 'J', '7' => continue,
else => unreachable,
}
},
.east => {
switch (pipes.grid[dr][dc]) {
'-', 'J', '7' => {},
'.', '|', 'L', 'F' => continue,
else => unreachable,
}
},
}
cost_grid[dr][dc] = 1;
try queue.add(.{ .state = .{ dr, dc }, .cost = 1 });
}
}
// Dijkstra...
while (queue.removeOrNull()) |curr| {
// change the directions based on the current pipe type
const dir = switch (pipes.grid[curr.state[0]][curr.state[1]]) {
'|' => [_][2]i2{ .{ -1, 0 }, .{ 1, 0 } },
'-' => [_][2]i2{ .{ 0, -1 }, .{ 0, 1 } },
'L' => [_][2]i2{ .{ -1, 0 }, .{ 0, 1 } },
'J' => [_][2]i2{ .{ -1, 0 }, .{ 0, -1 } },
'7' => [_][2]i2{ .{ 1, 0 }, .{ 0, -1 } },
'F' => [_][2]i2{ .{ 1, 0 }, .{ 0, 1 } },
'.' => continue,
else => unreachable,
};
for (dir) |d| {
const diffrow = @as(isize, @intCast(curr.state[0])) + d[0];
if (diffrow < 0 or diffrow >= GRID_ROWS) continue;
const dr = @as(usize, @intCast(diffrow));
const diffcol = @as(isize, @intCast(curr.state[1])) + d[1];
if (diffcol < 0 or diffcol >= GRID_COLS) continue;
const dc = @as(usize, @intCast(diffcol));
const cost = cost_grid[curr.state[0]][curr.state[1]] + 1;
if (cost < cost_grid[dr][dc]) {
cost_grid[dr][dc] = cost;
try queue.add(.{ .state = .{ dr, dc }, .cost = cost });
}
}
}
}
const test_input1 =
\\.....
\\.S-7.
\\.|.|.
\\.L-J.
\\.....
;
const test_input2 =
\\..F7.
\\.FJ|.
\\SJ.L7
\\|F--J
\\LJ...
;