You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
297 lines
7.8 KiB
297 lines
7.8 KiB
const std = @import("std");
|
|
|
|
fn StackList(comptime T: type, comptime capacity_type: type, comptime capacity: capacity_type) type {
|
|
return struct {
|
|
const Self = @This();
|
|
mem: [capacity]T,
|
|
length: capacity_type,
|
|
|
|
fn add(self: *Self, value: T) void {
|
|
self.mem[self.length] = value;
|
|
self.length += 1;
|
|
}
|
|
|
|
fn addIfNotNull(self: *Self, nullable_value: ?T) void {
|
|
if (nullable_value) |value| {
|
|
self.add(value);
|
|
}
|
|
}
|
|
|
|
fn has(self: *Self, needle: T) bool {
|
|
for (0..self.length) |i| {
|
|
if (self.mem[i] == needle) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
fn getMutableSlice(self: *Self) []T {
|
|
return (&self.mem)[0..self.length];
|
|
}
|
|
|
|
fn getSlice(self: *const Self) []const T {
|
|
return self.mem[0..self.length];
|
|
}
|
|
|
|
fn init() Self {
|
|
return Self{
|
|
.mem = undefined,
|
|
.length = 0,
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
const CellStatus = packed struct(u8) {
|
|
left: bool = false,
|
|
right: bool = false,
|
|
up: bool = false,
|
|
down: bool = false,
|
|
_padding: u4 = 0,
|
|
};
|
|
|
|
const Task = packed struct(u32) {
|
|
x: u12,
|
|
y: u12,
|
|
status: CellStatus,
|
|
};
|
|
|
|
const Queue = StackList(Task, usize, 65534);
|
|
|
|
fn getUpTask(original_task: Task) ?Task {
|
|
if (original_task.x == 0) {
|
|
return null;
|
|
}
|
|
|
|
return .{
|
|
.x = original_task.x - 1,
|
|
.y = original_task.y,
|
|
.status = .{ .up = true },
|
|
};
|
|
}
|
|
|
|
fn getDownTask(original_task: Task, lines: []const []const u8) ?Task {
|
|
if (original_task.x + 1 >= lines.len) {
|
|
return null;
|
|
}
|
|
|
|
return .{
|
|
.x = original_task.x + 1,
|
|
.y = original_task.y,
|
|
.status = .{ .down = true },
|
|
};
|
|
}
|
|
|
|
fn getLeftTask(original_task: Task) ?Task {
|
|
if (original_task.y == 0) {
|
|
return null;
|
|
}
|
|
|
|
return .{
|
|
.x = original_task.x,
|
|
.y = original_task.y - 1,
|
|
.status = .{ .left = true },
|
|
};
|
|
}
|
|
|
|
fn getRightTask(original_task: Task, lines: []const []const u8) ?Task {
|
|
if (original_task.y + 1 >= lines[original_task.x].len) {
|
|
return null;
|
|
}
|
|
|
|
return .{
|
|
.x = original_task.x,
|
|
.y = original_task.y + 1,
|
|
.status = .{ .right = true },
|
|
};
|
|
}
|
|
|
|
fn solveForStart(lines: []const []const u8, firstTask: Task) usize {
|
|
var state = std.mem.zeroes([128][128]CellStatus);
|
|
var tasks = Queue.init();
|
|
|
|
tasks.add(firstTask);
|
|
|
|
var step: usize = 0;
|
|
while (step < tasks.length) : (step += 1) {
|
|
const task = tasks.mem[step];
|
|
var cell_state = &state[task.x][task.y];
|
|
var cell_value = lines[task.x][task.y];
|
|
|
|
if (task.status.left and !cell_state.*.left) {
|
|
cell_state.*.left = true;
|
|
|
|
switch (cell_value) {
|
|
'.', '-' => {
|
|
tasks.addIfNotNull(getLeftTask(task));
|
|
},
|
|
'\\' => {
|
|
tasks.addIfNotNull(getUpTask(task));
|
|
},
|
|
'/' => {
|
|
tasks.addIfNotNull(getDownTask(task, lines));
|
|
},
|
|
'|' => {
|
|
tasks.addIfNotNull(getUpTask(task));
|
|
tasks.addIfNotNull(getDownTask(task, lines));
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
if (task.status.right and !cell_state.*.right) {
|
|
cell_state.*.right = true;
|
|
|
|
switch (cell_value) {
|
|
'.', '-' => {
|
|
tasks.addIfNotNull(getRightTask(task, lines));
|
|
},
|
|
'/' => {
|
|
tasks.addIfNotNull(getUpTask(task));
|
|
},
|
|
'\\' => {
|
|
tasks.addIfNotNull(getDownTask(task, lines));
|
|
},
|
|
'|' => {
|
|
tasks.addIfNotNull(getUpTask(task));
|
|
tasks.addIfNotNull(getDownTask(task, lines));
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
if (task.status.up and !cell_state.*.up) {
|
|
cell_state.*.up = true;
|
|
|
|
switch (cell_value) {
|
|
'.', '|' => {
|
|
tasks.addIfNotNull(getUpTask(task));
|
|
},
|
|
'\\' => {
|
|
tasks.addIfNotNull(getLeftTask(task));
|
|
},
|
|
'/' => {
|
|
tasks.addIfNotNull(getRightTask(task, lines));
|
|
},
|
|
'-' => {
|
|
tasks.addIfNotNull(getLeftTask(task));
|
|
tasks.addIfNotNull(getRightTask(task, lines));
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
if (task.status.down and !cell_state.*.down) {
|
|
cell_state.*.down = true;
|
|
|
|
switch (cell_value) {
|
|
'.', '|' => {
|
|
tasks.addIfNotNull(getDownTask(task, lines));
|
|
},
|
|
'/' => {
|
|
tasks.addIfNotNull(getLeftTask(task));
|
|
},
|
|
'\\' => {
|
|
tasks.addIfNotNull(getRightTask(task, lines));
|
|
},
|
|
'-' => {
|
|
tasks.addIfNotNull(getLeftTask(task));
|
|
tasks.addIfNotNull(getRightTask(task, lines));
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
}
|
|
|
|
var result: usize = 0;
|
|
for (0..state.len) |i| {
|
|
for (0..state[i].len) |j| {
|
|
if (@as(u8, @bitCast(state[i][j])) != 0) {
|
|
//std.debug.print("#", .{});
|
|
result += 1;
|
|
} else {
|
|
//std.debug.print(".", .{});
|
|
}
|
|
}
|
|
//std.debug.print("\n", .{});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
fn solveLines(lines: []const []const u8) usize {
|
|
var result: usize = 0;
|
|
for (0..lines.len) |i| {
|
|
result = @max(result, solveForStart(lines, .{
|
|
.x = @intCast(i),
|
|
.y = 0,
|
|
.status = .{
|
|
.right = true,
|
|
},
|
|
}));
|
|
result = @max(result, solveForStart(lines, .{
|
|
.x = @intCast(i),
|
|
.y = @intCast(lines[i].len - 1),
|
|
.status = .{
|
|
.left = true,
|
|
},
|
|
}));
|
|
}
|
|
|
|
for (0..lines[0].len) |j| {
|
|
result = @max(result, solveForStart(lines, .{
|
|
.x = 0,
|
|
.y = @intCast(j),
|
|
.status = .{
|
|
.down = true,
|
|
},
|
|
}));
|
|
result = @max(result, solveForStart(lines, .{
|
|
.x = @intCast(lines.len - 1),
|
|
.y = @intCast(j),
|
|
.status = .{
|
|
.up = true,
|
|
},
|
|
}));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
pub fn solveAll(reader: anytype) !usize {
|
|
var result: usize = 0;
|
|
while (true) {
|
|
var allocator_buffer: [20000]u8 = undefined;
|
|
var fba = std.heap.FixedBufferAllocator.init(&allocator_buffer);
|
|
var allocator = fba.allocator();
|
|
|
|
var lines = StackList([]u8, usize, 150).init();
|
|
var empty_line_reached = false;
|
|
|
|
var line_buffer: [1000]u8 = undefined;
|
|
while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line| {
|
|
if (line.len == 0) {
|
|
empty_line_reached = true;
|
|
break;
|
|
}
|
|
lines.add(try allocator.dupe(u8, line));
|
|
}
|
|
|
|
result += solveLines(lines.getMutableSlice());
|
|
|
|
if (!empty_line_reached) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn main() !void {
|
|
const stdout = std.io.getStdOut().writer();
|
|
|
|
const raw_in = std.io.getStdIn();
|
|
var buffered_reader = std.io.bufferedReader(raw_in.reader());
|
|
var reader = buffered_reader.reader();
|
|
const result = try solveAll(&reader);
|
|
try stdout.print("{d}\n", .{result});
|
|
}
|
|
|