add memstats / clear lists when new fen position
This commit is contained in:
16
FENTESTS.txt
16
FENTESTS.txt
@@ -1,8 +1,12 @@
|
|||||||
'4k3/4p3/8/b7/1BR1p2p/1Q3P2/5N2/4K3 w - - 0 1'
|
4k3/4p3/8/b7/1BR1p2p/1Q3P2/5N2/4K3 w - - 0 1
|
||||||
'r1bq1rk1/pppp1ppp/2n2n2/4p3/2B1P3/3PPN2/PPP3PP/RN1QK2R b KQ - 1 7'
|
r1bq1rk1/pppp1ppp/2n2n2/4p3/2B1P3/3PPN2/PPP3PP/RN1QK2R b KQ - 1 7
|
||||||
'6k1/6pp/R2p4/p1p5/8/1P1r3P/6P1/6K1 b - - 3 37'
|
6k1/6pp/R2p4/p1p5/8/1P1r3P/6P1/6K1 b - - 3 37
|
||||||
|
|
||||||
# both can castle queen only
|
# both can castle queen only
|
||||||
'r3k2r/8/3B4/8/8/3b4/8/R3K2R w KQkq - 0 1'
|
r3k2r/8/3B4/8/8/3b4/8/R3K2R w KQkq - 0 1
|
||||||
'r3k2r/8/3BB3/8/8/3bb3/8/R3K2R w KQkq - 0 1'
|
r3k2r/8/3BB3/8/8/3bb3/8/R3K2R w KQkq - 0 1
|
||||||
'r2bkb1r/8/8/8/8/3bb3/8/R2BKB1R w KQkq - 0 1'
|
r2bkb1r/8/8/8/8/3bb3/8/R2BKB1R w KQkq - 0 1
|
||||||
|
|
||||||
|
# illegal positions
|
||||||
|
4k3/8/8/8/7b/8/8/4K3 b - - 0 1
|
||||||
|
2r1k3/3P4/8/8/8/8/8/4K3 w - - 0 1
|
||||||
|
@@ -13,4 +13,5 @@
|
|||||||
src/list.h src/pool.h src/board.h src/position.h
|
src/list.h src/pool.h src/board.h src/position.h
|
||||||
./obj/pool.o:: src/pool.c src/list.h src/pool.h src/debug.h
|
./obj/pool.o:: src/pool.c src/list.h src/pool.h src/debug.h
|
||||||
./obj/position.o:: src/position.c src/chessdefs.h src/bits.h src/debug.h \
|
./obj/position.o:: src/position.c src/chessdefs.h src/bits.h src/debug.h \
|
||||||
src/position.h src/board.h src/piece.h src/list.h src/pool.h src/fen.h
|
src/position.h src/board.h src/piece.h src/list.h src/pool.h src/move.h \
|
||||||
|
src/fen.h
|
||||||
|
@@ -53,8 +53,10 @@ int do_fen (pos_t *, char*);
|
|||||||
int do_pos(pos_t *, char*);
|
int do_pos(pos_t *, char*);
|
||||||
int do_genmoves(pos_t *, char*);
|
int do_genmoves(pos_t *, char*);
|
||||||
int do_prmoves(pos_t *, char*);
|
int do_prmoves(pos_t *, char*);
|
||||||
|
int do_memstats(pos_t *, char*);
|
||||||
int do_eval(pos_t *, char*);
|
int do_eval(pos_t *, char*);
|
||||||
int do_quit(pos_t *, char*);
|
int do_quit(pos_t *, char*);
|
||||||
|
int do_debug(pos_t *, char*);
|
||||||
|
|
||||||
COMMAND commands[] = {
|
COMMAND commands[] = {
|
||||||
{ "help", do_help, "Display this text" },
|
{ "help", do_help, "Display this text" },
|
||||||
@@ -62,9 +64,11 @@ COMMAND commands[] = {
|
|||||||
{ "fen", do_fen, "Set position to FEN" },
|
{ "fen", do_fen, "Set position to FEN" },
|
||||||
{ "pos", do_pos, "Print current position" },
|
{ "pos", do_pos, "Print current position" },
|
||||||
{ "quit", do_quit, "Quit" },
|
{ "quit", do_quit, "Quit" },
|
||||||
{ "genmv", do_genmoves, "Generate next move list" },
|
{ "genmove", do_genmoves, "Generate next move list" },
|
||||||
{ "prmv", do_prmoves, "Generate next move list" },
|
{ "prmoves", do_prmoves, "Generate next move list" },
|
||||||
|
{ "memstats", do_memstats, "Generate next move list" },
|
||||||
{ "eval", do_eval, "Eval current position" },
|
{ "eval", do_eval, "Eval current position" },
|
||||||
|
{ "debug", do_debug, "Set log level to LEVEL" },
|
||||||
{ NULL, (int(*)()) NULL, NULL }
|
{ NULL, (int(*)()) NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -170,11 +174,9 @@ char *escape(const char *original)
|
|||||||
|
|
||||||
int quote_detector(char *line, int index)
|
int quote_detector(char *line, int index)
|
||||||
{
|
{
|
||||||
return (
|
return index > 0
|
||||||
index > 0 &&
|
&& line[index - 1] == '\\'
|
||||||
line[index - 1] == '\\' &&
|
&&!quote_detector(line, index - 1);
|
||||||
!quote_detector(line, index - 1)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Execute a command line. */
|
/* Execute a command line. */
|
||||||
@@ -200,7 +202,7 @@ int execute_line (pos_t *pos, char *line)
|
|||||||
|
|
||||||
if (!command) {
|
if (!command) {
|
||||||
fprintf(stderr, "%s: Unknown command.\n", word);
|
fprintf(stderr, "%s: Unknown command.\n", word);
|
||||||
return (-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get argument to command, if any. */
|
/* Get argument to command, if any. */
|
||||||
@@ -210,7 +212,7 @@ int execute_line (pos_t *pos, char *line)
|
|||||||
word = line + i;
|
word = line + i;
|
||||||
|
|
||||||
/* return command number */
|
/* return command number */
|
||||||
return ((*(command->func)) (pos, word));
|
return (*command->func)(pos, word);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look up NAME as the name of a command, and return a pointer to that
|
/* Look up NAME as the name of a command, and return a pointer to that
|
||||||
@@ -221,9 +223,9 @@ COMMAND *find_command(char *name)
|
|||||||
|
|
||||||
for (i = 0; commands[i].name; i++)
|
for (i = 0; commands[i].name; i++)
|
||||||
if (strcmp(name, commands[i].name) == 0)
|
if (strcmp(name, commands[i].name) == 0)
|
||||||
return (&commands[i]);
|
return &commands[i];
|
||||||
|
|
||||||
return ((COMMAND *)NULL);
|
return (COMMAND *)NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Strip whitespace from the start and end of STRING. Return a pointer
|
/* Strip whitespace from the start and end of STRING. Return a pointer
|
||||||
@@ -236,7 +238,7 @@ char *stripwhite(char *string)
|
|||||||
;
|
;
|
||||||
|
|
||||||
if (*s == 0)
|
if (*s == 0)
|
||||||
return (s);
|
return s;
|
||||||
|
|
||||||
t = s + strlen(s) - 1;
|
t = s + strlen(s) - 1;
|
||||||
while (t > s && whitespace(*t))
|
while (t > s && whitespace(*t))
|
||||||
@@ -286,12 +288,28 @@ int do_prmoves(pos_t *pos,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_memstats(__attribute__((unused)) pos_t *pos,
|
||||||
|
__attribute__((unused)) char *arg)
|
||||||
|
{
|
||||||
|
moves_pool_stats();
|
||||||
|
piece_pool_stats();
|
||||||
|
pos_pool_stats();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int do_quit(__attribute__((unused)) pos_t *pos,
|
int do_quit(__attribute__((unused)) pos_t *pos,
|
||||||
__attribute__((unused)) char *arg)
|
__attribute__((unused)) char *arg)
|
||||||
{
|
{
|
||||||
return done = 1;
|
return done = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_debug(__attribute__((unused)) pos_t *pos,
|
||||||
|
__attribute__((unused)) char *arg)
|
||||||
|
{
|
||||||
|
debug_level_set(atoi(arg));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Print out help for ARG, or for all of the commands if ARG is
|
/* Print out help for ARG, or for all of the commands if ARG is
|
||||||
not present. */
|
not present. */
|
||||||
int do_help(__attribute__((unused)) pos_t *pos,
|
int do_help(__attribute__((unused)) pos_t *pos,
|
||||||
@@ -324,7 +342,7 @@ int do_help(__attribute__((unused)) pos_t *pos,
|
|||||||
if (printed)
|
if (printed)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
return (0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BIN_bodichess
|
#ifdef BIN_bodichess
|
||||||
|
@@ -53,6 +53,7 @@ pos_t *fen2pos(pos_t *pos, char *fen)
|
|||||||
board_t *board = pos->board;
|
board_t *board = pos->board;
|
||||||
# define SKIP_BLANK(p) for(;*(p) == ' '; (p)++)
|
# define SKIP_BLANK(p) for(;*(p) == ' '; (p)++)
|
||||||
|
|
||||||
|
pos_clear(pos);
|
||||||
/* 1) get piece placement information
|
/* 1) get piece placement information
|
||||||
*/
|
*/
|
||||||
for (rank = 7, file = 0; *p && *p != ' '; ++p) {
|
for (rank = 7, file = 0; *p && *p != ' '; ++p) {
|
||||||
|
54
src/move.c
54
src/move.c
@@ -54,6 +54,12 @@ pool_t *moves_pool_init()
|
|||||||
return moves_pool;
|
return moves_pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void moves_pool_stats()
|
||||||
|
{
|
||||||
|
if (moves_pool)
|
||||||
|
pool_stats(moves_pool);
|
||||||
|
}
|
||||||
|
|
||||||
int move_print(move_t *move, move_flags_t flags)
|
int move_print(move_t *move, move_flags_t flags)
|
||||||
{
|
{
|
||||||
if (flags & M_PR_CAPT && !(move->flags & M_CAPTURE)) {
|
if (flags & M_PR_CAPT && !(move->flags & M_CAPTURE)) {
|
||||||
@@ -141,8 +147,17 @@ static move_t *move_add(pos_t *pos, piece_t piece, square_t from,
|
|||||||
FILE2C(GET_F(to)),
|
FILE2C(GET_F(to)),
|
||||||
RANK2C(GET_R(to)));
|
RANK2C(GET_R(to)));
|
||||||
# endif
|
# endif
|
||||||
|
/* impossible if opponent king is attacked
|
||||||
|
*/
|
||||||
if (COLOR(piece) != pos->turn)
|
if (COLOR(piece) != pos->turn)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (board[to].piece & KING) {
|
||||||
|
# ifdef DEBUG_MOVE
|
||||||
|
log_i(2, "invalid position: opponent king [%c%c] is in check.\n",
|
||||||
|
FILE2C(GET_F(to)), RANK2C(GET_R(to)));
|
||||||
|
# endif
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (!(move = pool_get(moves_pool)))
|
if (!(move = pool_get(moves_pool)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -165,6 +180,42 @@ static move_t *move_add(pos_t *pos, piece_t piece, square_t from,
|
|||||||
return move;
|
return move;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void move_del(struct list_head *ptr)
|
||||||
|
{
|
||||||
|
move_t *move = list_entry(ptr, move_t, list);
|
||||||
|
|
||||||
|
# ifdef DEBUG_MOVE
|
||||||
|
log_i(3, "delete move from %c%c to %c%c\n",
|
||||||
|
FILE2C(GET_F(move->from)), RANK2C(GET_R(move->from)),
|
||||||
|
FILE2C(GET_F(move->to)), RANK2C(GET_R(move->to)));
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* TODO: remove move->pos if non null */
|
||||||
|
if (move->pos) {
|
||||||
|
pos_clear(move->pos);
|
||||||
|
}
|
||||||
|
list_del(ptr);
|
||||||
|
pool_add(moves_pool, move);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int moves_del(pos_t *pos)
|
||||||
|
{
|
||||||
|
struct list_head *p_cur, *tmp, *head;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
head = &pos->moves;
|
||||||
|
|
||||||
|
list_for_each_safe(p_cur, tmp, head) {
|
||||||
|
move_del(p_cur);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
# ifdef DEBUG_PIECE
|
||||||
|
log_f(3, "removed=%d\n", count);
|
||||||
|
# endif
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: return nmoves */
|
/* TODO: return nmoves */
|
||||||
static move_t *move_pawn_add(pos_t *pos, piece_t piece, square_t from,
|
static move_t *move_pawn_add(pos_t *pos, piece_t piece, square_t from,
|
||||||
square_t to, unsigned char rank7)
|
square_t to, unsigned char rank7)
|
||||||
@@ -217,7 +268,7 @@ int pseudo_moves_pawn(pos_t *pos, piece_list_t *ppiece, bool doit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ifdef DEBUG_MOVE
|
# ifdef DEBUG_MOVE
|
||||||
log_f(3, "pos:%p turn:%s piece:%d [%s %s] dir:%d at %#04x[%c%c]\n",
|
log_f(2, "pos:%p turn:%s piece:%d [%s %s] dir:%d at %#04x[%c%c]\n",
|
||||||
pos,
|
pos,
|
||||||
IS_WHITE(pos->turn)? "white": "black",
|
IS_WHITE(pos->turn)? "white": "black",
|
||||||
piece,
|
piece,
|
||||||
@@ -464,6 +515,7 @@ int moves_gen(pos_t *pos, bool color, bool doit)
|
|||||||
if (doit)
|
if (doit)
|
||||||
pseudo_moves_castle(pos);
|
pseudo_moves_castle(pos);
|
||||||
list_for_each_safe(p_cur, tmp, piece_list) {
|
list_for_each_safe(p_cur, tmp, piece_list) {
|
||||||
|
|
||||||
piece = list_entry(p_cur, piece_list_t, list);
|
piece = list_entry(p_cur, piece_list_t, list);
|
||||||
if (PIECE(piece->piece) != PAWN)
|
if (PIECE(piece->piece) != PAWN)
|
||||||
pseudo_moves_gen(pos, piece, doit);
|
pseudo_moves_gen(pos, piece, doit);
|
||||||
|
10
src/move.h
10
src/move.h
@@ -19,10 +19,6 @@
|
|||||||
#include "pool.h"
|
#include "pool.h"
|
||||||
#include "piece.h"
|
#include "piece.h"
|
||||||
|
|
||||||
/* move_add() return value when generating
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* move flags
|
/* move flags
|
||||||
*/
|
*/
|
||||||
typedef unsigned char move_flags_t;
|
typedef unsigned char move_flags_t;
|
||||||
@@ -48,14 +44,18 @@ typedef struct move_s {
|
|||||||
piece_t promotion; /* promoted piece */
|
piece_t promotion; /* promoted piece */
|
||||||
move_flags_t flags;
|
move_flags_t flags;
|
||||||
struct list_head list; /* next move */
|
struct list_head list; /* next move */
|
||||||
struct pos_t *pos; /* resulting position */
|
struct pos_s *pos; /* resulting position */
|
||||||
} move_t;
|
} move_t;
|
||||||
|
|
||||||
pool_t *moves_pool_init();
|
pool_t *moves_pool_init();
|
||||||
|
void moves_pool_stats();
|
||||||
int move_print(move_t *move, move_flags_t flags);
|
int move_print(move_t *move, move_flags_t flags);
|
||||||
void moves_print(pos_t *move, move_flags_t flags);
|
void moves_print(pos_t *move, move_flags_t flags);
|
||||||
int pseudo_moves_castle(pos_t *pos);
|
int pseudo_moves_castle(pos_t *pos);
|
||||||
|
|
||||||
|
void move_del(struct list_head *ptr);
|
||||||
|
int moves_del(pos_t *pos);
|
||||||
|
|
||||||
int pseudo_moves_gen(pos_t *pos, piece_list_t *piece, bool doit);
|
int pseudo_moves_gen(pos_t *pos, piece_list_t *piece, bool doit);
|
||||||
int pseudo_moves_pawn(pos_t *pos, piece_list_t *piece, bool doit);
|
int pseudo_moves_pawn(pos_t *pos, piece_list_t *piece, bool doit);
|
||||||
int moves_gen(pos_t *pos, bool color, bool doit);
|
int moves_gen(pos_t *pos, bool color, bool doit);
|
||||||
|
34
src/piece.c
34
src/piece.c
@@ -54,6 +54,12 @@ pool_t *piece_pool_init()
|
|||||||
return pieces_pool;
|
return pieces_pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void piece_pool_stats()
|
||||||
|
{
|
||||||
|
if (pieces_pool)
|
||||||
|
pool_stats(pieces_pool);
|
||||||
|
}
|
||||||
|
|
||||||
static eval_t pieces_values[] = {
|
static eval_t pieces_values[] = {
|
||||||
[PAWN] = PAWN_VALUE,
|
[PAWN] = PAWN_VALUE,
|
||||||
[KNIGHT] = KNIGHT_VALUE,
|
[KNIGHT] = KNIGHT_VALUE,
|
||||||
@@ -85,6 +91,34 @@ piece_list_t *piece_add(pos_t *pos, piece_t piece, square_t square)
|
|||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void piece_del(struct list_head *ptr)
|
||||||
|
{
|
||||||
|
piece_list_t *piece = list_entry(ptr, piece_list_t, list);
|
||||||
|
# ifdef DEBUG_PIECE
|
||||||
|
log_f(3, "piece=%02x square=%02x\n", piece->piece, piece->square);
|
||||||
|
# endif
|
||||||
|
list_del(ptr);
|
||||||
|
pool_add(pieces_pool, piece);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pieces_del(pos_t *pos, short color)
|
||||||
|
{
|
||||||
|
struct list_head *p_cur, *tmp, *head;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
head = &pos->pieces[color];
|
||||||
|
|
||||||
|
list_for_each_safe(p_cur, tmp, head) {
|
||||||
|
piece_del(p_cur);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
# ifdef DEBUG_PIECE
|
||||||
|
log_f(3, "color=%d removed=%d\n", color, count);
|
||||||
|
# endif
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef BIN_piece
|
#ifdef BIN_piece
|
||||||
#include "fen.h"
|
#include "fen.h"
|
||||||
int main(int ac, char**av)
|
int main(int ac, char**av)
|
||||||
|
@@ -62,6 +62,9 @@ extern struct piece_details {
|
|||||||
|
|
||||||
void piece_list_print(struct list_head *list);
|
void piece_list_print(struct list_head *list);
|
||||||
pool_t *piece_pool_init();
|
pool_t *piece_pool_init();
|
||||||
|
void piece_pool_stats();
|
||||||
piece_list_t *piece_add(pos_t *pos, piece_t piece, square_t square);
|
piece_list_t *piece_add(pos_t *pos, piece_t piece, square_t square);
|
||||||
|
void piece_del(struct list_head *ptr);
|
||||||
|
int pieces_del(pos_t *pos, short color);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "chessdefs.h"
|
#include "chessdefs.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
|
#include "move.h"
|
||||||
#include "fen.h"
|
#include "fen.h"
|
||||||
#include "piece.h"
|
#include "piece.h"
|
||||||
|
|
||||||
@@ -149,9 +150,10 @@ pos_t *pos_clear(pos_t *pos)
|
|||||||
pos->controlled[BLACK] = 0;
|
pos->controlled[BLACK] = 0;
|
||||||
pos->mobility[WHITE] = 0;
|
pos->mobility[WHITE] = 0;
|
||||||
pos->mobility[BLACK] = 0;
|
pos->mobility[BLACK] = 0;
|
||||||
INIT_LIST_HEAD(&pos->pieces[WHITE]);
|
/* remove pieces / moves */
|
||||||
INIT_LIST_HEAD(&pos->pieces[BLACK]);
|
pieces_del(pos, WHITE);
|
||||||
INIT_LIST_HEAD(&pos->moves);
|
pieces_del(pos, BLACK);
|
||||||
|
moves_del(pos);
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
@@ -167,15 +169,10 @@ pos_t *pos_get()
|
|||||||
{
|
{
|
||||||
pos_t *pos = pool_get(pos_pool);
|
pos_t *pos = pool_get(pos_pool);
|
||||||
if (pos) {
|
if (pos) {
|
||||||
//printf("sizeof(board)=%lu\n", sizeof (board_t));
|
INIT_LIST_HEAD(&pos->pieces[WHITE]);
|
||||||
//pos->board = malloc(sizeof (board_t)*BOARDSIZE);
|
INIT_LIST_HEAD(&pos->pieces[BLACK]);
|
||||||
//printf("board mem: %p-%p\n", pos->board, pos->board+sizeof (board_t));
|
INIT_LIST_HEAD(&pos->moves);
|
||||||
//if (pos->board)
|
|
||||||
pos_clear(pos);
|
pos_clear(pos);
|
||||||
//else {
|
|
||||||
// free(pos);
|
|
||||||
// pos = NULL;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
@@ -207,3 +204,9 @@ pool_t *pos_pool_init()
|
|||||||
pos_pool = pool_init("positions", 128, sizeof(pos_t));
|
pos_pool = pool_init("positions", 128, sizeof(pos_t));
|
||||||
return pos_pool;
|
return pos_pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pos_pool_stats()
|
||||||
|
{
|
||||||
|
if (pos_pool)
|
||||||
|
pool_stats(pos_pool);
|
||||||
|
}
|
||||||
|
@@ -45,6 +45,7 @@ pos_t *pos_clear(pos_t *pos);
|
|||||||
pos_t *pos_startpos(pos_t *pos);
|
pos_t *pos_startpos(pos_t *pos);
|
||||||
pos_t *pos_create();
|
pos_t *pos_create();
|
||||||
pool_t *pos_pool_init();
|
pool_t *pos_pool_init();
|
||||||
|
void pos_pool_stats();
|
||||||
pos_t *pos_get();
|
pos_t *pos_get();
|
||||||
pos_t *pos_dup(pos_t *pos);
|
pos_t *pos_dup(pos_t *pos);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user