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") # Copyright (C) 2021-2023 Bruno Raoult ("br")
# Licensed under the GNU General Public License v3.0 or later. # Licensed under the GNU General Public License v3.0 or later.
@@ -58,19 +58,19 @@ CPPFLAGS := -I$(BRINCDIR) -I$(INCDIR)
CPPFLAGS += -DNDEBUG # assert CPPFLAGS += -DNDEBUG # assert
CPPFLAGS += -DBUG_ON # bug.h CPPFLAGS += -DBUG_ON # brlib bug.h
CPPFLAGS += -DWARN_ON # bug.h CPPFLAGS += -DWARN_ON # brlib bug.h
#CPPFLAGS += -DDEBUG # global - unused
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_DEBUG_C # enable log() settings
CPPFLAGS += -DDEBUG_POOL # memory pools management #CPPFLAGS += -DDEBUG_POOL # memory pools management
#CPPFLAGS += -DDEBUG_FEN # FEN decoding #CPPFLAGS += -DDEBUG_FEN # FEN decoding
#CPPFLAGS += -DDEBUG_POS # position.c #CPPFLAGS += -DDEBUG_POS # position.c
CPPFLAGS += -DDEBUG_MOVE # move generation #CPPFLAGS += -DDEBUG_MOVE # move generation
CPPFLAGS += -DDEBUG_EVAL # eval functions #CPPFLAGS += -DDEBUG_EVAL # eval functions
CPPFLAGS += -DDEBUG_PIECE # piece list management #CPPFLAGS += -DDEBUG_PIECE # piece list management
CPPFLAGS += -DDEBUG_SEARCH # move search #CPPFLAGS += -DDEBUG_SEARCH # move search
CPPFLAGS += -DDIAGRAM_SYM # diagram with symbols CPPFLAGS += -DDIAGRAM_SYM # diagram with symbols
@@ -272,18 +272,23 @@ memcheck: targets
##################################### test binaries ##################################### test binaries
.PHONY: testing test .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 FEN_OBJS := fen.o position.o piece.o bitboard.o board.o hyperbola-quintessence.o \
BB_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 \ 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)) TEST := $(addprefix $(BINDIR)/,$(TEST))
FEN_OBJS := $(addprefix $(OBJDIR)/,$(FEN_OBJS)) FEN_OBJS := $(addprefix $(OBJDIR)/,$(FEN_OBJS))
BB_OBJS := $(addprefix $(OBJDIR)/,$(BB_OBJS)) BB_OBJS := $(addprefix $(OBJDIR)/,$(BB_OBJS))
MOVEGEN_OBJS := $(addprefix $(OBJDIR)/,$(MOVEGEN_OBJS)) MOVEGEN_OBJS := $(addprefix $(OBJDIR)/,$(MOVEGEN_OBJS))
ATTACK_OBJS := $(addprefix $(OBJDIR)/,$(ATTACK_OBJS))
test: test:
echo TEST=$(TEST) echo TEST=$(TEST)
@@ -291,18 +296,22 @@ test:
testing: $(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. @echo compiling $@ test executable.
@$(CC) $(ALL_CFLAGS) $< $(FEN_OBJS) $(ALL_LDFLAGS) -o $@ @$(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. @echo compiling $@ test executable.
@$(CC) $(ALL_CFLAGS) $< $(BB_OBJS) $(ALL_LDFLAGS) -o $@ @$(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. @echo compiling $@ test executable.
@$(CC) $(ALL_CFLAGS) $< $(MOVEGEN_OBJS) $(ALL_LDFLAGS) -o $@ @$(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 ##################################### Makefile debug
.PHONY: showflags wft .PHONY: showflags wft

View File

@@ -18,9 +18,10 @@
#include "bitboard.h" #include "bitboard.h"
#include "position.h" #include "position.h"
#include "hyperbola-quintessence.h" #include "hyperbola-quintessence.h"
#include "attack.h"
/** /**
* square_attackers() - find attackers on a square * sq_attackers() - find attackers on a square
* @pos: position * @pos: position
* @sq: square to test * @sq: square to test
* @c: attacker color * @c: attacker color
@@ -36,32 +37,69 @@
* @Return: a bitboard of attackers. * @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 attackers = 0;
bitboard_t from = mask(sq); bitboard_t sqbb = mask(sq);
bitboard_t c_pieces = pos->bb[c][ALL_PIECES]; bitboard_t c_pieces = pos->bb[c][ALL_PIECES];
bitboard_t occ = c_pieces | pos->bb[OPPONENT(c)][ALL_PIECES]; bitboard_t occ = c_pieces | pos->bb[OPPONENT(c)][ALL_PIECES];
bitboard_t to; bitboard_t to;
color_t opp = OPPONENT(c);
/* pawn */ /* pawn */
to = pos->bb[c][PAWN]; to = pos->bb[c][PAWN];
attackers |= pawn_push_upleft(from, c) & to; attackers |= pawn_shift_upleft(sqbb, opp) & to;
attackers |= pawn_push_upright(from, c) & 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 */ /* knight & king */
to = pos->bb[c][KNIGHT]; 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]; 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 */ /* bishop / queen */
to = pos->bb[c][BISHOP] | pos->bb[c][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 */ /* rook / queen */
to = pos->bb[c][ROOK] | pos->bb[c][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; 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 "chessdefs.h"
#include "bitboard.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 #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_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_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)) #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_up(sq, c) ((sq) + ((c) == WHITE ? NORTH: SOUTH))
#define pawn_push_upleft(sq, c) ((sq) + ((c) == WHITE ? NORTH_WEST: SOUTH_EAST)) #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)) #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") * Copyright (C) 2024 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * 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_print() - Print a board
* @board: &board_t to print * @board: &board_t to print
*/ */
void board_print(piece_t *board) void board_print(const piece_t *board)
{ {
printf(" +---+---+---+---+---+---+---+---+\n"); printf(" +---+---+---+---+---+---+---+---+\n");
for (int rank = 7; rank >= 0; --rank) { 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. * 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 // 6: blink
# define REVERSE "\e[7m▌" # 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"); 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 const char *sq_to_string(const square_t sq);
extern square_t sq_from_string(const char *sq_string); extern square_t sq_from_string(const char *sq_string);
extern void board_print(piece_t *board); extern void board_print(const piece_t *board);
extern void board_print_mask(piece_t *board, bitboard_t mask); 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 */ #endif /* _BOARD_H */

View File

@@ -22,13 +22,15 @@
//typedef ushort board; //typedef ushort board;
#define BOARDSIZE (8*8) #define BOARDSIZE (8*8)
/* from human to machine */ /* from human to machin e */
#define C2FILE(c) (tolower(c) - 'a') #define C2FILE(c) (tolower(c) - 'a')
#define C2RANK(c) (tolower(c) - '1') #define C2RANK(c) (tolower(c) - '1')
/* from machine to human */ /* from machine to huma n */
#define FILE2C(f) ((f) + 'a') #define FILE2C(f) ((f) + 'a')
#define RANK2C(r) ((r) + '1') #define RANK2C(r) ((r) + '1')
/* relative rank */
#define REL_RANK(r, c) ((7 * (c)) ^ r)
/* castle_t bits structure /* castle_t bits structure
*/ */
typedef enum { 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") * Copyright (C) 2021-2024 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * Licensed under the GNU General Public License v3.0 or later.
@@ -22,8 +22,8 @@
#include "chessdefs.h" #include "chessdefs.h"
#include "util.h" #include "util.h"
#include "piece.h" //#include "piece.h"
#include "bitboard.h" //#include "bitboard.h"
#include "position.h" #include "position.h"
#include "fen.h" #include "fen.h"
@@ -61,19 +61,18 @@ static const char *castle_str = "KQkq";
#define SKIP_BLANK(p) for(;isspace(*(p)); (p)++) #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 * @pos: position
* *
* fen_test() tests the following: * Test and fix the following:
* - fatal: number of pawns > 8 * - inconsistent castle flags (if K & R are not in correct position)
* - fatal: number of pieces > 16 * - inconsistent en-passant square (turn, bad pawn position)
* - fatal: number of kings != 1 *
* - fixable: inconsistent castle flags (if K & R are not in correct position) * pos_check() is also called, leading to fatal errors if something is wrong.
* - fixable: inconsistent en-passant square (turn, bad pawn position)
* *
* @return: 0 if OK, 1 if OK after fix, -1 if fatal issue. * @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"}; char *colstr[2] = { "white", "black"};
int error = 0, warning = 0; int error = 0, warning = 0;
@@ -82,24 +81,25 @@ static int fen_test(pos_t *pos)
if (pos->en_passant != SQUARE_NONE) { if (pos->en_passant != SQUARE_NONE) {
rank_t eprank = sq_rank(pos->en_passant); rank_t eprank = sq_rank(pos->en_passant);
file_t epfile = sq_file(pos->en_passant); file_t epfile = sq_file(pos->en_passant);
rank_t rank5 = pos->turn == WHITE? RANK_5: RANK_4; rank_t rank5 = REL_RANK(RANK_5, pos->turn);
rank_t rank6 = pos->turn == WHITE? RANK_6: RANK_3; rank_t rank6 = REL_RANK(RANK_6, pos->turn);
rank_t rank7 = pos->turn == WHITE? RANK_7: RANK_2; rank_t rank7 = REL_RANK(RANK_7, pos->turn);
piece_t pawn = pos->turn == WHITE? B_PAWN: W_PAWN; piece_t pawn = pos->turn == WHITE? B_PAWN: W_PAWN;
if (warn(eprank != rank6 || if (warn(eprank != rank6 ||
pos->board[sq_make(epfile, rank5)] != pawn || pos->board[sq_make(epfile, rank5)] != pawn ||
pos->board[sq_make(epfile, rank6)] != EMPTY || pos->board[sq_make(epfile, rank6)] != EMPTY ||
pos->board[sq_make(epfile, rank7)] != EMPTY, pos->board[sq_make(epfile, rank7)] != EMPTY,
"fen warn: wrong en-passant settings. (fixed)\n")) { "fen warn: wrong en-passant settings. (fixed)\n")) {
# ifdef DEBUG_FEN
printf("ep5=%o ep6=%o ep7=%o\n", sq_make(epfile, rank5), 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++; warning++;
pos->en_passant = SQUARE_NONE; pos->en_passant = SQUARE_NONE;
} }
} }
for (int color = WHITE; color <= BLACK; ++color) { for (int color = WHITE; color <= BLACK; ++color) {
int n;
rank_t rank1 = color == WHITE? RANK_1: RANK_8; rank_t rank1 = color == WHITE? RANK_1: RANK_8;
/* castling */ /* 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)) { err_line, err_pos, err_char, err_char)) {
return NULL; return NULL;
} }
if (fen_test(&tmppos) >= 0) { if (fen_check(&tmppos) < 0)
return NULL;
if (!pos) if (!pos)
pos = pos_new(); pos = pos_new();
*pos = tmppos; *pos = tmppos;
}
# ifdef DEBUG_FEN # ifdef DEBUG_FEN
pos_print_board_raw(&tmppos, 1); pos_print_raw(&tmppos, 1);
# endif # endif
return pos; return pos;

View File

@@ -14,7 +14,7 @@
#ifndef FEN_H #ifndef FEN_H
#define FEN_H #define FEN_H
#include "position.h" #include "chessdefs.h"
#define FENSTRLEN 92 /* secure FEN string size */ #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") * Copyright (C) 2024 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * Licensed under the GNU General Public License v3.0 or later.
@@ -21,7 +21,7 @@
#include "position.h" #include "position.h"
#include "move.h" #include "move.h"
#include "hyperbola-quintessence.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 */ /* pawn: relative rank and files */
bitboard_t rel_rank7 = (me == WHITE ? RANK_7bb : RANK_2bb); bitboard_t rel_rank7 = me == WHITE ? RANK_7bb : RANK_2bb;
bitboard_t rel_rank3 = (me == WHITE ? RANK_3bb : RANK_6bb); bitboard_t rel_rank3 = me == WHITE ? RANK_3bb : RANK_6bb;
//bitboard_t rel_filea = (me == WHITE ? FILE_Abb : FILE_Hbb); //bitboard_t rel_filea = (me == WHITE ? FILE_Abb : FILE_Hbb);
//bitboard_t rel_fileh = (me == WHITE ? FILE_Hbb : FILE_Abb); //bitboard_t rel_fileh = (me == WHITE ? FILE_Hbb : FILE_Abb);
int en_passant = pos->en_passant == SQUARE_NONE? 0: pos->en_passant; 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") * Copyright (C) 2024 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * 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 m1 = *(move_t *)p1;
move_t m2 = *(move_t *)p2; move_t m2 = *(move_t *)p2;
@@ -129,7 +129,7 @@ static int _moves_comp_square(const void *p1, const void *p2)
/* f1 == f2 */ /* f1 == f2 */
if (t1 < t2) return -1; if (t1 < t2) return -1;
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 * 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) 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 "piece.h"
#include "util.h" #include "util.h"
#include "board.h" #include "board.h"
#include "attack.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') *
****************************************************/
/** /**
* pos_new() - allocate a new position * 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 * 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)); pos_t *newpos = safe_malloc(sizeof(pos_t));
@@ -104,82 +93,143 @@ pos_t *pos_clear(pos_t *pos)
# ifdef DEBUG_POS # ifdef DEBUG_POS
printf("size(pos_board=%lu elt=%lu\n", sizeof(pos->board), sizeof(int)); printf("size(pos_board=%lu elt=%lu\n", sizeof(pos->board), sizeof(int));
# endif # endif
//for (square square = A1; square <= H8; ++square)
// pos->board[square] = EMPTY;
SET_WHITE(pos->turn);
pos->node_count = 0; pos->node_count = 0;
pos->turn = 0; pos->turn = WHITE;
pos->clock_50 = 0; pos->clock_50 = 0;
pos->plycount = 0; pos->plycount = 0;
pos->en_passant = SQUARE_NONE; pos->en_passant = SQUARE_NONE;
pos->castle = 0; 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 (color_t color = WHITE; color <= BLACK; ++color) {
for (piece_type_t piece = 0; piece <= KING; ++piece) for (piece_type_t piece = 0; piece <= KING; ++piece)
pos->bb[color][piece] = 0; pos->bb[color][piece] = 0;
pos->controlled[WHITE] = 0; pos->controlled[color] = 0;
pos->controlled[BLACK] = 0;
pos->king[color] = SQUARE_NONE; pos->king[color] = SQUARE_NONE;
} }
pos->moves.curmove = pos->moves.nmoves = 0;
//pos->mobility[WHITE] = 0; for (square_t sq = A1; sq <= H8; ++sq)
//pos->mobility[BLACK] = 0; pos->board[sq] = EMPTY;
//pos->moves_generated = false; pos->moves.curmove = 0;
//pos->moves_counted = false; pos->moves.nmoves = 0;
/* remove pieces / moves */
//pieces_del(pos, WHITE);
//pieces_del(pos, BLACK);
//moves_del(pos);
return pos; 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_print() - Print position and fen on stdout.
* @pos: &position * @pos: &position
*/ */
void pos_print(pos_t *pos) void pos_print(const pos_t *pos)
{ {
//int rank, file; char str[92];
//piece_t *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(pos->board); board_print(pos->board);
printf("fen %s\n", pos2fen(pos, str));
/* printf("checkers %s\n", pos_checkers2str(pos, str));
* 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]);
} }
/** /**
@@ -187,50 +237,30 @@ void pos_print(pos_t *pos)
* @pos: &position * @pos: &position
* @mask: mask of highlighted squares. * @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]; 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); 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("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 * @pos: &position
*/ */
void pos_pieces_print(pos_t *pos) void pos_print_pieces(const pos_t *pos)
{ {
int bit, count, cur; int bit, count, cur;
char *pname; char *pname;
@@ -254,123 +284,5 @@ void pos_pieces_print(pos_t *pos)
printf(" "); printf(" ");
} }
printf("\n"); 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; square_t en_passant;
castle_rights_t castle; 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 bb[2][PIECE_TYPE_MAX]; /* bb[0][PAWN], bb[1][ALL_PIECES] */
bitboard_t controlled[2]; bitboard_t controlled[2]; /* unsure */
//u16 mobility[2]; bitboard_t checkers; /* opponent checkers */
//struct list_head pieces[2]; /* pieces list, King is first */
//struct list_head moves[2];
piece_t board[BOARDSIZE]; piece_t board[BOARDSIZE];
movelist_t moves; movelist_t moves;
//int nmoves;
} pos_t; } 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); //void bitboard_print2(bitboard_t bb1, bitboard_t bb2, char *title);
extern pos_t *pos_new(); 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 void pos_del(pos_t *pos);
extern pos_t *pos_clear(pos_t *pos); extern pos_t *pos_clear(pos_t *pos);
extern void pos_print(pos_t *pos); extern bitboard_t pos_checkers(const pos_t *pos, const color_t color);
extern void pos_print_mask(pos_t *pos, bitboard_t mask); extern char *pos_checkers2str(const pos_t *pos, char *str);
extern void pos_pieces_print(pos_t *pos);
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 */ #endif /* POSITION_H */

View File

@@ -1,12 +1,22 @@
//#include "debug.h" /* bitboard-test.c - basic bitboard/hyperbola tests.
//#include "pool.h" *
* 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 <stdio.h>
#include <string.h> #include <string.h>
#include "bug.h" #include "bug.h"
#include "position.h"
#include "piece.h" #include "chessdefs.h"
#include "bitboard.h" #include "bitboard.h"
#include "hyperbola-quintessence.h" #include "hyperbola-quintessence.h"
@@ -27,26 +37,25 @@ int main(int __unused ac, __unused char**av)
bb_file[i], bb_rank[i], bb_file[i], bb_rank[i],
bb_knight[i], bb_king[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][A8], bb_between[A1][H8],
bb_between[A1][H1], bb_between[A2][A7], bb_between[A1][H1], bb_between[A2][A7],
bb_between[A2][G7], bb_between[A2][G2]); 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][C6], bb_between[C3][F6],
bb_between[C3][F3], bb_between[C3][E1], bb_between[C3][F3], bb_between[C3][E1],
bb_between[C3][C1], bb_between[C3][A1], bb_between[C3][C1], bb_between[C3][A1],
bb_between[C3][A3], bb_between[C3][A5]); 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][C6], bb_between[C4][F6],
bb_between[C4][F3], bb_between[C4][E1], bb_between[C4][F3], bb_between[C4][E1],
bb_between[C4][C1], bb_between[C4][A1], bb_between[C4][C1], bb_between[C4][A1],
bb_between[C4][A3], bb_between[C4][A5]); 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; 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 "chessdefs.h"
#include "bitboard.h" #include "bitboard.h"
#include "position.h" #include "position.h"
#include "fen.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; pos_t *pos;
bitboard_t mask = A1bb | C3bb | A8bb | G7bb | H8bb | H1bb;
const char *fen; const char *fen;
char revfen[128]; 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));
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); pos2fen(pos, revfen);
//printf("reverse fen=[%s]\n", pos2fen(pos, NULL)); printf("fen = [%s]\nrev = [%s]", fen, revfen);
comp = strcmp(fen, revfen); if (strcmp(fen, revfen))
printf("compare=%d - %s\n", comp, comp? "NOK": "OK"); printf(" **FIXED\n");
pos_print_board_raw(pos, 0); pos_del(pos);
pos_print_board_raw(pos, 1); }
}
} }

