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 solveLines(lines: []const []const u8) usize { var state = std.mem.zeroes([128][128]CellStatus); var tasks = Queue.init(); tasks.add(.{ .x = 0, .y = 0, .status = .{ .right = true, }, }); 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; } 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}); }