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)
This commit is contained in:
2024-03-04 21:34:29 +01:00
parent a499893f32
commit b351d198b8
18 changed files with 584 additions and 482 deletions

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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))

View File

@@ -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");
}
}

View File

@@ -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 */

View File

@@ -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 {

View File

@@ -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;

View File

@@ -14,7 +14,7 @@
#ifndef FEN_H
#define FEN_H
#include "position.h"
#include "chessdefs.h"
#define FENSTRLEN 92 /* secure FEN string size */

View File

@@ -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;

View File

@@ -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.

View File

@@ -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);
}
/*

View File

@@ -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);
* }
*/

View File

@@ -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 */

View File

@@ -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 <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#include <stdio.h>
#include <string.h>
#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;
}

188
test/common-test.h Normal file
View File

@@ -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 <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#include <stdio.h>
#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;
}

View File

@@ -1,39 +1,43 @@
//#include <stdio.h>
/* 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 <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#include "bug.h"
#include <stdio.h>
#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);
}
}
}

View File

@@ -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 <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#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);