enable tests
This commit is contained in:
parent
c1b0ea24a6
commit
654469bb4a
107
Cargo.lock
generated
107
Cargo.lock
generated
@ -17,7 +17,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
"zerocopy",
|
"zerocopy 0.7.35",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -64,6 +64,12 @@ version = "1.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.21.0"
|
version = "1.21.0"
|
||||||
@ -126,6 +132,18 @@ dependencies = [
|
|||||||
"ttf-parser",
|
"ttf-parser",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glam"
|
name = "glam"
|
||||||
version = "0.27.0"
|
version = "0.27.0"
|
||||||
@ -248,13 +266,22 @@ version = "0.17.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"fdeflate",
|
"fdeflate",
|
||||||
"flate2",
|
"flate2",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy 0.8.25",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.92"
|
version = "1.0.92"
|
||||||
@ -279,6 +306,41 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-fuzzy-search"
|
name = "rust-fuzzy-search"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -326,6 +388,7 @@ version = "0.1.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"argh",
|
"argh",
|
||||||
"macroquad",
|
"macroquad",
|
||||||
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -357,6 +420,15 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.14.2+wasi-0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@ -379,13 +451,31 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rt"
|
||||||
|
version = "0.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.35"
|
version = "0.7.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive 0.7.35",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive 0.8.25",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -398,3 +488,14 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
@ -8,6 +8,9 @@ default-run = "sol_chess"
|
|||||||
argh = "0.1.13"
|
argh = "0.1.13"
|
||||||
macroquad = "0.4.13"
|
macroquad = "0.4.13"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = "0.9.1"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 's'
|
opt-level = 's'
|
||||||
lto = true
|
lto = true
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
use argh::FromArgs;
|
use argh::FromArgs;
|
||||||
|
|
||||||
use sol_chess::board::Board;
|
use sol_chess::board::Board;
|
||||||
use sol_chess::generator;
|
use sol_chess::generator::{self, RandomRange};
|
||||||
use sol_chess::solver::Solver;
|
use sol_chess::solver::Solver;
|
||||||
|
|
||||||
|
// Learn how to specify a different dependency for this binary
|
||||||
|
struct MacroquadRngTodo;
|
||||||
|
impl RandomRange for MacroquadRngTodo {
|
||||||
|
fn gen_range(&self, min: usize, max: usize) -> usize {
|
||||||
|
macroquad::rand::gen_range(min, max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Args = argh::from_env();
|
let args: Args = argh::from_env();
|
||||||
|
|
||||||
@ -65,7 +73,7 @@ fn generate_puzzle(num_pieces: Option<u32>, num_solutions: Option<u32>) -> Optio
|
|||||||
"Generating a puzzle with {} pieces with a maximum of {} solutions",
|
"Generating a puzzle with {} pieces with a maximum of {} solutions",
|
||||||
num_pieces, num_solutions
|
num_pieces, num_solutions
|
||||||
);
|
);
|
||||||
let gen = generator::generate(num_pieces, num_solutions);
|
let gen = generator::generate(num_pieces, num_solutions, &MacroquadRngTodo);
|
||||||
gen.print_stats();
|
gen.print_stats();
|
||||||
|
|
||||||
let Some(board) = gen.board() else {
|
let Some(board) = gen.board() else {
|
||||||
|
13
src/game.rs
13
src/game.rs
@ -4,16 +4,23 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use button::Button;
|
use button::Button;
|
||||||
use macroquad::{math, prelude::*};
|
use macroquad::{math, prelude::*, rand};
|
||||||
use sol_chess::{
|
use sol_chess::{
|
||||||
board::{Board, BoardState},
|
board::{Board, BoardState},
|
||||||
generator,
|
generator::{self, RandomRange},
|
||||||
};
|
};
|
||||||
use texture::PieceTexture;
|
use texture::PieceTexture;
|
||||||
|
|
||||||
pub mod button;
|
pub mod button;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
|
|
||||||
|
pub struct MacroquadRandAdapter;
|
||||||
|
impl RandomRange for MacroquadRandAdapter {
|
||||||
|
fn gen_range(&self, min: usize, max: usize) -> usize {
|
||||||
|
rand::gen_range(min, max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
// The generated puzzle. We keep a copy of this to reset the game.
|
// The generated puzzle. We keep a copy of this to reset the game.
|
||||||
original_board: Board,
|
original_board: Board,
|
||||||
@ -445,7 +452,7 @@ impl Game {
|
|||||||
|
|
||||||
fn next_puzzle(&mut self) {
|
fn next_puzzle(&mut self) {
|
||||||
self.reset();
|
self.reset();
|
||||||
let generate = generator::generate(6, 100);
|
let generate = generator::generate(6, 100, &MacroquadRandAdapter);
|
||||||
let board = generate.board().expect("No puzzle was generated");
|
let board = generate.board().expect("No puzzle was generated");
|
||||||
self.original_board = board.clone();
|
self.original_board = board.clone();
|
||||||
self.board = board;
|
self.board = board;
|
||||||
|
@ -5,9 +5,11 @@ use crate::{
|
|||||||
solver::Solver,
|
solver::Solver,
|
||||||
};
|
};
|
||||||
|
|
||||||
use macroquad::{prelude::rand, time};
|
pub trait RandomRange {
|
||||||
|
fn gen_range(&self, min: usize, max: usize) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate(num_pieces: u32, num_solutions: u32) -> GenerateStats {
|
pub fn generate(num_pieces: u32, num_solutions: u32, rand: &impl RandomRange) -> GenerateStats {
|
||||||
let candidate_pieces = vec![
|
let candidate_pieces = vec![
|
||||||
Piece::Pawn,
|
Piece::Pawn,
|
||||||
Piece::Pawn,
|
Piece::Pawn,
|
||||||
@ -33,18 +35,13 @@ pub fn generate(num_pieces: u32, num_solutions: u32) -> GenerateStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let attempts: u32 = 1000;
|
let attempts: u32 = 1000;
|
||||||
let mut overall_stats = GenerateStats::new(0, 0, 0, 0., None);
|
let mut overall_stats = GenerateStats::new(0, 0, 0, None);
|
||||||
for _ in 0..attempts {
|
for _ in 0..attempts {
|
||||||
let stats = try_generate(num_pieces, num_solutions, candidate_pieces.clone());
|
let stats = try_generate(num_pieces, num_solutions, rand, candidate_pieces.clone());
|
||||||
overall_stats.piece_total += stats.piece_total;
|
overall_stats.piece_total += stats.piece_total;
|
||||||
overall_stats.piece_success += stats.piece_success;
|
overall_stats.piece_success += stats.piece_success;
|
||||||
overall_stats.total += stats.total;
|
overall_stats.total += stats.total;
|
||||||
overall_stats.total_seconds += stats.total_seconds;
|
|
||||||
overall_stats.board = stats.board;
|
overall_stats.board = stats.board;
|
||||||
println!(
|
|
||||||
"Generating puzzle.. Elapsed: {}s",
|
|
||||||
overall_stats.total_seconds,
|
|
||||||
);
|
|
||||||
if overall_stats.board.is_some() {
|
if overall_stats.board.is_some() {
|
||||||
return overall_stats;
|
return overall_stats;
|
||||||
}
|
}
|
||||||
@ -57,23 +54,15 @@ pub struct GenerateStats {
|
|||||||
piece_total: u32,
|
piece_total: u32,
|
||||||
piece_success: u32,
|
piece_success: u32,
|
||||||
total: u32,
|
total: u32,
|
||||||
total_seconds: f64,
|
|
||||||
board: Option<Board>,
|
board: Option<Board>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenerateStats {
|
impl GenerateStats {
|
||||||
fn new(
|
fn new(piece_total: u32, piece_success: u32, total: u32, board: Option<Board>) -> Self {
|
||||||
piece_total: u32,
|
|
||||||
piece_success: u32,
|
|
||||||
total: u32,
|
|
||||||
total_millis: f64,
|
|
||||||
board: Option<Board>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
piece_total,
|
piece_total,
|
||||||
piece_success,
|
piece_success,
|
||||||
total,
|
total,
|
||||||
total_seconds: total_millis,
|
|
||||||
board,
|
board,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,7 +72,6 @@ impl GenerateStats {
|
|||||||
add_stat(&mut stats, "Total attempts", self.total);
|
add_stat(&mut stats, "Total attempts", self.total);
|
||||||
add_stat(&mut stats, "Total pieces placed", self.piece_total);
|
add_stat(&mut stats, "Total pieces placed", self.piece_total);
|
||||||
add_stat(&mut stats, "Success pieces placed", self.piece_success);
|
add_stat(&mut stats, "Success pieces placed", self.piece_success);
|
||||||
add_stat(&mut stats, "Total time (ms)", self.total_seconds);
|
|
||||||
|
|
||||||
println!("{}", stats);
|
println!("{}", stats);
|
||||||
}
|
}
|
||||||
@ -103,28 +91,27 @@ where
|
|||||||
fn try_generate(
|
fn try_generate(
|
||||||
num_pieces: u32,
|
num_pieces: u32,
|
||||||
num_solutions: u32,
|
num_solutions: u32,
|
||||||
|
rand: &impl RandomRange,
|
||||||
mut candidate_pieces: Vec<Piece>,
|
mut candidate_pieces: Vec<Piece>,
|
||||||
) -> GenerateStats {
|
) -> GenerateStats {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
let mut piece_total = 0;
|
let mut piece_total = 0;
|
||||||
let mut piece_success = 0;
|
let mut piece_success = 0;
|
||||||
let now = time::get_time();
|
|
||||||
for _ in 0..num_pieces {
|
for _ in 0..num_pieces {
|
||||||
let mut placed = false;
|
let mut placed = false;
|
||||||
let empty_squares = board.empty_squares();
|
let empty_squares = board.empty_squares();
|
||||||
let mut attempts = 15;
|
let mut attempts = 15;
|
||||||
while !placed {
|
while !placed {
|
||||||
if attempts == 0 {
|
if attempts == 0 {
|
||||||
let elapsed = time::get_time() - now;
|
return GenerateStats::new(piece_total, piece_success, 1, None);
|
||||||
return GenerateStats::new(piece_total, piece_success, 1, elapsed, None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attempts -= 1;
|
attempts -= 1;
|
||||||
piece_total += 1;
|
piece_total += 1;
|
||||||
|
|
||||||
let index = rand::gen_range(0, candidate_pieces.len());
|
let index = rand.gen_range(0, candidate_pieces.len());
|
||||||
let piece = candidate_pieces[index];
|
let piece = candidate_pieces[index];
|
||||||
let square_index = rand::gen_range(0, empty_squares.len());
|
let square_index = rand.gen_range(0, empty_squares.len());
|
||||||
let mut random_square = empty_squares[square_index].clone();
|
let mut random_square = empty_squares[square_index].clone();
|
||||||
random_square.piece = Some(piece);
|
random_square.piece = Some(piece);
|
||||||
board.set(random_square.clone());
|
board.set(random_square.clone());
|
||||||
@ -142,11 +129,10 @@ fn try_generate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let solutions = Solver::new(board.clone()).solve();
|
let solutions = Solver::new(board.clone()).solve();
|
||||||
let elapsed = time::get_time() - now;
|
|
||||||
if solutions.len() > num_solutions as usize {
|
if solutions.len() > num_solutions as usize {
|
||||||
GenerateStats::new(piece_total, piece_success, 1, elapsed, None)
|
GenerateStats::new(piece_total, piece_success, 1, None)
|
||||||
} else {
|
} else {
|
||||||
GenerateStats::new(piece_total, piece_success, 1, elapsed, Some(board))
|
GenerateStats::new(piece_total, piece_success, 1, Some(board))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,11 +142,19 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// Figure out a way to remove the macroquad dependencies from this package
|
use rand::Rng;
|
||||||
// #[test]
|
|
||||||
|
struct TestRandom;
|
||||||
|
impl RandomRange for TestRandom {
|
||||||
|
fn gen_range(&self, min: usize, max: usize) -> usize {
|
||||||
|
rand::rng().random_range(min..max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
fn generator_smoke() {
|
fn generator_smoke() {
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
let gen_stats = generate(5, 5);
|
let gen_stats = generate(5, 5, &TestRandom);
|
||||||
let board = gen_stats.board.expect("No puzzle was generated");
|
let board = gen_stats.board.expect("No puzzle was generated");
|
||||||
assert_eq!(board.game_state, BoardState::InProgress);
|
assert_eq!(board.game_state, BoardState::InProgress);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use game::Game;
|
use game::{Game, MacroquadRandAdapter};
|
||||||
use macroquad::prelude::*;
|
use macroquad::prelude::*;
|
||||||
use miniquad::date;
|
use miniquad::date;
|
||||||
use sol_chess::generator;
|
use sol_chess::generator;
|
||||||
@ -37,7 +37,7 @@ async fn init() -> Game {
|
|||||||
let texture_res = Texture2D::from_file_with_format(&texture_bytes[..], None);
|
let texture_res = Texture2D::from_file_with_format(&texture_bytes[..], None);
|
||||||
texture_res.set_filter(FilterMode::Nearest);
|
texture_res.set_filter(FilterMode::Nearest);
|
||||||
build_textures_atlas();
|
build_textures_atlas();
|
||||||
let generate = generator::generate(6, 100);
|
let generate = generator::generate(6, 100, &MacroquadRandAdapter);
|
||||||
let board = generate.board().expect("No puzzle was generated");
|
let board = generate.board().expect("No puzzle was generated");
|
||||||
let game = Game::new(board, texture_res);
|
let game = Game::new(board, texture_res);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user