View File

@@ -1,103 +1,26 @@
//#include "debug.h" /* movegen-test.c - basic movegen tests.
//#include "pool.h" *
* 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <string.h>
#include "position.h" #include "chessdefs.h"
#include "piece.h"
#include "bitboard.h"
#include "fen.h" #include "fen.h"
#include "move.h" #include "move-gen.h"
#include "movegen.h" #include "position.h"
static char *fentest[] = { #include "common-test.h"
/* 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
};
#define RD 0 #define RD 0
#define WR 1 #define WR 1
@@ -264,8 +187,8 @@ int main(int __unused ac, __unused char**av)
{ {
int i = 0; int i = 0;
FILE *outfd; FILE *outfd;
pos_t *mypos = pos_new(), *fishpos = pos_new(); pos_t *pos, *fishpos = pos_new();
char *fen;
//bitboard_t wrong = 0x5088000040, tmp, loop; //bitboard_t wrong = 0x5088000040, tmp, loop;
//bit_for_each64(loop, tmp, ) //bit_for_each64(loop, tmp, )
@@ -273,36 +196,44 @@ int main(int __unused ac, __unused char**av)
hyperbola_init(); hyperbola_init();
outfd = open_stockfish(); outfd = open_stockfish();
while (fentest[i]) { while ((fen = next_fen(MOVEGEN))) {
//printf(">>>>> %s\n", test[i]); //printf(">>>>> %s\n", test[i]);
fen2pos(mypos, fentest[i]); printf("original fen %d: [%s]\n", i, fen);
pos_print(mypos); 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]); /* print movelists */
printf("Fu "); send_stockfish_fen(outfd, fishpos, fen);
moves_print(fishpos, 0); gen_all_pseudomoves(pos);
fflush(stdout); //printf("Fu ");
gen_all_pseudomoves(mypos); //moves_print(fishpos, 0);
printf("Mu "); //fflush(stdout);
moves_print(mypos, 0); //printf("Mu ");
fflush(stdout); //moves_print(pos, 0);
puts("zobi"); //fflush(stdout);
fflush(stdout);
/* sort and print movelists */
move_sort_by_sq(fishpos); move_sort_by_sq(fishpos);
printf("\nFs "); move_sort_by_sq(pos);
moves_print(fishpos, 0); // printf("\nFs ");
fflush(stdout); // moves_print(fishpos, 0);
// fflush(stdout);
// printf("Ms ");
// moves_print(pos, 0);
// fflush(stdout);
move_sort_by_sq(mypos); /* compare movelists */
printf("Ms ");
moves_print(mypos, 0);
fflush(stdout);
printf("\n"); printf("\n");
compare_moves(fishpos, mypos); compare_moves(fishpos, pos);
//pos_print_board_raw(pos, 1); //pos_print_board_raw(pos, 1);
//printf("%s\n", pos2fen(pos, str)); //printf("%s\n", pos2fen(pos, str));
//get_stockfish_moves(test[i]); //get_stockfish_moves(test[i]);
//exit(0); //exit(0);
pos_del(pos);
i++; i++;
} }
fclose(outfd); fclose(outfd);