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.
330 lines
9.7 KiB
330 lines
9.7 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 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,
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
fn debugBoard(lines: []const []const u8) void {
|
|
for (lines) |line| {
|
|
std.debug.print("{s}\n", .{line});
|
|
}
|
|
}
|
|
|
|
fn getNextPoints(char: u8, point: [2]usize) [2][2]usize {
|
|
return switch (char) {
|
|
'L' => .{ .{ point[0] - 1, point[1] }, .{ point[0], point[1] + 1 } },
|
|
'F' => .{ .{ point[0] + 1, point[1] }, .{ point[0], point[1] + 1 } },
|
|
'7' => .{ .{ point[0] + 1, point[1] }, .{ point[0], point[1] - 1 } },
|
|
'J' => .{ .{ point[0] - 1, point[1] }, .{ point[0], point[1] - 1 } },
|
|
'-' => .{ .{ point[0], point[1] - 1 }, .{ point[0], point[1] + 1 } },
|
|
'|' => .{ .{ point[0] - 1, point[1] }, .{ point[0] + 1, point[1] } },
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
fn getNextPoint(char: u8, point: [2]usize, previous_point: [2]usize) [2]usize {
|
|
const next_points = getNextPoints(char, point);
|
|
|
|
if (next_points[0][0] == previous_point[0] and next_points[0][1] == previous_point[1]) {
|
|
return next_points[1];
|
|
}
|
|
|
|
if (next_points[1][0] == previous_point[0] and next_points[1][1] == previous_point[1]) {
|
|
return next_points[0];
|
|
}
|
|
|
|
unreachable;
|
|
}
|
|
|
|
fn expandBoard(source: []const []const u8, target: [][]u8) [2][2]usize {
|
|
var sources = StackList([2]usize, usize, 1).init();
|
|
|
|
for (source, 0..) |source_row, i| {
|
|
for (source_row, 0..) |char, j| {
|
|
switch (char) {
|
|
'.' => {},
|
|
'L' => {
|
|
target[3 * i + 1][3 * j + 1] = 'L';
|
|
target[3 * i + 1][3 * j + 2] = '-';
|
|
target[3 * i + 0][3 * j + 1] = '|';
|
|
},
|
|
'F' => {
|
|
target[3 * i + 1][3 * j + 1] = 'F';
|
|
target[3 * i + 1][3 * j + 2] = '-';
|
|
target[3 * i + 2][3 * j + 1] = '|';
|
|
},
|
|
'7' => {
|
|
target[3 * i + 1][3 * j + 1] = '7';
|
|
target[3 * i + 1][3 * j + 0] = '-';
|
|
target[3 * i + 2][3 * j + 1] = '|';
|
|
},
|
|
'J' => {
|
|
target[3 * i + 1][3 * j + 1] = 'J';
|
|
target[3 * i + 1][3 * j + 0] = '-';
|
|
target[3 * i + 0][3 * j + 1] = '|';
|
|
},
|
|
'|' => {
|
|
target[3 * i + 0][3 * j + 1] = '|';
|
|
target[3 * i + 1][3 * j + 1] = '|';
|
|
target[3 * i + 2][3 * j + 1] = '|';
|
|
},
|
|
'-' => {
|
|
target[3 * i + 1][3 * j + 0] = '-';
|
|
target[3 * i + 1][3 * j + 1] = '-';
|
|
target[3 * i + 1][3 * j + 2] = '-';
|
|
},
|
|
'S' => {
|
|
sources.add(.{ i, j });
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
}
|
|
|
|
const source_start_value = sources.getSlice()[0];
|
|
const source_i = source_start_value[0];
|
|
const source_j = source_start_value[1];
|
|
const target_i = 3 * source_i + 1;
|
|
const target_j = 3 * source_j + 1;
|
|
|
|
var adjacents = StackList([2]usize, usize, 2).init();
|
|
|
|
if (source_i > 0) {
|
|
switch (source[source_i - 1][source_j]) {
|
|
'7', '|', 'F' => {
|
|
target[target_i - 1][target_j] = '|';
|
|
adjacents.add(.{ target_i - 1, target_j });
|
|
},
|
|
'J', '-', 'L', '.' => {},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
if (source_i + 1 < source.len) {
|
|
switch (source[source_i + 1][source_j]) {
|
|
'J', '|', 'L' => {
|
|
target[target_i + 1][target_j] = '|';
|
|
adjacents.add(.{ target_i + 1, target_j });
|
|
},
|
|
'7', '-', 'F', '.' => {},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
if (source_j > 0) {
|
|
switch (source[source_i][source_j - 1]) {
|
|
'L', '-', 'F' => {
|
|
target[target_i][target_j - 1] = '-';
|
|
adjacents.add(.{ target_i, target_j - 1 });
|
|
},
|
|
'J', '|', '7', '.' => {},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
if (source_j + 1 < source[source_i].len) {
|
|
switch (source[source_i][source_j + 1]) {
|
|
'J', '-', '7' => {
|
|
target[target_i][target_j + 1] = '-';
|
|
adjacents.add(.{ target_i, target_j + 1 });
|
|
},
|
|
'L', '|', 'F', '.' => {},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
return .{ .{ target_i, target_j }, adjacents.getSlice()[1] };
|
|
}
|
|
|
|
fn walkBoard(board: [][]u8, starting_info: [2][2]usize) void {
|
|
const starting_point = starting_info[0];
|
|
board[starting_point[0]][starting_point[1]] = 'X';
|
|
|
|
var path_length: usize = 1;
|
|
var previous_point = starting_info[0];
|
|
var current_point = starting_info[1];
|
|
while (current_point[0] != starting_point[0] or current_point[1] != starting_point[1]) {
|
|
const next_point = getNextPoint(board[current_point[0]][current_point[1]], current_point, previous_point);
|
|
previous_point = current_point;
|
|
current_point = next_point;
|
|
board[previous_point[0]][previous_point[1]] = 'X';
|
|
path_length += 1;
|
|
}
|
|
}
|
|
|
|
fn cleanJunkPipe(board: [][]u8) void {
|
|
for (board) |row| {
|
|
for (row) |*cell| {
|
|
switch (cell.*) {
|
|
'X', '.' => {},
|
|
'7', 'F', 'L', 'J', '|', '-' => {
|
|
cell.* = '.';
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn floodBoard(board: [][]u8) !void {
|
|
var points = try std.ArrayList([2]usize).initCapacity(std.heap.page_allocator, 1048576);
|
|
defer points.deinit();
|
|
|
|
try points.append(.{ 0, 0 });
|
|
|
|
var step: usize = 0;
|
|
while (step < points.items.len) : (step += 1) {
|
|
const point = points.items[step];
|
|
const i = point[0];
|
|
const j = point[1];
|
|
if (board[i][j] == 'O') {
|
|
continue;
|
|
}
|
|
|
|
board[i][j] = 'O';
|
|
|
|
if (i > 0) {
|
|
if (board[i - 1][j] == '.') {
|
|
try points.append(.{ i - 1, j });
|
|
}
|
|
}
|
|
|
|
if (i + 1 < board.len) {
|
|
if (board[i + 1][j] == '.') {
|
|
try points.append(.{ i + 1, j });
|
|
}
|
|
}
|
|
|
|
if (j > 0) {
|
|
if (board[i][j - 1] == '.') {
|
|
try points.append(.{ i, j - 1 });
|
|
}
|
|
}
|
|
|
|
if (j + 1 < board[i].len) {
|
|
if (board[i][j + 1] == '.') {
|
|
try points.append(.{ i, j + 1 });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn countEmptyCells(expanded_board: []const []const u8) usize {
|
|
var result: usize = 0;
|
|
var i: usize = 1;
|
|
while (i < expanded_board.len) : (i += 3) {
|
|
var j: usize = 1;
|
|
while (j < expanded_board[i].len) : (j += 3) {
|
|
if (expanded_board[i][j] == '.') {
|
|
result += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
fn solveLines(source: [][]u8) !usize {
|
|
//std.debug.print("original board\n", .{});
|
|
//debugBoard(source);
|
|
|
|
var target_storage: [1048576]u8 = undefined;
|
|
@memset(target_storage[0..], '.');
|
|
var target_raw: [1024][]u8 = undefined;
|
|
for (0..source.len) |i| {
|
|
for (0..3) |j| {
|
|
const index = 3 * i + j;
|
|
target_raw[index] = target_storage[1024 * index .. 1024 * index + 3 * source[i].len];
|
|
}
|
|
}
|
|
var target = target_raw[0..(3 * source.len)];
|
|
|
|
const starting_point_info = expandBoard(source, target);
|
|
//std.debug.print("new board\n", .{});
|
|
//debugBoard(target);
|
|
|
|
walkBoard(target, starting_point_info);
|
|
//std.debug.print("after walking\n", .{});
|
|
//debugBoard(target);
|
|
|
|
cleanJunkPipe(target);
|
|
//std.debug.print("after cleaning\n", .{});
|
|
//debugBoard(target);
|
|
|
|
try floodBoard(target);
|
|
//std.debug.print("after flooding\n", .{});
|
|
//debugBoard(target);
|
|
|
|
return countEmptyCells(target);
|
|
}
|
|
|
|
pub fn solveAll(reader: anytype) !usize {
|
|
var result: usize = 0;
|
|
while (true) {
|
|
var allocator_buffer: [100000]u8 = undefined;
|
|
var fba = std.heap.FixedBufferAllocator.init(&allocator_buffer);
|
|
var allocator = fba.allocator();
|
|
|
|
var lines = StackList([]u8, usize, 200).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 += try 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});
|
|
}
|
|
|