an attempt was made at part 2 day 23
This commit is contained in:
@ -17,18 +17,11 @@ pub fn build(b: *std.Build) void {
|
|||||||
|
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = "AdventOfCode2023",
|
.name = "AdventOfCode2023",
|
||||||
// In this case the main source file is merely a path, however, in more
|
.root_source_file = b.path("src/main.zig"),
|
||||||
// complicated build scripts, this could be a generated file.
|
|
||||||
.root_source_file = .{ .path = "src/main.zig" },
|
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
const utils_module = b.createModule(.{
|
|
||||||
.source_file = .{ .path = "src/utils.zig" },
|
|
||||||
});
|
|
||||||
exe.addModule("utils", utils_module);
|
|
||||||
|
|
||||||
// This declares intent for the executable to be installed into the
|
// This declares intent for the executable to be installed into the
|
||||||
// standard location when the user invokes the "install" step (the default
|
// standard location when the user invokes the "install" step (the default
|
||||||
// step when running `zig build`).
|
// step when running `zig build`).
|
||||||
@ -60,7 +53,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
// Creates a step for unit testing. This only builds the test executable
|
// Creates a step for unit testing. This only builds the test executable
|
||||||
// but does not run it.
|
// but does not run it.
|
||||||
const unit_tests = b.addTest(.{
|
const unit_tests = b.addTest(.{
|
||||||
.root_source_file = .{ .path = "src/main.zig" },
|
.root_source_file = b.path("src/main.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
@ -43,22 +43,6 @@ pub fn Queue(comptime Child: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Hash maps as refs" {
|
|
||||||
var allocator = std.heap.page_allocator;
|
|
||||||
|
|
||||||
var q = Queue(*std.AutoHashMap(u128, bool)).init(allocator);
|
|
||||||
var m = std.AutoHashMap(u128, bool).init(allocator);
|
|
||||||
|
|
||||||
try m.put(0, true);
|
|
||||||
try m.put(1, true);
|
|
||||||
try expect(m.count() == 2);
|
|
||||||
|
|
||||||
try q.enqueue(&m);
|
|
||||||
var mPointer: *std.AutoHashMap(u128, bool) = q.dequeue().?;
|
|
||||||
|
|
||||||
try expect(mPointer.*.count() == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn coord(myX: usize, myY: usize) u128 {
|
fn coord(myX: usize, myY: usize) u128 {
|
||||||
return @as(u128, myX) << 64 | @as(u128, myY);
|
return @as(u128, myX) << 64 | @as(u128, myY);
|
||||||
}
|
}
|
||||||
@ -94,15 +78,6 @@ fn isStep(s: u8, want: u8) bool {
|
|||||||
return want == s;
|
return want == s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// const Edge = struct {
|
|
||||||
// weight: usize,
|
|
||||||
// node: *Node,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// const Node = struct {
|
|
||||||
// edges: []?Edge,
|
|
||||||
// };
|
|
||||||
|
|
||||||
fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), root: u128, allocator: std.mem.Allocator, part2: bool) !usize {
|
fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), root: u128, allocator: std.mem.Allocator, part2: bool) !usize {
|
||||||
var q = Queue(u128).init(allocator);
|
var q = Queue(u128).init(allocator);
|
||||||
var qLength = Queue(usize).init(allocator);
|
var qLength = Queue(usize).init(allocator);
|
||||||
@ -126,42 +101,41 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r
|
|||||||
var part1: usize = 0;
|
var part1: usize = 0;
|
||||||
|
|
||||||
while (q.dequeue()) |c| {
|
while (q.dequeue()) |c| {
|
||||||
var child: u128 = c;
|
const child: u128 = c;
|
||||||
var size: usize = qLength.dequeue().?;
|
const size: usize = qLength.dequeue().?;
|
||||||
var paths: []u128 = qPaths.dequeue().?;
|
const paths: []u128 = qPaths.dequeue().?;
|
||||||
|
|
||||||
if (child == goal) {
|
if (child == goal) {
|
||||||
// don't return, because we need all routes.
|
|
||||||
print("Size: {}\n", .{size + 1});
|
|
||||||
part1 = size + 1;
|
part1 = size + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var up = child - 1;
|
const up = child - 1;
|
||||||
var upX = x(up);
|
const upX = x(up);
|
||||||
var upY = y(up);
|
const upY = y(up);
|
||||||
|
|
||||||
var down = child + 1;
|
const down = child + 1;
|
||||||
var downX = x(down);
|
const downX = x(down);
|
||||||
var downY = y(down);
|
const downY = y(down);
|
||||||
|
|
||||||
var right = child + (@as(u128, 1) << 64);
|
const right = child + (@as(u128, 1) << 64);
|
||||||
var rightX = x(right);
|
const rightX = x(right);
|
||||||
var rightY = y(right);
|
const rightY = y(right);
|
||||||
|
|
||||||
var left = child - (@as(u128, 1) << 64);
|
const left = child - (@as(u128, 1) << 64);
|
||||||
var leftX = x(left);
|
const leftX = x(left);
|
||||||
var leftY = y(left);
|
const leftY = y(left);
|
||||||
|
|
||||||
var lastStep = paths[paths.len - 1];
|
const lastStep = paths[paths.len - 1];
|
||||||
var lastStepX = x(lastStep);
|
const lastStepX = x(lastStep);
|
||||||
var lastStepY = y(lastStep);
|
const lastStepY = y(lastStep);
|
||||||
var l = grid[lastStepY][lastStepX];
|
var l = grid[lastStepY][lastStepX];
|
||||||
|
|
||||||
if (part2) {
|
if (part2) {
|
||||||
l = '.';
|
l = '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!linear_search(paths, up) and grid[upY][upX] != WALL and isStep(l, '^')) {
|
if (grid[upY][upX] != WALL and isStep(l, '^') and !linear_search(paths, up)) {
|
||||||
var pathsCopy = try allocator.alloc(u128, paths.len + 1);
|
var pathsCopy = try allocator.alloc(u128, paths.len + 1);
|
||||||
short_copy(pathsCopy, paths);
|
short_copy(pathsCopy, paths);
|
||||||
pathsCopy[pathsCopy.len - 1] = up;
|
pathsCopy[pathsCopy.len - 1] = up;
|
||||||
@ -170,7 +144,8 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r
|
|||||||
try qLength.enqueue(size + 1);
|
try qLength.enqueue(size + 1);
|
||||||
try qPaths.enqueue(pathsCopy);
|
try qPaths.enqueue(pathsCopy);
|
||||||
}
|
}
|
||||||
if (!linear_search(paths, down) and grid[downY][downX] != WALL and isStep(l, 'v')) {
|
|
||||||
|
if (grid[downY][downX] != WALL and isStep(l, 'v') and !linear_search(paths, down)) {
|
||||||
var pathsCopy = try allocator.alloc(u128, paths.len + 1);
|
var pathsCopy = try allocator.alloc(u128, paths.len + 1);
|
||||||
short_copy(pathsCopy, paths);
|
short_copy(pathsCopy, paths);
|
||||||
pathsCopy[pathsCopy.len - 1] = down;
|
pathsCopy[pathsCopy.len - 1] = down;
|
||||||
@ -179,7 +154,8 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r
|
|||||||
try qLength.enqueue(size + 1);
|
try qLength.enqueue(size + 1);
|
||||||
try qPaths.enqueue(pathsCopy);
|
try qPaths.enqueue(pathsCopy);
|
||||||
}
|
}
|
||||||
if (!linear_search(paths, right) and grid[rightY][rightX] != WALL and isStep(l, '>')) {
|
|
||||||
|
if (grid[rightY][rightX] != WALL and isStep(l, '>') and !linear_search(paths, right)) {
|
||||||
var pathsCopy = try allocator.alloc(u128, paths.len + 1);
|
var pathsCopy = try allocator.alloc(u128, paths.len + 1);
|
||||||
short_copy(pathsCopy, paths);
|
short_copy(pathsCopy, paths);
|
||||||
pathsCopy[pathsCopy.len - 1] = right;
|
pathsCopy[pathsCopy.len - 1] = right;
|
||||||
@ -188,7 +164,8 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r
|
|||||||
try qLength.enqueue(size + 1);
|
try qLength.enqueue(size + 1);
|
||||||
try qPaths.enqueue(pathsCopy);
|
try qPaths.enqueue(pathsCopy);
|
||||||
}
|
}
|
||||||
if (!linear_search(paths, left) and grid[leftY][leftX] != WALL and isStep(l, '<')) {
|
|
||||||
|
if (grid[leftY][leftX] != WALL and isStep(l, '<') and !linear_search(paths, left)) {
|
||||||
var pathsCopy = try allocator.alloc(u128, paths.len + 1);
|
var pathsCopy = try allocator.alloc(u128, paths.len + 1);
|
||||||
short_copy(pathsCopy, paths);
|
short_copy(pathsCopy, paths);
|
||||||
pathsCopy[pathsCopy.len - 1] = left;
|
pathsCopy[pathsCopy.len - 1] = left;
|
||||||
@ -204,65 +181,225 @@ fn bfs(grid: [][]const u8, goal: u128, explored: *std.AutoHashMap(u128, bool), r
|
|||||||
return part1;
|
return part1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dps(grid: [][]const u8, root: u128, goal: u128, explored: []u128, size: usize, allocator: std.mem.Allocator) !usize {
|
const Direction = enum { up, down, right, left };
|
||||||
if (root == goal) {
|
|
||||||
print("Size: {}\n", .{size});
|
fn num_of_non_wall(input: *const [][]const u8, coord1: u128, coord2: u128, coord3: u128, coord4: u128) u8 {
|
||||||
return size;
|
var counter: u8 = 0;
|
||||||
|
|
||||||
|
if (input.*[y(coord1)][x(coord1)] != WALL) {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
if (input.*[y(coord2)][x(coord2)] != WALL) {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
if (input.*[y(coord3)][x(coord3)] != WALL) {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
if (input.*[y(coord4)][x(coord4)] != WALL) {
|
||||||
|
counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var up = root - 1;
|
return counter;
|
||||||
var down = root + 1;
|
}
|
||||||
var right = root + (@as(u128, 1) << 64);
|
|
||||||
var left = root - (@as(u128, 1) << 64);
|
|
||||||
var neighbours = [_]u128{ up, down, right, left };
|
|
||||||
|
|
||||||
var highest: usize = 0;
|
fn direction(input: *const [][]const u8, explored: *std.AutoHashMap(u128, bool), up: u128, down: u128, right: u128, left: u128) Direction {
|
||||||
|
if (explored.get(up) == null and input.*[y(up)][x(up)] != WALL) {
|
||||||
|
return Direction.up;
|
||||||
|
}
|
||||||
|
if (explored.get(down) == null and input.*[y(down)][x(down)] != WALL) {
|
||||||
|
return Direction.down;
|
||||||
|
}
|
||||||
|
if (explored.get(left) == null and input.*[y(left)][x(left)] != WALL) {
|
||||||
|
return Direction.left;
|
||||||
|
}
|
||||||
|
if (explored.get(right) == null and input.*[y(right)][x(right)] != WALL) {
|
||||||
|
return Direction.right;
|
||||||
|
}
|
||||||
|
|
||||||
for (neighbours) |n| {
|
unreachable;
|
||||||
var nx = x(n);
|
}
|
||||||
var ny = y(n);
|
|
||||||
if (!linear_search(explored, n) and grid[ny][nx] != WALL) {
|
const Edge = struct { distance: usize, to_x: usize, to_y: usize };
|
||||||
var copyExplored = try allocator.alloc(u128, explored.len + 1);
|
|
||||||
short_copy(copyExplored, explored);
|
fn compress_graph(allocator: std.mem.Allocator, input: [][]const u8) !std.AutoHashMap(u128, []Edge) {
|
||||||
copyExplored[copyExplored.len - 1] = n;
|
var nodes = std.ArrayList(u128).init(allocator);
|
||||||
var h = try dps(grid, n, goal, copyExplored, size + 1, allocator);
|
defer nodes.deinit();
|
||||||
if (h > highest) {
|
|
||||||
highest = h;
|
for (1..input.len - 1) |y_part| {
|
||||||
|
for (1..input[0].len - 1) |x_part| {
|
||||||
|
if (input[y_part][x_part] != '.') {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
allocator.free(copyExplored);
|
const up = coord(x_part, y_part - 1);
|
||||||
|
const down = coord(x_part, y_part + 1);
|
||||||
|
const right = coord(x_part + 1, y_part);
|
||||||
|
const left = coord(x_part - 1, y_part);
|
||||||
|
|
||||||
|
const non_wall_count = num_of_non_wall(&input, up, down, right, left);
|
||||||
|
if (non_wall_count == 0 or non_wall_count == 1) {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (non_wall_count == 2) {
|
||||||
|
if (input[y(up)][x(up)] != WALL and input[y(down)][x(down)] != WALL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input[y(left)][x(left)] != WALL and input[y(right)][x(right)] != WALL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try nodes.append(coord(x_part, y_part));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return highest;
|
var edges_map = std.AutoHashMap(u128, []Edge).init(allocator);
|
||||||
|
|
||||||
|
for (nodes.items) |a| {
|
||||||
|
lower: for (nodes.items) |b| {
|
||||||
|
if (a == b) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x(a) == x(b)) {
|
||||||
|
// Check they can see each other.
|
||||||
|
const min_y = if (y(a) < y(b)) y(a) else y(b);
|
||||||
|
const max_y = if (y(a) > y(b)) y(a) else y(b);
|
||||||
|
|
||||||
|
const contains_walls = for (min_y..max_y) |counter_y| {
|
||||||
|
if (input[counter_y][x(a)] == WALL) {
|
||||||
|
break true;
|
||||||
|
}
|
||||||
|
} else false;
|
||||||
|
|
||||||
|
if (contains_walls) {
|
||||||
|
continue :lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
const new_edge = Edge{ .to_x = x(b), .to_y = y(b), .distance = max_y - min_y };
|
||||||
|
|
||||||
|
if (edges_map.get(a)) |edges_arr| {
|
||||||
|
var new_edges_arr = try allocator.realloc(edges_arr, edges_arr.len + 1);
|
||||||
|
new_edges_arr[new_edges_arr.len - 1] = new_edge;
|
||||||
|
|
||||||
|
try edges_map.put(a, new_edges_arr);
|
||||||
|
} else {
|
||||||
|
var new_edges_arr = try allocator.alloc(Edge, 1);
|
||||||
|
new_edges_arr[0] = new_edge;
|
||||||
|
|
||||||
|
try edges_map.put(a, new_edges_arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y(a) == y(b)) {
|
||||||
|
// Check they can see each other.
|
||||||
|
const min_x = if (x(a) < x(b)) x(a) else x(b);
|
||||||
|
const max_x = if (x(a) > x(b)) x(a) else x(b);
|
||||||
|
|
||||||
|
const contains_walls = for (min_x..max_x) |counter_x| {
|
||||||
|
if (input[y(a)][counter_x] == WALL) {
|
||||||
|
break true;
|
||||||
|
}
|
||||||
|
} else false;
|
||||||
|
|
||||||
|
if (contains_walls) {
|
||||||
|
continue :lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
const new_edge = Edge{ .to_x = x(b), .to_y = y(b), .distance = max_x - min_x };
|
||||||
|
|
||||||
|
if (edges_map.get(a)) |edges_arr| {
|
||||||
|
var new_edges_arr = try allocator.realloc(edges_arr, edges_arr.len + 1);
|
||||||
|
new_edges_arr[new_edges_arr.len - 1] = new_edge;
|
||||||
|
|
||||||
|
try edges_map.put(a, new_edges_arr);
|
||||||
|
} else {
|
||||||
|
var new_edges_arr = try allocator.alloc(Edge, 1);
|
||||||
|
new_edges_arr[0] = new_edge;
|
||||||
|
|
||||||
|
try edges_map.put(a, new_edges_arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return edges_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn includes(nodes: []u128, node: u128) bool {
|
||||||
|
for (nodes) |n| {
|
||||||
|
if (n == node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dfs(allocator: std.mem.Allocator, edges_map: std.AutoHashMap(u128, []Edge), end: u128) !usize {
|
||||||
|
var stack = std.ArrayList(u128).init(allocator);
|
||||||
|
var stack_distance = std.ArrayList(usize).init(allocator);
|
||||||
|
var stack_explored = std.ArrayList([]u128).init(allocator);
|
||||||
|
|
||||||
|
defer stack.deinit();
|
||||||
|
defer stack_distance.deinit();
|
||||||
|
defer stack_explored.deinit();
|
||||||
|
|
||||||
|
const initial_explored = try allocator.alloc(u128, 0);
|
||||||
|
|
||||||
|
try stack.append(coord(1, 1));
|
||||||
|
try stack_distance.append(2);
|
||||||
|
try stack_explored.append(initial_explored);
|
||||||
|
|
||||||
|
var longest_distance: usize = 0;
|
||||||
|
|
||||||
|
while (stack.items.len > 0) {
|
||||||
|
const popped_node = stack.pop();
|
||||||
|
const popped_distance = stack_distance.pop();
|
||||||
|
const popped_explored = stack_explored.pop();
|
||||||
|
|
||||||
|
defer allocator.free(popped_explored);
|
||||||
|
|
||||||
|
if (includes(popped_explored, popped_node)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popped_node == end and popped_distance > longest_distance) {
|
||||||
|
longest_distance = popped_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const edges = edges_map.get(popped_node).?;
|
||||||
|
for (edges) |edge| {
|
||||||
|
var copied_edges = try allocator.alloc(u128, popped_explored.len + 1);
|
||||||
|
std.mem.copyForwards(u128, copied_edges, popped_explored);
|
||||||
|
copied_edges[copied_edges.len - 1] = popped_node;
|
||||||
|
|
||||||
|
try stack.append(coord(edge.to_x, edge.to_y));
|
||||||
|
try stack_distance.append(popped_distance + edge.distance);
|
||||||
|
try stack_explored.append(copied_edges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return longest_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn solve(input: [][]const u8) !void {
|
pub fn solve(input: [][]const u8) !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
var allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
var explored = std.AutoHashMap(u128, bool).init(allocator);
|
var explored = std.AutoHashMap(u128, bool).init(allocator);
|
||||||
|
|
||||||
var start = coord(1, 0);
|
const start = coord(1, 0);
|
||||||
try explored.put(start, true);
|
try explored.put(start, true);
|
||||||
|
|
||||||
var fakeStart = coord(1, 1);
|
const goal = coord(input[0].len - 2, input.len - 2);
|
||||||
|
|
||||||
var goal = coord(input[0].len - 2, input.len - 1);
|
// const part1 = try bfs(input, goal, &explored, fakeStart, allocator, false);
|
||||||
_ = goal;
|
|
||||||
|
|
||||||
// var part1 = try bfs(input, goal, &explored, fakeStart, allocator, false);
|
|
||||||
// print("Part 1: {}\n", .{part1});
|
// print("Part 1: {}\n", .{part1});
|
||||||
|
|
||||||
// var part2 = try bfs(input, goal, &explored, fakeStart, allocator, true);
|
const edges = try compress_graph(allocator, input);
|
||||||
// print("Part 2: {}\n", .{part2});
|
const part2 = try dfs(allocator, edges, goal);
|
||||||
|
print("Part 2: {}\n", .{part2});
|
||||||
var exp = try allocator.alloc(u128, 1);
|
|
||||||
exp[0] = start;
|
|
||||||
|
|
||||||
// var exampleLastInter = coord(19, 20);
|
|
||||||
var exampleLastInter = coord(133, 137);
|
|
||||||
|
|
||||||
var part2 = try dps(input, fakeStart, exampleLastInter, exp, 0, allocator);
|
|
||||||
print("Part 2: {}\n", .{part2 + 21});
|
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,15 @@ const std = @import("std");
|
|||||||
// const day19 = @import("./day19/day19.zig");
|
// const day19 = @import("./day19/day19.zig");
|
||||||
// const day20 = @import("./day20/day20.zig");
|
// const day20 = @import("./day20/day20.zig");
|
||||||
// const day21 = @import("./day21/day21.zig");
|
// const day21 = @import("./day21/day21.zig");
|
||||||
const day22 = @import("./day22/day22.zig");
|
// const day22 = @import("./day22/day22.zig");
|
||||||
// const day23 = @import("./day23/day23.zig");
|
const day23 = @import("./day23/day23.zig");
|
||||||
// const day24 = @import("./day24/day24.zig");
|
// const day24 = @import("./day24/day24.zig");
|
||||||
const utils = @import("utils.zig");
|
const utils = @import("utils.zig");
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const allocator = std.heap.page_allocator;
|
const allocator = std.heap.page_allocator;
|
||||||
const input = try utils.getInput("./src/day22/input.txt", allocator);
|
const input = try utils.getInput("./src/day23/input.txt", allocator);
|
||||||
defer allocator.free(input);
|
defer allocator.free(input);
|
||||||
|
|
||||||
try day22.solve(input);
|
try day23.solve(input);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user