From b351d198b8b5b7027811986828bd7b18c765d6b7 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Mon, 4 Mar 2024 21:34:29 +0100 Subject: [PATCH] sq_attackers() + others (see dedails). Ready for move do/undo ? - add many "const" in func parameters - attack.c: sq_attackers() - move print_board_raw from position.c to to board.c - move some fen_check() tests to pos_check() - add REL_RANK() macro. TODO: add one more for bitboards - fen.c: more tests for FEN validity - position.c: add pos_checkers() and pos_check() - tests: add common-test.h (for shared FEN positions access) --- Makefile | 45 +++-- src/attack.c | 56 +++++- src/attack.h | 3 +- src/bitboard.h | 4 +- src/board.c | 26 ++- src/board.h | 5 +- src/chessdefs.h | 22 ++- src/fen.c | 60 +++--- src/fen.h | 2 +- src/{movegen.c => move-gen.c} | 8 +- src/{movegen.h => move-gen.h} | 2 +- src/move.c | 6 +- src/position.c | 352 +++++++++++++--------------------- src/position.h | 31 ++- test/bitboard-test.c | 37 ++-- test/common-test.h | 188 ++++++++++++++++++ test/fen-test.c | 56 +++--- test/movegen-test.c | 163 +++++----------- 18 files changed, 584 insertions(+), 482 deletions(-) rename src/{movegen.c => move-gen.c} (97%) rename src/{movegen.h => move-gen.h} (95%) create mode 100644 test/common-test.h diff --git a/Makefile b/Makefile index 1134207..45e63d8 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Makefile - GNU make only. +# Makefile - brchess Makefile, **GNU make only** # # Copyright (C) 2021-2023 Bruno Raoult ("br") # Licensed under the GNU General Public License v3.0 or later. @@ -58,19 +58,19 @@ CPPFLAGS := -I$(BRINCDIR) -I$(INCDIR) CPPFLAGS += -DNDEBUG # assert -CPPFLAGS += -DBUG_ON # bug.h -CPPFLAGS += -DWARN_ON # bug.h -#CPPFLAGS += -DDEBUG # global - unused +CPPFLAGS += -DBUG_ON # brlib bug.h +CPPFLAGS += -DWARN_ON # brlib bug.h -CPPFLAGS += -DDEBUG_DEBUG # enable log() functions +#CPPFLAGS += -DDEBUG # global - unused +#CPPFLAGS += -DDEBUG_DEBUG # enable log() functions #CPPFLAGS += -DDEBUG_DEBUG_C # enable log() settings -CPPFLAGS += -DDEBUG_POOL # memory pools management +#CPPFLAGS += -DDEBUG_POOL # memory pools management #CPPFLAGS += -DDEBUG_FEN # FEN decoding -#CPPFLAGS += -DDEBUG_POS # position.c -CPPFLAGS += -DDEBUG_MOVE # move generation -CPPFLAGS += -DDEBUG_EVAL # eval functions -CPPFLAGS += -DDEBUG_PIECE # piece list management -CPPFLAGS += -DDEBUG_SEARCH # move search +#CPPFLAGS += -DDEBUG_POS # position.c +#CPPFLAGS += -DDEBUG_MOVE # move generation +#CPPFLAGS += -DDEBUG_EVAL # eval functions +#CPPFLAGS += -DDEBUG_PIECE # piece list management +#CPPFLAGS += -DDEBUG_SEARCH # move search CPPFLAGS += -DDIAGRAM_SYM # diagram with symbols @@ -272,18 +272,23 @@ memcheck: targets ##################################### test binaries .PHONY: testing test -TEST := fen-test bitboard-test movegen-test +TEST := fen-test bitboard-test movegen-test attack-test -FEN_OBJS := fen.o position.o piece.o bitboard.o board.o util.o -BB_OBJS := fen.o position.o piece.o bitboard.o board.o hyperbola-quintessence.o +FEN_OBJS := fen.o position.o piece.o bitboard.o board.o hyperbola-quintessence.o \ + attack.o +BB_OBJS := fen.o position.o piece.o bitboard.o board.o hyperbola-quintessence.o \ + attack.o MOVEGEN_OBJS := fen.o position.o piece.o bitboard.o board.o hyperbola-quintessence.o \ - move.o movegen.o + attack.o move.o movegen.o +ATTACK_OBJS := fen.o position.o piece.o bitboard.o board.o hyperbola-quintessence.o \ + attack.o move.o movegen.o TEST := $(addprefix $(BINDIR)/,$(TEST)) FEN_OBJS := $(addprefix $(OBJDIR)/,$(FEN_OBJS)) BB_OBJS := $(addprefix $(OBJDIR)/,$(BB_OBJS)) MOVEGEN_OBJS := $(addprefix $(OBJDIR)/,$(MOVEGEN_OBJS)) +ATTACK_OBJS := $(addprefix $(OBJDIR)/,$(ATTACK_OBJS)) test: echo TEST=$(TEST) @@ -291,18 +296,22 @@ test: testing: $(TEST) -bin/fen-test: test/fen-test.c $(FEN_OBJS) +bin/fen-test: test/fen-test.c test/common-test.h $(FEN_OBJS) @echo compiling $@ test executable. @$(CC) $(ALL_CFLAGS) $< $(FEN_OBJS) $(ALL_LDFLAGS) -o $@ -bin/bitboard-test: test/bitboard-test.c $(BB_OBJS) +bin/bitboard-test: test/bitboard-test.c test/common-test.h $(BB_OBJS) @echo compiling $@ test executable. @$(CC) $(ALL_CFLAGS) $< $(BB_OBJS) $(ALL_LDFLAGS) -o $@ -bin/movegen-test: test/movegen-test.c $(MOVEGEN_OBJS) +bin/movegen-test: test/movegen-test.c test/common-test.h $(MOVEGEN_OBJS) @echo compiling $@ test executable. @$(CC) $(ALL_CFLAGS) $< $(MOVEGEN_OBJS) $(ALL_LDFLAGS) -o $@ +bin/attack-test: test/attack-test.c test/common-test.h $(ATTACK_OBJS) + @echo compiling $@ test executable. + @$(CC) $(ALL_CFLAGS) $< $(ATTACK_OBJS) $(ALL_LDFLAGS) -o $@ + ##################################### Makefile debug .PHONY: showflags wft diff --git a/src/attack.c b/src/attack.c index d2cb225..a3e2998 100644 --- a/src/attack.c +++ b/src/attack.c @@ -18,9 +18,10 @@ #include "bitboard.h" #include "position.h" #include "hyperbola-quintessence.h" +#include "attack.h" /** - * square_attackers() - find attackers on a square + * sq_attackers() - find attackers on a square * @pos: position * @sq: square to test * @c: attacker color @@ -36,32 +37,69 @@ * @Return: a bitboard of attackers. * */ -bitboard_t sq_attackers(pos_t *pos, square_t sq, color_t c) +bitboard_t sq_attackers(const pos_t *pos, const square_t sq, const color_t c) { bitboard_t attackers = 0; - bitboard_t from = mask(sq); + bitboard_t sqbb = mask(sq); bitboard_t c_pieces = pos->bb[c][ALL_PIECES]; bitboard_t occ = c_pieces | pos->bb[OPPONENT(c)][ALL_PIECES]; bitboard_t to; + color_t opp = OPPONENT(c); /* pawn */ to = pos->bb[c][PAWN]; - attackers |= pawn_push_upleft(from, c) & to; - attackers |= pawn_push_upright(from, c) & to; + attackers |= pawn_shift_upleft(sqbb, opp) & to; +# ifdef DEBUG_ATTACK + bitboard_print("sq_attackers after pawn upleft", attackers); +# endif + attackers |= pawn_shift_upright(sqbb, opp) & to; +# ifdef DEBUG_ATTACK + bitboard_print("sq_attackers pawn upright", attackers); +# endif /* knight & king */ to = pos->bb[c][KNIGHT]; - attackers |= bb_knight_moves(c_pieces, from); + attackers |= bb_knight_moves(to, sq); +# ifdef DEBUG_ATTACK + bitboard_print("sq_attackers after knight", attackers); +# endif to = pos->bb[c][KING]; - attackers |= bb_king_moves(c_pieces, from); + attackers |= bb_king_moves(to, sq); +# ifdef DEBUG_ATTACK + bitboard_print("sq_attackers after king", attackers); +# endif /* bishop / queen */ to = pos->bb[c][BISHOP] | pos->bb[c][QUEEN]; - attackers |= hyperbola_bishop_moves(occ, from) & to; + attackers |= hyperbola_bishop_moves(occ, sq) & to; +# ifdef DEBUG_ATTACK + bitboard_print("sq_attackers after bishop/queen", attackers); +# endif /* rook / queen */ to = pos->bb[c][ROOK] | pos->bb[c][QUEEN]; - attackers |= hyperbola_rook_moves(occ, from) & to; +# ifdef DEBUG_ATTACK + bitboard_print("sq_attackers after queen", attackers); +# endif + attackers |= hyperbola_rook_moves(occ, sq) & to; +# ifdef DEBUG_ATTACK + bitboard_print("sq_attackers after rook/queen", attackers); +# endif return attackers; } + +/** + * sq_attackers() - find all attackers on a square + * @pos: position + * @sq: square to test + * + * Find all attacks on @sq. En-passant is not considered. + * Just a wrapper over @sq_attackers(). + * + * @Return: a bitboard of attackers. + */ +bitboard_t sq_attackers_all(const pos_t *pos, const square_t sq) +{ + return sq_attackers(pos, sq, WHITE) | sq_attackers(pos, sq, BLACK); +} diff --git a/src/attack.h b/src/attack.h index 70ddbb1..9872874 100644 --- a/src/attack.h +++ b/src/attack.h @@ -17,6 +17,7 @@ #include "chessdefs.h" #include "bitboard.h" -extern bitboard_t sq_attackers(pos_t *pos, square_t sq, color_t c); +extern bitboard_t sq_attackers(const pos_t *pos, const square_t sq, const color_t c); +extern bitboard_t sq_attackers_all(const pos_t *pos, const square_t sq); #endif diff --git a/src/bitboard.h b/src/bitboard.h index 8cdf2e2..a3c950a 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -118,7 +118,9 @@ static __always_inline bitboard_t shift_nw(const bitboard_t bb) #define pawn_shift_up(bb, c) ((c) == WHITE ? shift_n(bb): shift_s(bb)) #define pawn_shift_upleft(bb, c) ((c) == WHITE ? shift_nw(bb): shift_se(bb)) #define pawn_shift_upright(bb, c) ((c) == WHITE ? shift_ne(bb): shift_sw(bb)) -/* pawn move (for single pawn) */ +/* pawn move (for single pawn) - NO SQUARE CONTROL HERE ! + * Need to make functions with control instead. + */ #define pawn_push_up(sq, c) ((sq) + ((c) == WHITE ? NORTH: SOUTH)) #define pawn_push_upleft(sq, c) ((sq) + ((c) == WHITE ? NORTH_WEST: SOUTH_EAST)) #define pawn_push_upright(sq, c) ((sq) + ((c) == WHITE ? NORTH_EAST: SOUTH_WEST)) diff --git a/src/board.c b/src/board.c index 2b6d1b0..3fa4c5b 100644 --- a/src/board.c +++ b/src/board.c @@ -1,4 +1,4 @@ -/* board.c - 8x8 board. +/* board.c - 8x8 functions. * * Copyright (C) 2024 Bruno Raoult ("br") * Licensed under the GNU General Public License v3.0 or later. @@ -57,7 +57,7 @@ square_t sq_from_string(const char *sqstr) * board_print() - Print a board * @board: &board_t to print */ -void board_print(piece_t *board) +void board_print(const piece_t *board) { printf(" +---+---+---+---+---+---+---+---+\n"); for (int rank = 7; rank >= 0; --rank) { @@ -82,7 +82,7 @@ void board_print(piece_t *board) * * Squares corresponding to @mask will be displayed in reverse colors. */ -void board_print_mask(piece_t *board, bitboard_t mask) +void board_print_mask(const piece_t *board, const bitboard_t mask) { // 6: blink # define REVERSE "\e[7m▌" @@ -106,3 +106,23 @@ void board_print_mask(piece_t *board, bitboard_t mask) } printf(" A B C D E F G H\n"); } + +/** + * board_print_raw - print raw (octal or FEN symbol) board + * @bb: the bitboard + * @type: int, 0 for octal, 1 for fen symbol + */ +void board_print_raw(const piece_t *board, const int type) +{ + for (rank_t r = RANK_8; r >= RANK_1; --r) { + for (file_t f = FILE_A; f <= FILE_H; ++f) { + piece_t p = board[sq_make(f, r)]; + if (type) { + printf("%s ", p == EMPTY? ".": piece_to_char_color(p)); + } else { + printf("%02o ", p); + } + } + printf("\n"); + } +} diff --git a/src/board.h b/src/board.h index 5f84bd9..3e28cd2 100644 --- a/src/board.h +++ b/src/board.h @@ -65,7 +65,8 @@ static __always_inline rank_t sq_rank(square_t square) extern const char *sq_to_string(const square_t sq); extern square_t sq_from_string(const char *sq_string); -extern void board_print(piece_t *board); -extern void board_print_mask(piece_t *board, bitboard_t mask); +extern void board_print(const piece_t *board); +extern void board_print_mask(const piece_t *board, const bitboard_t mask); +extern void board_print_raw(const piece_t *board, const int type); #endif /* _BOARD_H */ diff --git a/src/chessdefs.h b/src/chessdefs.h index 9c0b17c..7bae0b1 100644 --- a/src/chessdefs.h +++ b/src/chessdefs.h @@ -16,19 +16,21 @@ #include "brlib.h" /* brlib types */ -#define ONE 1ull -#define C64(const_u64) const_u64##ULL -#define mask(i) ( (unsigned long long) (ONE << (i)) ) +#define ONE 1ull +#define C64(const_u64) const_u64##ULL +#define mask(i) ( (unsigned long long) (ONE << (i)) ) //typedef ushort board; -#define BOARDSIZE (8*8) -/* from human to machine */ -#define C2FILE(c) (tolower(c) - 'a') -#define C2RANK(c) (tolower(c) - '1') -/* from machine to human */ -#define FILE2C(f) ((f) + 'a') -#define RANK2C(r) ((r) + '1') +#define BOARDSIZE (8*8) +/* from human to machin e */ +#define C2FILE(c) (tolower(c) - 'a') +#define C2RANK(c) (tolower(c) - '1') +/* from machine to huma n */ +#define FILE2C(f) ((f) + 'a') +#define RANK2C(r) ((r) + '1') +/* relative rank */ +#define REL_RANK(r, c) ((7 * (c)) ^ r) /* castle_t bits structure */ typedef enum { diff --git a/src/fen.c b/src/fen.c index de9bf20..605b015 100644 --- a/src/fen.c +++ b/src/fen.c @@ -1,4 +1,4 @@ -/* fen.c - fen notation. +/* fen.c - fen parsing/generation/test. * * Copyright (C) 2021-2024 Bruno Raoult ("br") * Licensed under the GNU General Public License v3.0 or later. @@ -22,8 +22,8 @@ #include "chessdefs.h" #include "util.h" -#include "piece.h" -#include "bitboard.h" +//#include "piece.h" +//#include "bitboard.h" #include "position.h" #include "fen.h" @@ -61,19 +61,18 @@ static const char *castle_str = "KQkq"; #define SKIP_BLANK(p) for(;isspace(*(p)); (p)++) /** - * fen_test(pos_t *pos) - test (and try to fix) fen-generated position. + * fen_check(pos_t *pos) - test (and try to fix) fen-generated position. * @pos: position * - * fen_test() tests the following: - * - fatal: number of pawns > 8 - * - fatal: number of pieces > 16 - * - fatal: number of kings != 1 - * - fixable: inconsistent castle flags (if K & R are not in correct position) - * - fixable: inconsistent en-passant square (turn, bad pawn position) + * Test and fix the following: + * - inconsistent castle flags (if K & R are not in correct position) + * - inconsistent en-passant square (turn, bad pawn position) + * + * pos_check() is also called, leading to fatal errors if something is wrong. * * @return: 0 if OK, 1 if OK after fix, -1 if fatal issue. */ -static int fen_test(pos_t *pos) +static int fen_check(pos_t *pos) { char *colstr[2] = { "white", "black"}; int error = 0, warning = 0; @@ -82,24 +81,25 @@ static int fen_test(pos_t *pos) if (pos->en_passant != SQUARE_NONE) { rank_t eprank = sq_rank(pos->en_passant); file_t epfile = sq_file(pos->en_passant); - rank_t rank5 = pos->turn == WHITE? RANK_5: RANK_4; - rank_t rank6 = pos->turn == WHITE? RANK_6: RANK_3; - rank_t rank7 = pos->turn == WHITE? RANK_7: RANK_2; + rank_t rank5 = REL_RANK(RANK_5, pos->turn); + rank_t rank6 = REL_RANK(RANK_6, pos->turn); + rank_t rank7 = REL_RANK(RANK_7, pos->turn); piece_t pawn = pos->turn == WHITE? B_PAWN: W_PAWN; if (warn(eprank != rank6 || pos->board[sq_make(epfile, rank5)] != pawn || pos->board[sq_make(epfile, rank6)] != EMPTY || pos->board[sq_make(epfile, rank7)] != EMPTY, "fen warn: wrong en-passant settings. (fixed)\n")) { +# ifdef DEBUG_FEN printf("ep5=%o ep6=%o ep7=%o\n", sq_make(epfile, rank5), - sq_make(epfile, rank6), sq_make(epfile, rank6)); + sq_make(epfile, rank6), sq_make(epfile, rank7)); +# endif warning++; pos->en_passant = SQUARE_NONE; } } for (int color = WHITE; color <= BLACK; ++color) { - int n; rank_t rank1 = color == WHITE? RANK_1: RANK_8; /* castling */ @@ -130,18 +130,12 @@ static int fen_test(pos_t *pos) } } - /* piece, pawn, anf king count */ - n = popcount64(pos->bb[color][PAWN]); - error += warn(n > 8, - "fen err: %s has %d pawns\n", colstr[color], n); - n = popcount64(pos->bb[color][KING]); - error += warn(n != 1, - "fen err: %s has %d kings\n", colstr[color], n); - n = popcount64(pos->bb[color][ALL_PIECES]); - error += warn(n > 16, - "fen err: %s has %d pieces\n", colstr[color], n); } - return error ? -1: warning ? 1: 0; + if (!(error = pos_check(pos, 0))) { + /* TODO: Should it really be here ? */ + pos->checkers = pos_checkers(pos, pos->turn); + } + return error ? -1: warning; } /** @@ -259,13 +253,13 @@ end: err_line, err_pos, err_char, err_char)) { return NULL; } - if (fen_test(&tmppos) >= 0) { - if (!pos) - pos = pos_new(); - *pos = tmppos; - } + if (fen_check(&tmppos) < 0) + return NULL; + if (!pos) + pos = pos_new(); + *pos = tmppos; # ifdef DEBUG_FEN - pos_print_board_raw(&tmppos, 1); + pos_print_raw(&tmppos, 1); # endif return pos; diff --git a/src/fen.h b/src/fen.h index db55884..6a38b0a 100644 --- a/src/fen.h +++ b/src/fen.h @@ -14,7 +14,7 @@ #ifndef FEN_H #define FEN_H -#include "position.h" +#include "chessdefs.h" #define FENSTRLEN 92 /* secure FEN string size */ diff --git a/src/movegen.c b/src/move-gen.c similarity index 97% rename from src/movegen.c rename to src/move-gen.c index 9aeb8a4..8bc5c68 100644 --- a/src/movegen.c +++ b/src/move-gen.c @@ -1,4 +1,4 @@ -/* movegen.c - move generation +/* move-gen.c - move generation * * Copyright (C) 2024 Bruno Raoult ("br") * Licensed under the GNU General Public License v3.0 or later. @@ -21,7 +21,7 @@ #include "position.h" #include "move.h" #include "hyperbola-quintessence.h" -#include "movegen.h" +#include "move-gen.h" /** @@ -111,8 +111,8 @@ int gen_all_pseudomoves(pos_t *pos) } /* pawn: relative rank and files */ - bitboard_t rel_rank7 = (me == WHITE ? RANK_7bb : RANK_2bb); - bitboard_t rel_rank3 = (me == WHITE ? RANK_3bb : RANK_6bb); + bitboard_t rel_rank7 = me == WHITE ? RANK_7bb : RANK_2bb; + bitboard_t rel_rank3 = me == WHITE ? RANK_3bb : RANK_6bb; //bitboard_t rel_filea = (me == WHITE ? FILE_Abb : FILE_Hbb); //bitboard_t rel_fileh = (me == WHITE ? FILE_Hbb : FILE_Abb); int en_passant = pos->en_passant == SQUARE_NONE? 0: pos->en_passant; diff --git a/src/movegen.h b/src/move-gen.h similarity index 95% rename from src/movegen.h rename to src/move-gen.h index 0db750a..6b765fd 100644 --- a/src/movegen.h +++ b/src/move-gen.h @@ -1,4 +1,4 @@ -/* movegen.h - move generation +/* move-gen.h - move generation * * Copyright (C) 2024 Bruno Raoult ("br") * Licensed under the GNU General Public License v3.0 or later. diff --git a/src/move.c b/src/move.c index b89239b..6660c7b 100644 --- a/src/move.c +++ b/src/move.c @@ -115,7 +115,7 @@ void moves_print(pos_t *pos, __unused int flags) } -static int _moves_comp_square(const void *p1, const void *p2) +static int _moves_cmp_bysquare(const void *p1, const void *p2) { move_t m1 = *(move_t *)p1; move_t m2 = *(move_t *)p2; @@ -129,7 +129,7 @@ static int _moves_comp_square(const void *p1, const void *p2) /* f1 == f2 */ if (t1 < t2) return -1; if (t1 > t2) return 1; - return 0; /* DUP BUG ! */ + return 0; } /** * move_sort_by_sq() - sort moves by from/to squares ascending @@ -139,7 +139,7 @@ static int _moves_comp_square(const void *p1, const void *p2) */ void move_sort_by_sq(pos_t *pos) { - qsort(pos->moves.move, pos->moves.nmoves, sizeof(move_t), _moves_comp_square); + qsort(pos->moves.move, pos->moves.nmoves, sizeof(move_t), _moves_cmp_bysquare); } /* diff --git a/src/position.c b/src/position.c index cc34e52..a328128 100644 --- a/src/position.c +++ b/src/position.c @@ -28,18 +28,7 @@ #include "piece.h" #include "util.h" #include "board.h" - -/**************************************************** - * #define BYTE_PRINT "%c%c%c%c%c%c%c%c" * - * #define BYTE2BIN(b) ((b) & 0x01 ? '1' : '0'), \ * - * ((b) & 0x02 ? '1' : '0'), \ * - * ((b) & 0x04 ? '1' : '0'), \ * - * ((b) & 0x08 ? '1' : '0'), \ * - * ((b) & 0x10 ? '1' : '0'), \ * - * ((b) & 0x20 ? '1' : '0'), \ * - * ((b) & 0x40 ? '1' : '0'), \ * - * ((b) & 0x80 ? '1' : '0') * - ****************************************************/ +#include "attack.h" /** * pos_new() - allocate a new position @@ -71,7 +60,7 @@ pos_t *pos_new(void) * * TODO: merge with pos_new - NULL for init, non null for duplicate */ -pos_t *pos_dup(pos_t *pos) +pos_t *pos_dup(const pos_t *pos) { pos_t *newpos = safe_malloc(sizeof(pos_t)); @@ -104,82 +93,143 @@ pos_t *pos_clear(pos_t *pos) # ifdef DEBUG_POS printf("size(pos_board=%lu elt=%lu\n", sizeof(pos->board), sizeof(int)); # endif - //for (square square = A1; square <= H8; ++square) - // pos->board[square] = EMPTY; - - SET_WHITE(pos->turn); pos->node_count = 0; - pos->turn = 0; + pos->turn = WHITE; pos->clock_50 = 0; pos->plycount = 0; pos->en_passant = SQUARE_NONE; pos->castle = 0; - memset(pos->board, EMPTY, sizeof(pos->board)); - //pos->curmove = 0; - //pos->eval = 0; - //pos->occupied[WHITE] = 0; - //pos->occupied[BLACK] = 0; + for (color_t color = WHITE; color <= BLACK; ++color) { for (piece_type_t piece = 0; piece <= KING; ++piece) pos->bb[color][piece] = 0; - pos->controlled[WHITE] = 0; - pos->controlled[BLACK] = 0; + pos->controlled[color] = 0; pos->king[color] = SQUARE_NONE; } - pos->moves.curmove = pos->moves.nmoves = 0; - //pos->mobility[WHITE] = 0; - //pos->mobility[BLACK] = 0; - //pos->moves_generated = false; - //pos->moves_counted = false; - /* remove pieces / moves */ - //pieces_del(pos, WHITE); - //pieces_del(pos, BLACK); - //moves_del(pos); + + for (square_t sq = A1; sq <= H8; ++sq) + pos->board[sq] = EMPTY; + pos->moves.curmove = 0; + pos->moves.nmoves = 0; return pos; } +/** + * pos_checkers() - find all checkers on a king. + * @pos: &position + * @color: king color + * + * Get a bitboard of all checkers on @color king. + * Just a wrapper over @sq_attackers(). + * + * @return: a bitboard of attackers. + */ +bitboard_t pos_checkers(const pos_t *pos, const color_t color) +{ + return sq_attackers(pos, pos->king[color], OPPONENT(color)); +} + +/** + * pos_checkers2str() - convert checkers to string. + * @pos: &position + * @str: destination string (should be at least 2*3 + 1 = 7 length) + * + * @return: The string. + */ +char *pos_checkers2str(const pos_t *pos, char *str) +{ + int sq, tmp; + char *p = str; + bit_for_each64(sq, tmp, pos->checkers) { + const char *sqstr = sq_to_string(sq); + *p++ = sqstr[0]; + *p++ = sqstr[1]; + *p++ = ' '; + } + *p = 0; + return str; +} + +/** + * pos_check() - extensive position consistenci check. + * @pos: &position + * @strict: if not zero, call bug_on() on any error. + * + * Check (hopefully) if position is valid: + * - pawns on first or 8th rank + * - number of pawns per color > 8 + * - total number of pieces per color > 16 or zero + * - number of kings per color != 1 + * - discrepancy between bitboards per piece and ALL_PIECES per color + * - discrepancy between bitboards and board + * - side-to-move already checking opponent king. + * + * In case of errors, and @abort is true, @bug_on() is called, and program will + * be terminated. + * This function should be called with @abort == 0 during initialization phase + * (eg after fen parsing), and with @abort != 0 otherwise (as we have some data + * corruption). + * + * TODO: add more checks + * - en-prise king for side to move. + * + * @Return: 0 if no error detected + * the number of detected error if @abort == 0. + * this function does not return if @abort != 0 and errors are found. + */ +int pos_check(const pos_t *pos, const int fatal) +{ + int n, count = 0, bbcount = 0, error = 0; + + /* pawns on 1st ot 8th rank */ + n = popcount64((pos->bb[WHITE][PAWN] | pos->bb[BLACK][PAWN]) & + (RANK_1bb | RANK_8bb)); + error += warn_on(n != 0); + + for (color_t color = WHITE; color <= BLACK; ++color) { + /* pawn count */ + n = popcount64(pos->bb[color][PAWN]); + error += warn_on(n > 8); + /* king count */ + n = popcount64(pos->bb[color][KING]); + error += warn_on(n != 1); + /* pieces count */ + n = popcount64(pos->bb[color][ALL_PIECES]); + error += warn_on(n == 0 || n > 16); + bbcount += n; + } + for (square_t sq = 0; sq < 64; ++sq) { + piece_t piece = pos->board[sq]; + bitboard_t match; + if (piece == EMPTY) + continue; + color_t c = COLOR(piece); + piece_type_t p = PIECE(piece); + match = pos->bb[c][p] & mask(sq); + error += warn_on(!match); + count++; + } + /* occupied occupation is different from bitboards */ + error += warn_on(count != bbcount); + /* is color to play in check ? */ + error += warn_on(pos_checkers(pos, OPPONENT(pos->turn))); + + bug_on(fatal && error); + return error; +} + /** * pos_print() - Print position and fen on stdout. * @pos: &position */ -void pos_print(pos_t *pos) +void pos_print(const pos_t *pos) { - //int rank, file; - //piece_t *board = pos->board; - char fen[92]; + char str[92]; - //piece_list_t *wk = list_first_entry(&pos->pieces[WHITE], piece_list_t, list), - // *bk = list_first_entry(&pos->pieces[BLACK], piece_list_t, list); board_print(pos->board); - - /* - * printf(" +---+---+---+---+---+---+---+---+\n"); - * for (rank = 7; rank >= 0; --rank) { - * printf("%c |", rank + '1'); - * for (file = 0; file < 8; ++file) { - * pc = board[sq_make(file, rank)]; - * printf(" %s |", pc? piece_to_sym_color(pc): " "); - * } - * printf("\n +---+---+---+---+---+---+---+---+\n"); - * } - * printf(" A B C D E F G H\n"); - */ - printf("fen %s\n", pos2fen(pos, fen)); - //printf("Turn: %s.\n", IS_WHITE(pos->turn) ? "white" : "black"); - /* - * printf("Kings: W:%c%c B:%c%c\n", - * FILE2C(F88(wk->square)), - * RANK2C(R88(wk->square)), - * FILE2C(F88(bk->square)), - * RANK2C(R88(bk->square))); - */ - //printf("plies=%d clock50=%d\n", pos->plycount, pos->clock_50); - //printf("Current move = %d\n", pos->curmove); - //printf("Squares controlled: W:%d B:%d\n", popcount64(pos->controlled[WHITE]), - // popcount64(pos->controlled[BLACK])); - //printf("Mobility: W:%u B:%u\n", pos->mobility[WHITE], - // pos->mobility[BLACK]); + printf("fen %s\n", pos2fen(pos, str)); + printf("checkers %s\n", pos_checkers2str(pos, str)); } /** @@ -187,50 +237,30 @@ void pos_print(pos_t *pos) * @pos: &position * @mask: mask of highlighted squares. */ -void pos_print_mask(pos_t *pos, bitboard_t mask) +void pos_print_mask(const pos_t *pos, const bitboard_t mask) { - //int rank, file; - //piece_t pc, *board = pos->board; char fen[92]; - //piece_list_t *wk = list_first_entry(&pos->pieces[WHITE], piece_list_t, list), - // *bk = list_first_entry(&pos->pieces[BLACK], piece_list_t, list); board_print_mask(pos->board, mask); - - /* - * printf(" +---+---+---+---+---+---+---+---+\n"); - * for (rank = 7; rank >= 0; --rank) { - * printf("%c |", rank + '1'); - * for (file = 0; file < 8; ++file) { - * pc = board[sq_make(file, rank)]; - * printf(" %s |", pc? piece_to_sym_color(pc): " "); - * } - * printf("\n +---+---+---+---+---+---+---+---+\n"); - * } - * printf(" A B C D E F G H\n"); - */ printf("fen %s\n", pos2fen(pos, fen)); - //printf("Turn: %s.\n", IS_WHITE(pos->turn) ? "white" : "black"); - /* - * printf("Kings: W:%c%c B:%c%c\n", - * FILE2C(F88(wk->square)), - * RANK2C(R88(wk->square)), - * FILE2C(F88(bk->square)), - * RANK2C(R88(bk->square))); - */ - //printf("plies=%d clock50=%d\n", pos->plycount, pos->clock_50); - //printf("Current move = %d\n", pos->curmove); - //printf("Squares controlled: W:%d B:%d\n", popcount64(pos->controlled[WHITE]), - // popcount64(pos->controlled[BLACK])); - //printf("Mobility: W:%u B:%u\n", pos->mobility[WHITE], - // pos->mobility[BLACK]); } /** - * pos_pieces_print() - Print position pieces + * pos_print_board_raw - print simple position board (octal/FEN symbol values) + * @bb: the bitboard + * @type: int, 0 for octal, 1 for fen symbol + */ +void pos_print_raw(const pos_t *pos, const int type) +{ + board_print_raw(pos->board, type); + return; +} + +/** + * pos_print_pieces() - Print position pieces * @pos: &position */ -void pos_pieces_print(pos_t *pos) +void pos_print_pieces(const pos_t *pos) { int bit, count, cur; char *pname; @@ -254,123 +284,5 @@ void pos_pieces_print(pos_t *pos) printf(" "); } printf("\n"); - //printf("White pieces (%d): \t", popcount64(pos->occupied[WHITE])); - //piece_list_print(&pos->pieces[WHITE]); - //printf("Black pieces (%d): \t", popcount64(pos->occupied[BLACK])); - //piece_list_print(&pos->pieces[BLACK]); } } - - -/* -inline void bitboard_print2_raw(bitboard_t bb1, bitboard_t bb2, char *title) -{ - int i; - printf("%s%s", title? title: "", title? ":\n": ""); - - printf("\tW: %#018lx\tB: %#018lx\n", bb1, bb2); - for (i=56; i>=0; i-=8) - printf("\t"BYTE_PRINT"\t\t"BYTE_PRINT"\n", - BYTE2BIN(bb1>>i), - BYTE2BIN(bb2>>i)); -} -*/ - -/** - * pos_print_board_raw - print simple position board (octal/FEN symbol values) - * @bb: the bitboard - * @type: int, 0 for octal, 1 for fen symbol - */ -void pos_print_board_raw(const pos_t *pos, int type) -{ - if (type == 0) { - for (rank_t r = RANK_8; r >= RANK_1; --r) { - for (file_t f = FILE_A; f <= FILE_H; ++f) - printf("%02o ", pos->board[sq_make(f, r)]); - printf(" \n"); - } - } else { - for (rank_t r = RANK_8; r >= RANK_1; --r) { - for (file_t f = FILE_A; f <= FILE_H; ++f) { - square_t sq = sq_make(f, r); - if (pos->board[sq] == EMPTY) - printf(". "); - else - printf("%s ", piece_to_char_color(pos->board[sq])); - } - printf(" \n"); - } - } - return; -} - -/** - * pos_bitboards_print() - Print position bitboards - * @pos: &position - */ -//void pos_bitboards_print(pos_t *pos) -//{ -// printf("Bitboards occupied :\n"); -// bitboard_print2(pos->occupied[WHITE], pos->occupied[BLACK]); -// printf("Bitboards controlled :\n"); -// bitboard_print2(pos->controlled[WHITE], pos->controlled[BLACK]); -// -//} - -/** - * pos_check() - extensive position consistenci check. - * @pos: &position - */ -/* - * void pos_check(position *pos) - * { - * int rank, file; - * piece_t piece; - * board_t *board = pos->board; - * - * /\* check that board and bitboard reflect same information *\/ - * for (rank = 7; rank >= 0; --rank) { - * for (file = 0; file < 8; ++file) { - * piece_list_t *ppiece; - * printf("checking %c%c ", file+'a', rank+'1'); - * - * piece = board[SQ88(file, rank)].piece; - * ppiece= board[SQ88(file, rank)].s_piece; - * printf("piece=%s ", P_CSYM(piece)); - * if (ppiece) - * printf("ppiece=%s/sq=%#x ", P_CSYM(ppiece->piece), ppiece->square); - * switch(PIECE(piece)) { - * case PAWN: - * printf("pawn" ); - * break; - * case KNIGHT: - * printf("knight "); - * break; - * case BISHOP: - * printf("bishop "); - * break; - * case ROOK: - * printf("rook "); - * break; - * case QUEEN: - * printf("queen "); - * break; - * case KING: - * printf("king "); - * break; - * } - * printf("\n"); - * } - * } - * } - */ - -/* - * void pos_del(pos_t *pos) - * { - * pieces_del(pos, WHITE); - * pieces_del(pos, BLACK); - * moves_del(pos); - * pool_add(pos_pool, pos); - * } - */ diff --git a/src/position.h b/src/position.h index 8590f1e..3e9add9 100644 --- a/src/position.h +++ b/src/position.h @@ -35,22 +35,11 @@ typedef struct __pos_s { square_t en_passant; castle_rights_t castle; - //eval_t eval; - //int check[2]; - //int eval_simple_phase; - //eval_t eval_simple; - //move_t *bestmove; - //bool moves_generated; - //bool moves_counted; - bitboard_t bb[2][PIECE_TYPE_MAX]; /* bb[0][PAWN], bb[1][ALL_PIECES] */ - bitboard_t controlled[2]; - //u16 mobility[2]; - //struct list_head pieces[2]; /* pieces list, King is first */ - //struct list_head moves[2]; + bitboard_t controlled[2]; /* unsure */ + bitboard_t checkers; /* opponent checkers */ piece_t board[BOARDSIZE]; movelist_t moves; - //int nmoves; } pos_t; /** @@ -93,18 +82,20 @@ static inline void pos_clr_sq(pos_t *pos, square_t square) //void bitboard_print2(bitboard_t bb1, bitboard_t bb2, char *title); extern pos_t *pos_new(); -extern pos_t *pos_dup(pos_t *pos); +extern pos_t *pos_dup(const pos_t *pos); extern void pos_del(pos_t *pos); extern pos_t *pos_clear(pos_t *pos); -extern void pos_print(pos_t *pos); -extern void pos_print_mask(pos_t *pos, bitboard_t mask); -extern void pos_pieces_print(pos_t *pos); +extern bitboard_t pos_checkers(const pos_t *pos, const color_t color); +extern char *pos_checkers2str(const pos_t *pos, char *str); -extern void pos_print_board_raw(const pos_t *pos, int type); +extern int pos_check(const pos_t *pos, const int strict); -//extern pos_t *pos_startpos(pos_t *pos); +extern void pos_print(const pos_t *pos); +extern void pos_print_mask(const pos_t *pos, const bitboard_t mask); +extern void pos_print_raw(const pos_t *pos, const int type); + +extern void pos_print_pieces(const pos_t *pos); -//void pos_check(position *pos); #endif /* POSITION_H */ diff --git a/test/bitboard-test.c b/test/bitboard-test.c index 9d3e145..0f4758d 100644 --- a/test/bitboard-test.c +++ b/test/bitboard-test.c @@ -1,12 +1,22 @@ -//#include "debug.h" -//#include "pool.h" +/* bitboard-test.c - basic bitboard/hyperbola tests. + * + * Copyright (C) 2024 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ #include #include #include "bug.h" -#include "position.h" -#include "piece.h" + +#include "chessdefs.h" #include "bitboard.h" #include "hyperbola-quintessence.h" @@ -27,26 +37,25 @@ int main(int __unused ac, __unused char**av) bb_file[i], bb_rank[i], bb_knight[i], bb_king[i]); } - bitboard_print_multi("a1-a8 a1-h8 a1-h1 a2-a7 a2-g7 a2-g2", 6, + sprintf(str, "between: %-22s%-22s%-22s%-22s%-22s%-22s", + "a1-a8", "a1-h8", "a1-h1", "a2-a7", "a2-g7", "a2-g2"); + bitboard_print_multi(str, 6, bb_between[A1][A8], bb_between[A1][H8], bb_between[A1][H1], bb_between[A2][A7], bb_between[A2][G7], bb_between[A2][G2]); - bitboard_print_multi("c3-c6 c3-f6 c3-f3 c3-e1 c3-c1 c3-a1 c3-a3 c3-a5", 8, + sprintf(str, "between: %-22s%-22s%-22s%-22s%-22s%-22s%-22s%-22s", + "c3-c6", "c3-f6", "c3-f3", "c3-e1", "c3-c1", "c3-a1", "c3-a3", "c3-a5"); + bitboard_print_multi(str, 8, bb_between[C3][C6], bb_between[C3][F6], bb_between[C3][F3], bb_between[C3][E1], bb_between[C3][C1], bb_between[C3][A1], bb_between[C3][A3], bb_between[C3][A5]); - bitboard_print_multi("c4-c6 c4-f6 c4-f3 c4-e1 c4-c1 c4-a1 c4-a3 c4-a5", 8, + sprintf(str, "between: %-22s%-22s%-22s%-22s%-22s%-22s%-22s%-22s", + "c4-c6", "c4-f6", "c4-f3", "c4-e1", "c4-c1", "c4-a1", "c4-a3", "c4-a5"); + bitboard_print_multi(str, 8, bb_between[C4][C6], bb_between[C4][F6], bb_between[C4][F3], bb_between[C4][E1], bb_between[C4][C1], bb_between[C4][A1], bb_between[C4][A3], bb_between[C4][A5]); - - for (square_t sq = 0; sq < 64; ++sq) { - sprintf(str, "%2d %#lx %#lx knight", sq, bb_sq[sq], bb_knight[sq]); - bitboard_print(str, bb_knight[sq]); - //sprintf(str, "%2d %#lx %#lx knight", sq, bb_sq[sq], bb_king[sq]); - //bitboard_print(str, bb_king[sq]); - } return 0; } diff --git a/test/common-test.h b/test/common-test.h new file mode 100644 index 0000000..eac5e1e --- /dev/null +++ b/test/common-test.h @@ -0,0 +1,188 @@ +/* common-test.h - common static vars/funcs test + * + * Copyright (C) 2024 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include +#include "chessdefs.h" +/* when below FENs are in a struct with selection per test */ +#define FEN 1 +#define BITBOARD 2 +#define MOVEGEN 4 + + +struct fentest { + uint modules; + char *comment; + char *fen; +} fentest[] = { +//static char *fentest[] = { + /* tests rank movegen bug - FIXED + */ + //"4k3/pppppppp/8/8/8/8/PPPPPPPP/2BRK3 w - - 0 1", + //"4k3/pppppppp/8/8/8/8/PPPPPPPP/1B1R1K2 w - - 0 1", + { MOVEGEN, "onli pawn captures", + "4k3/8/2p1p3/2PpP3/8/pp4pp/PP4PP/4K3 w - d6 0 1" }, + { FEN , "king in check", + "4k3/8/8/8/7b/8/8/4K3 w - - 0 1" }, + + /* illegal positions (en-prise king, wrong e.p. or castle flags, ...), + * sometimes crash Stockfish + */ + { FEN , "illegal EP and castle flags, fix-able by fen parser, SF crash", + "4k3/8/8/8/7B/8/8/4K3 w KQkq e6 0 1" }, + { FEN , "illegal, SF crash", + "4k3/8/8/8/7b/8/8/4K3 b - - 0 1" }, + { FEN , "illegal, SF crash", + "2r1k3/3B4/8/8/8/8/8/4K3 w - - 0 1" }, + { FEN , "illegal, SF crash", + "2r1k3/3P4/8/8/8/8/8/4K3 w - - 0 1" }, + + // First game moves + { FEN | MOVEGEN, "initial pos", + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" }, + { FEN | MOVEGEN, "1.e4", + "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1" }, + { FEN | MOVEGEN, "1.Nh3", + "rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1" }, + { FEN | MOVEGEN, "1.e4 e5 2.Nf3 Nc6", + "r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1" }, + + // castling test + // both can castle queen only + { FEN | MOVEGEN, "", + "r3k2r/8/3B4/8/8/3b4/8/R3K2R w KQkq - 0 1" }, + { FEN | MOVEGEN, "", + "r3k2r/8/3BB3/8/8/3bb3/8/R3K2R w KQkq - 0 1" }, + { FEN | MOVEGEN, "", + "r2bkb1r/8/8/8/8/3bb3/8/R2BKB1R w KQkq - 0 1" }, + // 4 castle possible, only K+R + { FEN | MOVEGEN, "", + "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1" }, + // only kings on A1/A8, white to play + { FEN | MOVEGEN, "", + "k7/8/8/8/8/8/8/K7 w - - 0 1" }, + // only one move possible (Pf2xBg3) + { FEN | MOVEGEN, "", + "k7/8/8/1p1p4/pPpPp3/P1PpPpb1/NBNP1P2/KBB1B3 w - - 0 1" }, + // only 2 moves possible (Ph5xg6 e.p., Ph5-h6) + { FEN | MOVEGEN, "", + "k7/8/8/1p1p2pP/pPpPp3/P1PpPp2/NBNP1P2/KBB1B3 w - g6 0 1" }, + // 2 Kings, W/B/ pawns on 7th for promotion + { FEN | MOVEGEN, "", + "k4n2/4P3/8/8/8/8/4p3/K4N2 w - - 0 1" }, + // white castled, and can e.p. on c6 black can castle + // white is a pawn down + // white has 36 moves: P=11 + 1 e.p. N=6+3 B=5+5 R=1 Q=3 K=1 + 1 e.p. + // black has 33 moves: P=11 N=2+7 B=5 R=3 Q=3 K=1 + castle + { FEN | MOVEGEN, "", + "rnbqk2r/pp1pbpp1/7p/2pPp3/4n3/3B1N2/PPP2PPP/RNBQ1RK1 w kq c6 0 7" }, + + { FEN | MOVEGEN, "", + "4k3/4p3/8/b7/1BR1p2p/1Q3P2/5N2/4K3 w - - 0 1" }, + { FEN | MOVEGEN, "", + "r1bq1rk1/pppp1ppp/2n2n2/4p3/2B1P3/3PPN2/PPP3PP/RN1QK2R b KQ - 1 7" }, + { FEN | MOVEGEN, "", + "6k1/6pp/R2p4/p1p5/8/1P1r3P/6P1/6K1 b - - 3 3" }, + + // below tests are from: + // - Rodent IV + // - https://www.chessprogramming.net/perfect-perft/ + { FEN | MOVEGEN, "", + "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1" }, + { FEN | MOVEGEN, "", + "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1" }, + { FEN | MOVEGEN, "", + "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19" }, + { FEN | MOVEGEN, "", + "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14" }, + { FEN | MOVEGEN, "", + "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14" }, + { FEN | MOVEGEN, "", + "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15" }, + { FEN | MOVEGEN, "", + "1rbqk1nr/p3ppbp/2np2p1/2p5/1p2PP2/3PB1P1/PPPQ2BP/R2NK1NR b KQk - 0 1" }, + { FEN | MOVEGEN, "", + "r1bqk2r/pp1p1ppp/2n1pn2/2p5/1bPP4/2NBP3/PP2NPPP/R1BQK2R b KQkq - 0 1" }, + { FEN | MOVEGEN, "", + "rnb1kb1r/ppp2ppp/1q2p3/4P3/2P1Q3/5N2/PP1P1PPP/R1B1KB1R b KQkq - 0 1" }, + { FEN | MOVEGEN, "", + "r1b2rk1/pp2nppp/1b2p3/3p4/3N1P2/2P2NP1/PP3PBP/R3R1K1 b - - 0 1" }, + { FEN | MOVEGEN, "", + "n1q1r1k1/3b3n/p2p1bp1/P1pPp2p/2P1P3/2NBB2P/3Q1PK1/1R4N1 b - - 0 1" }, + { FEN | MOVEGEN, "", + "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16" }, + { FEN | MOVEGEN, "", + "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22" }, + { FEN | MOVEGEN, "", + "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18" }, + { FEN | MOVEGEN, "", + "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22" }, + { FEN | MOVEGEN, "", + "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26" }, + { FEN | MOVEGEN, "", + "2r5/8/1n6/1P1p1pkp/p2P4/R1P1PKP1/8/1R6 w - - 0 1" }, + { FEN | MOVEGEN, "", + "r2q1rk1/1b1nbppp/4p3/3pP3/p1pP4/PpP2N1P/1P3PP1/R1BQRNK1 b - - 0 1" }, + { FEN | MOVEGEN, "", + "6k1/5pp1/7p/p1p2n1P/P4N2/6P1/1P3P1K/8 w - - 0 35" }, + { FEN | MOVEGEN, "", + "r4rk1/1pp1q1pp/p2p4/3Pn3/1PP1Pp2/P7/3QB1PP/2R2RK1 b - - 0 1" }, + + { FEN | MOVEGEN, "", + "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" }, + { FEN | MOVEGEN, "", + "1k6/1b6/8/8/7R/8/8/4K2R b K - 0 1" }, + { FEN | MOVEGEN, "", + "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1" }, // Illegal ep move #1 + { FEN | MOVEGEN, "", + "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1" }, // Illegal ep move #2 + { FEN | MOVEGEN, "", + "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1" }, // EP Capture Checks Opponent + { FEN | MOVEGEN, "", + "5k2/8/8/8/8/8/8/4K2R w K - 0 1" }, // Short Castling Gives Check + { FEN | MOVEGEN, "", + "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1" }, // Long Castling Gives Check + { FEN | MOVEGEN, "", + "r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1" }, // Castle Rights + { FEN | MOVEGEN, "", + "r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1" }, // Castling Prevented + { FEN | MOVEGEN, "", + "2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1" }, // Promote out of Check + { FEN | MOVEGEN, "", + "8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1" }, // Discovered Check + { FEN | MOVEGEN, "", + "4k3/1P6/8/8/8/8/K7/8 w - - 0 1" }, // Promote to give check + { FEN | MOVEGEN, "", + "8/P1k5/K7/8/8/8/8/8 w - - 0 1" }, // Under Promote to give check + { FEN | MOVEGEN, "", + "K1k5/8/P7/8/8/8/8/8 w - - 0 1" }, // Self Stalemate + { FEN | MOVEGEN, "", + "8/k1P5/8/1K6/8/8/8/8 w - - 0 1" }, // Stalemate & Checkmate + { FEN | MOVEGEN, "", + "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1" }, // Stalemate & Checkmate + + { 0, NULL, NULL } +}; + +static int cur = 0; + +static char* next_fen(uint module) +{ + while (fentest[cur].fen && !(fentest[cur].modules & module)) + cur++; + return fentest[cur].fen ? fentest[cur++].fen: NULL; +} + +static __unused char* cur_comment() +{ + return fentest[cur].comment; +} diff --git a/test/fen-test.c b/test/fen-test.c index a8ff1cc..8526f63 100644 --- a/test/fen-test.c +++ b/test/fen-test.c @@ -1,39 +1,43 @@ -//#include +/* fen-test.c - basic fen tests. + * + * Copyright (C) 2024 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ -#include "bug.h" +#include #include "chessdefs.h" #include "bitboard.h" #include "position.h" #include "fen.h" -int main(int ac, char**av) +#include "common-test.h" + +int main(__unused int ac, __unused char**av) { pos_t *pos; - bitboard_t mask = A1bb | C3bb | A8bb | G7bb | H8bb | H1bb; const char *fen; char revfen[128]; - int comp; - //debug_init(5, stderr, true); - //pos_pool_init(); - bitboard_init(); - pos = pos_new(); - if (ac == 1) { - fen = startfen; - startpos(pos); - } else { - fen = av[1]; - fen2pos(pos, fen); - } - pos_print(pos); - pos_print_mask(pos, mask); - printf("ULL=#%lx %#lx %#lx %#lx #%lx\n", A5bb, H5bb, H6bb, H7bb, H8bb); - printf("ULL=%llx %llx %llx\n", mask(A5), mask(H7), mask(H8)); - pos2fen(pos, revfen); - //printf("reverse fen=[%s]\n", pos2fen(pos, NULL)); - comp = strcmp(fen, revfen); - printf("compare=%d - %s\n", comp, comp? "NOK": "OK"); - pos_print_board_raw(pos, 0); - pos_print_board_raw(pos, 1); + bitboard_init(); + + while ((fen = next_fen(FEN))) { + if (!(pos = fen2pos(NULL, fen))) { + printf("fen = [%s] **INVALID\n", fen); + } else { + pos_print_raw(pos, 1); + pos2fen(pos, revfen); + printf("fen = [%s]\nrev = [%s]", fen, revfen); + if (strcmp(fen, revfen)) + printf(" **FIXED\n"); + pos_del(pos); + } + } } diff --git a/test/movegen-test.c b/test/movegen-test.c index 4a58996..707c167 100644 --- a/test/movegen-test.c +++ b/test/movegen-test.c @@ -1,103 +1,26 @@ -//#include "debug.h" -//#include "pool.h" +/* movegen-test.c - basic movegen tests. + * + * Copyright (C) 2024 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ #include #include #include -#include -#include "position.h" -#include "piece.h" -#include "bitboard.h" +#include "chessdefs.h" #include "fen.h" -#include "move.h" -#include "movegen.h" +#include "move-gen.h" +#include "position.h" -static char *fentest[] = { - /* tests rank movegen bug - FIXED */ - // "4k3/pppppppp/8/8/8/8/PPPPPPPP/2BRK3 w - - 0 1", - // "4k3/pppppppp/8/8/8/8/PPPPPPPP/1B1R1K2 w - - 0 1", - - // illegal positions (en-prise king) - "4k3/8/8/8/7b/8/8/4K3 b - - 0 1", - "2r1k3/3P4/8/8/8/8/8/4K3 w - - 0 1", - - // initial pos - "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", - // position after 1.e4 - "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1", - // position after 1.Nh3 - "rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1", - // after e4 e5 Nf3 Nc6 - "r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 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", - "6k1/6pp/R2p4/p1p5/8/1P1r3P/6P1/6K1 b - - 3 3", - // both can castle queen only - "r3k2r/8/3B4/8/8/3b4/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", - // 4 castle possible, only K+R - "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1", - // only kings on A1/A8, white to play - "k7/8/8/8/8/8/8/K7 w - - 0 1", - // only one move possible (Pf2xBg3) - "k7/8/8/1p1p4/pPpPp3/P1PpPpb1/NBNP1P2/KBB1B3 w - - 0 1", - // only 2 moves possible (Ph5xg6 e.p., Ph5-h6) - "k7/8/8/1p1p2pP/pPpPp3/P1PpPp2/NBNP1P2/KBB1B3 w - g6 0 1", - // 2 Kings, W/B/ pawns on 7th for promotion - "k4n2/4P3/8/8/8/8/4p3/K4N2 w - - 0 1", - // white castled, and can e.p. on c6 black can castle - // white is a pawn down - // white has 36 moves: P=11 + 1 e.p. N=6+3 B=5+5 R=1 Q=3 K=1 + 1 e.p. - // black has 33 moves: P=11 N=2+7 B=5 R=3 Q=3 K=1 + castle - "rnbqk2r/pp1pbpp1/7p/2pPp3/4n3/3B1N2/PPP2PPP/RNBQ1RK1 w kq c6 0 7", - - // below tests are from: - // - Rodent IV - // - https://www.chessprogramming.net/perfect-perft/ - "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", - "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", - "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", - "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", - "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", - "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", - "1rbqk1nr/p3ppbp/2np2p1/2p5/1p2PP2/3PB1P1/PPPQ2BP/R2NK1NR b KQk - 0 1", - "r1bqk2r/pp1p1ppp/2n1pn2/2p5/1bPP4/2NBP3/PP2NPPP/R1BQK2R b KQkq - 0 1", - "rnb1kb1r/ppp2ppp/1q2p3/4P3/2P1Q3/5N2/PP1P1PPP/R1B1KB1R b KQkq - 0 1", - "r1b2rk1/pp2nppp/1b2p3/3p4/3N1P2/2P2NP1/PP3PBP/R3R1K1 b - - 0 1", - "n1q1r1k1/3b3n/p2p1bp1/P1pPp2p/2P1P3/2NBB2P/3Q1PK1/1R4N1 b - - 0 1", - "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", - "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", - "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", - "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", - "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", - "2r5/8/1n6/1P1p1pkp/p2P4/R1P1PKP1/8/1R6 w - - 0 1", - "r2q1rk1/1b1nbppp/4p3/3pP3/p1pP4/PpP2N1P/1P3PP1/R1BQRNK1 b - - 0 1", - "6k1/5pp1/7p/p1p2n1P/P4N2/6P1/1P3P1K/8 w - - 0 35", - "r4rk1/1pp1q1pp/p2p4/3Pn3/1PP1Pp2/P7/3QB1PP/2R2RK1 b - - 0 1", - - "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", - "1k6/1b6/8/8/7R/8/8/4K2R b K - 0 1", - "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1", // Illegal ep move #1 - "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1", // Illegal ep move #2 - "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1", // EP Capture Checks Opponent - "5k2/8/8/8/8/8/8/4K2R w K - 0 1", // Short Castling Gives Check - "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1", // Long Castling Gives Check - "r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1", // Castle Rights - "r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1", // Castling Prevented - "2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1", // Promote out of Check - "8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1", // Discovered Check - "4k3/1P6/8/8/8/8/K7/8 w - - 0 1", // Promote to give check - "8/P1k5/K7/8/8/8/8/8 w - - 0 1", // Under Promote to give check - "K1k5/8/P7/8/8/8/8/8 w - - 0 1", // Self Stalemate - "8/k1P5/8/1K6/8/8/8/8 w - - 0 1", // Stalemate & Checkmate - "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1", // Stalemate & Checkmate - - NULL -}; +#include "common-test.h" #define RD 0 #define WR 1 @@ -264,8 +187,8 @@ int main(int __unused ac, __unused char**av) { int i = 0; FILE *outfd; - pos_t *mypos = pos_new(), *fishpos = pos_new(); - + pos_t *pos, *fishpos = pos_new(); + char *fen; //bitboard_t wrong = 0x5088000040, tmp, loop; //bit_for_each64(loop, tmp, ) @@ -273,36 +196,44 @@ int main(int __unused ac, __unused char**av) hyperbola_init(); outfd = open_stockfish(); - while (fentest[i]) { + while ((fen = next_fen(MOVEGEN))) { //printf(">>>>> %s\n", test[i]); - fen2pos(mypos, fentest[i]); - pos_print(mypos); + printf("original fen %d: [%s]\n", i, fen); + if (!(pos = fen2pos(NULL, fen))) { + printf("wrong fen %d: [%s]\n", i, fen); + continue; + } + pos_print(pos); + fflush(stdout); - send_stockfish_fen(outfd, fishpos, fentest[i]); - printf("Fu "); - moves_print(fishpos, 0); - fflush(stdout); - gen_all_pseudomoves(mypos); - printf("Mu "); - moves_print(mypos, 0); - fflush(stdout); - puts("zobi"); - fflush(stdout); + /* print movelists */ + send_stockfish_fen(outfd, fishpos, fen); + gen_all_pseudomoves(pos); + //printf("Fu "); + //moves_print(fishpos, 0); + //fflush(stdout); + //printf("Mu "); + //moves_print(pos, 0); + //fflush(stdout); + + /* sort and print movelists */ move_sort_by_sq(fishpos); - printf("\nFs "); - moves_print(fishpos, 0); - fflush(stdout); + move_sort_by_sq(pos); + // printf("\nFs "); + // moves_print(fishpos, 0); + // fflush(stdout); + // printf("Ms "); + // moves_print(pos, 0); + // fflush(stdout); - move_sort_by_sq(mypos); - printf("Ms "); - moves_print(mypos, 0); - fflush(stdout); + /* compare movelists */ printf("\n"); - compare_moves(fishpos, mypos); + compare_moves(fishpos, pos); //pos_print_board_raw(pos, 1); //printf("%s\n", pos2fen(pos, str)); //get_stockfish_moves(test[i]); //exit(0); + pos_del(pos); i++; } fclose(outfd);