From 87e7695873a3755cb5434301b6106bca25268ef2 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Thu, 7 Mar 2024 10:50:19 +0100 Subject: [PATCH] new pos/bb funcs, legal(), better castling gen, etc. [see commit details] - new sq_pinners - new pseudo_is_legal() (unfinished) - improve castling pseudo move gen - more position and lower level bitboard helper funcs: - pos_{_occ,between_occ,between_count,pinners} - bb_{rank,file,rel_rank,_sq_aligned,_sq_between) - rename some bitboard globals - replace bb ranks/files enums with defines (issue with clang) -> Need to find a way to use enum safely - tests: - add common-test.h - new attack-test.c --- Makefile | 24 +- brlib | 2 +- src/attack.c | 100 ++++++--- src/attack.h | 2 +- src/bitboard.c | 84 +++++-- src/bitboard.h | 224 +++++++++++++++---- src/board.h | 7 + src/chessdefs.h | 33 +-- src/fen.c | 14 +- src/hyperbola-quintessence.c | 6 +- src/move-gen.c | 207 ++++++++++------- src/move-gen.h | 5 +- src/move.c | 3 +- src/move.h | 11 +- src/position.c | 41 ++-- src/position.h | 61 ++++- test/bitboard-test.c | 16 +- test/common-test.h | 419 ++++++++++++++++++++++++----------- test/fen-test.c | 16 +- test/movegen-test.c | 11 +- 20 files changed, 913 insertions(+), 373 deletions(-) diff --git a/Makefile b/Makefile index 45e63d8..184960d 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ SHELL := /bin/bash CC := gcc +#CC := clang LD := ld BEAR := bear TOUCH := touch @@ -40,6 +41,7 @@ OBJ := $(addprefix $(OBJDIR)/,$(SRC_FN:.c=.o)) TSTSRC := $(wildcard $(TSTDIR)/*.c) LIB := br_$(shell uname -m) # library name +LIBS := $(strip -l$(LIB) -lreadline) DEP_FN := $(SRC_FN) DEP := $(addprefix $(DEPDIR)/,$(DEP_FN:.c=.d)) @@ -47,9 +49,6 @@ DEP := $(addprefix $(DEPDIR)/,$(DEP_FN:.c=.d)) TARGET_FN := brchess TARGET := $(addprefix $(BINDIR)/,$(TARGET_FN)) -LDFLAGS := -L$(BRLIBDIR) -LIBS := $(strip -l$(LIB) -lreadline) - ASMFILES := $(SRC:.c=.s) $(TSTSRC:.c=.s) CPPFILES := $(SRC:.c=.i) $(TSTSRC:.c=.i) @@ -68,6 +67,12 @@ CPPFLAGS += -DWARN_ON # brlib bug.h #CPPFLAGS += -DDEBUG_FEN # FEN decoding #CPPFLAGS += -DDEBUG_POS # position.c #CPPFLAGS += -DDEBUG_MOVE # move generation + +# attack.c +#CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS1 # sq_attackers details +CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers +CPPFLAGS += -DDEBUG_ATTACK_PINNERS # sq_pinners details + #CPPFLAGS += -DDEBUG_EVAL # eval functions #CPPFLAGS += -DDEBUG_PIECE # piece list management #CPPFLAGS += -DDEBUG_SEARCH # move search @@ -79,6 +84,7 @@ CPPFLAGS := $(strip $(CPPFLAGS)) ##################################### compiler flags CFLAGS := -std=gnu11 +#CFLAGS += -flto CFLAGS += -O1 CFLAGS += -g CFLAGS += -Wall @@ -92,6 +98,12 @@ CFLAGS += -Wmissing-declarations CFLAGS := $(strip $(CFLAGS)) +##################################### linker flags +LDFLAGS := -L$(BRLIBDIR) +#LDFLAGS += -flto + +LDFLAGS := $(strip $(LDFLAGS)) + ##################################### archiver/dependency flags ARFLAGS := rcs DEPFLAGS = -MMD -MP -MF $(DEPDIR)/$*.d @@ -193,7 +205,7 @@ cleanobjdir: cleanobj # "normal" ones, but do not imply to rebuild target. $(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR) $(DEPDIR) @echo compiling brchess module: $< "->" $@. - @$(CC) -c $(ALL_CFLAGS) $< -o $@ + $(CC) -c $(ALL_CFLAGS) $< -o $@ ##################################### brlib libraries .PHONY: cleanbrlib cleanallbrlib brlib @@ -279,9 +291,9 @@ FEN_OBJS := fen.o position.o piece.o bitboard.o board.o hyperbola-quintesse 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 \ - attack.o move.o movegen.o + attack.o move.o move-gen.o ATTACK_OBJS := fen.o position.o piece.o bitboard.o board.o hyperbola-quintessence.o \ - attack.o move.o movegen.o + attack.o move.o move-gen.o TEST := $(addprefix $(BINDIR)/,$(TEST)) diff --git a/brlib b/brlib index 9162db3..8ff163d 160000 --- a/brlib +++ b/brlib @@ -1 +1 @@ -Subproject commit 9162db31e3341f7ecceab25569e8cb6f91eb99ce +Subproject commit 8ff163dcf569105bbdec28860eff100280e32898 diff --git a/src/attack.c b/src/attack.c index a3e2998..b56db1c 100644 --- a/src/attack.c +++ b/src/attack.c @@ -35,60 +35,110 @@ * T on @sq could attack a white T piece. * * @Return: a bitboard of attackers. - * */ + +// #define DEBUG_ATTACK_ATTACKERS1 + bitboard_t sq_attackers(const pos_t *pos, const square_t sq, const color_t c) { - bitboard_t attackers = 0; + bitboard_t attackers = 0, tmp; 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 occ = pos_occ(pos); bitboard_t to; color_t opp = OPPONENT(c); /* pawn */ to = pos->bb[c][PAWN]; - attackers |= pawn_shift_upleft(sqbb, opp) & to; -# ifdef DEBUG_ATTACK - bitboard_print("sq_attackers after pawn upleft", attackers); + tmp = pawn_shift_upleft(sqbb, opp) & to; + attackers |= tmp; +# ifdef DEBUG_ATTACK_ATTACKERS1 + bb_print("att pawn upleft", tmp); # endif - attackers |= pawn_shift_upright(sqbb, opp) & to; -# ifdef DEBUG_ATTACK - bitboard_print("sq_attackers pawn upright", attackers); + tmp = pawn_shift_upright(sqbb, opp) & to; + attackers |= tmp; +# ifdef DEBUG_ATTACK_ATTACKERS1 + bb_print("att pawn upright", tmp); # endif /* knight & king */ to = pos->bb[c][KNIGHT]; - attackers |= bb_knight_moves(to, sq); -# ifdef DEBUG_ATTACK - bitboard_print("sq_attackers after knight", attackers); + tmp = bb_knight_moves(to, sq); + attackers |= tmp; +# ifdef DEBUG_ATTACK_ATTACKERS1 + bb_print("att knight", tmp); # endif to = pos->bb[c][KING]; - attackers |= bb_king_moves(to, sq); -# ifdef DEBUG_ATTACK - bitboard_print("sq_attackers after king", attackers); + tmp = bb_king_moves(to, sq); + attackers |= tmp; +# ifdef DEBUG_ATTACK_ATTACKERS1 + bb_print("att king", tmp); # endif /* bishop / queen */ to = pos->bb[c][BISHOP] | pos->bb[c][QUEEN]; - attackers |= hyperbola_bishop_moves(occ, sq) & to; -# ifdef DEBUG_ATTACK - bitboard_print("sq_attackers after bishop/queen", attackers); + tmp = hyperbola_bishop_moves(occ, sq) & to; + attackers |= tmp; +# ifdef DEBUG_ATTACK_ATTACKERS1 + bb_print("att bishop/queen", tmp); # endif /* rook / queen */ to = pos->bb[c][ROOK] | pos->bb[c][QUEEN]; -# 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); + tmp = hyperbola_rook_moves(occ, sq) & to; + attackers |= tmp; +# ifdef DEBUG_ATTACK_ATTACKERS1 + bb_print("att rook/queen", tmp); + bb_print("ATTACKERS", attackers); + printf("attackers=%lx\n", attackers); # endif return attackers; } +/** + * sq_pinners() - find pinners on a square + * @pos: position + * @sq: square to test + * @c: attacker color + * + * Find all @c pieces which are separated from @sq by only one piece (of + * any color). + * + * @Return: a bitboard of attackers. + */ +bitboard_t sq_pinners(const pos_t *pos, const square_t sq, const color_t c) +{ + bitboard_t attackers, pinners = 0; + bitboard_t occ = pos_occ(pos); + bitboard_t maybe_pinner, tmp, lines; + + /* bishop type */ + attackers = pos->bb[c][BISHOP] | pos->bb[c][QUEEN]; + /* occupancy on sq diag and antidiag */ + lines = (bb_sqdiag[sq] | bb_sqanti[sq]) & occ; + bit_for_each64(maybe_pinner, tmp, attackers) { + bitboard_t between = bb_between_excl[maybe_pinner][sq]; + /* keep only squares between AND on sq diag/anti */ + if (popcount64(between & lines) == 1) + pinners |= mask(maybe_pinner); + } + + /* same for rook type */ + attackers = pos->bb[c][ROOK] | pos->bb[c][QUEEN]; + lines = (bb_sqrank[sq] | bb_sqfile[sq]) & occ; + bit_for_each64(maybe_pinner, tmp, attackers) { + bitboard_t between = bb_between_excl[maybe_pinner][sq]; + if (popcount64(between & lines) == 1) + pinners |= mask(maybe_pinner); + } +# ifdef DEBUG_ATTACK_ATTACKERS1 + char str[32]; + printf("pinners : %s\n", pos_pinners2str(pos, str, sizeof(str))); + printf("pinners : %lx\n", pinners); +# endif + return pinners; +} + /** * sq_attackers() - find all attackers on a square * @pos: position diff --git a/src/attack.h b/src/attack.h index 9872874..c388939 100644 --- a/src/attack.h +++ b/src/attack.h @@ -19,5 +19,5 @@ 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); - +extern bitboard_t sq_pinners(const pos_t *pos, const square_t sq, const color_t c); #endif diff --git a/src/bitboard.c b/src/bitboard.c index 03e4eaf..4f4aa60 100644 --- a/src/bitboard.c +++ b/src/bitboard.c @@ -21,6 +21,13 @@ #include "board.h" #include "bitboard.h" +bitboard_t bb_sq[64]; +bitboard_t bb_sqrank[64], bb_sqfile[64], bb_sqdiag[64], bb_sqanti[64]; +bitboard_t bb_between_excl[64][64]; +bitboard_t bb_between[64][64]; + +bitboard_t bb_knight[64], bb_king[64]; + /* vectors are clockwise from N */ static int knight_vector[] = { NORTH_EAST + NORTH, NORTH_EAST + EAST, @@ -33,14 +40,6 @@ static int king_vector[8] = { SOUTH, SOUTH_WEST, WEST, NORTH_WEST }; -bitboard_t bb_sq[64]; -bitboard_t bb_rank[64], bb_file[64], bb_diag[64], bb_anti[64]; -bitboard_t bb_between_excl[64][64]; -bitboard_t bb_between[64][64]; - -bitboard_t bb_knight[64], bb_king[64]; -bitboard_t bb_pawn_push[2][64], bb_bpawn_attack[2][64], bb_pawn_ep[2][64]; - /** * bitboard_between_excl() - get bitboard of squares between two squares. * @sq1, @sq2: The two square_t squares @@ -81,12 +80,13 @@ bitboard_t bitboard_between_excl(square_t sq1, square_t sq2) * * Generate the following bitboards : * bb_sq[64]: square to bitboard + * bb_sqrank[64]: square to rank + * bb_sqfile[64]: square to file + * bb_sqdiag[64]: square to diagonal + * bb_sqanti[64]: square to antidiagonal + * * bb_between_excl[64][64]: strict squares between two squares * bb_between[64][64]: squares between two squares including second square - * bb_rank[64]: square to rank - * bb_file[64]: square to file - * bb_diagonal[64]: square to diagonal - * bb_antidiagonal[64]: square to antidiagonal * * And the following pseudo move masks: * bb_knight[64]: knight moves @@ -134,10 +134,10 @@ void bitboard_init(void) } } for (square_t sq = 0; sq < 64; ++sq) { - bb_file[sq] = tmpbb[sq][0]; - bb_rank[sq] = tmpbb[sq][1]; - bb_diag[sq] = tmpbb[sq][2]; - bb_anti[sq] = tmpbb[sq][3]; + bb_sqfile[sq] = tmpbb[sq][0]; + bb_sqrank[sq] = tmpbb[sq][1]; + bb_sqdiag[sq] = tmpbb[sq][2]; + bb_sqanti[sq] = tmpbb[sq][3]; } /* 3) knight and king moves */ @@ -189,11 +189,11 @@ bitboard_t bb_king_moves(bitboard_t notmine, square_t sq) } /** - * bitboard_print() - print simple bitboard representation + * bb_print() - print simple bitboard representation * @title: a string or NULL * @bitboard: the bitboard */ -void bitboard_print(const char *title, const bitboard_t bitboard) +void bb_print(const char *title, const bitboard_t bitboard) { //char c = p? p: 'X'; if (title) @@ -210,14 +210,14 @@ void bitboard_print(const char *title, const bitboard_t bitboard) } /** - * bitboard_print_multi() - print multiple bitboards horizontally + * bb_print_multi() - print multiple bitboards horizontally * @title: a string or NULL * @n: number of bitboards * @bb_ptr...: pointers to bitboards * * @n is the number of bitboards to print. If @n > 10, it is reduced to 10 */ -void bitboard_print_multi(const char *title, int n, ...) +void bb_print_multi(const char *title, int n, ...) { bitboard_t bb[8]; va_list ap; @@ -252,13 +252,13 @@ void bitboard_print_multi(const char *title, int n, ...) } /** - * bitboard_rank_sprint() - print an u8 rank binary representation + * bb_rank_sprint() - print an u8 rank binary representation * @str: the destination string * @bb8: the uchar to print * * @return: @str, filled with ascii representation */ -char *bitboard_rank_sprint(char *str, const uchar bb8) +char *bb_rank_sprint(char *str, const uchar bb8) { file_t f; for (f = FILE_A; f <= FILE_H; ++f) { @@ -269,3 +269,43 @@ char *bitboard_rank_sprint(char *str, const uchar bb8) //printf("\n"); return str; } + +/** + * bb_sq2str() - convert bitboard to a string with a list of squares. + * @bb: bitboard + * @str: destination string + * @len: max @str length + * + * @str will be filled with the list of string representation of @bb, up to @len + * characters. + * 3 characters are used per square, so, for example, @str len should be : + * - For a valid position checkers (two checkers max): 2*3 + 1 = 7. + * - For a valid position pinners (8 pinners): 8*3 + 1 = 25. + * - for a full bitboard: 64*3 + 1 = 193. + * + * If @len is not enough to fill all moves, the last fitting move is replaced by "...". + * + * @return: The string. + */ +char *bb_sq2str(const bitboard_t bb, char *str, const int len) +{ + bitboard_t tmp, sq; + int nocc = popcount64(bb); + int needed = 3 * nocc + 1; + int willdo = (int) len >= needed ? nocc: (len - 1) / 3 - 1; + int current = 0; + + //printf("sq2str bb=%lx len=%d willdo=%d\n", bb, len, willdo); + bit_for_each64(sq, tmp, bb) { + if (current == willdo) { + strcpy(str + current * 3, "..."); + } else { + strcpy(str + current * 3, sq_to_string(sq)); + str[current * 3 + 2] = ' '; + } + if (++current > willdo) + break; + } + str[current * 3] = 0; + return str; +} diff --git a/src/bitboard.h b/src/bitboard.h index a3c950a..3eb0c27 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -30,53 +30,192 @@ extern bitboard_t bb_between_excl[64][64]; /* squares between sq1 and sq2, including sq2 */ extern bitboard_t bb_between[64][64]; -/* bb_rank[64]: square to rank - * bb_file[64]: square to file - * bb_diag[64]: square to diagonal - * bb_anti[64]: square to antidiagonal +/* bb_sqrank[64]: square to rank + * bb_sqfile[64]: square to file + * bb_sqdiag[64]: square to diagonal + * bb_sqanti[64]: square to antidiagonal */ -extern bitboard_t bb_rank[64], bb_file[64], bb_diag[64], bb_anti[64]; +extern bitboard_t bb_sqrank[64], bb_sqfile[64], bb_sqdiag[64], bb_sqanti[64]; /* knight and king moves */ extern bitboard_t bb_knight[64], bb_king[64]; -/* Unsure if it will work with all compilers. Use #define instead ? + +/* TODO (maybe C23?) when we can ensure an enum can be u64 + * + * enum { + * A1bb = mask(A1), A2bb = mask(A2), A3bb = mask(A3), A4bb = mask(A4), + * A5bb = mask(A5), A6bb = mask(A6), A7bb = mask(A7), A8bb = mask(A8), + * B1bb = mask(B1), B2bb = mask(B2), B3bb = mask(B3), B4bb = mask(B4), + * B5bb = mask(B5), B6bb = mask(B6), B7bb = mask(B7), B8bb = mask(B8), + * C1bb = mask(C1), C2bb = mask(C2), C3bb = mask(C3), C4bb = mask(C4), + * C5bb = mask(C5), C6bb = mask(C6), C7bb = mask(C7), C8bb = mask(C8), + * D1bb = mask(D1), D2bb = mask(D2), D3bb = mask(D3), D4bb = mask(D4), + * D5bb = mask(D5), D6bb = mask(D6), D7bb = mask(D7), D8bb = mask(D8), + * E1bb = mask(E1), E2bb = mask(E2), E3bb = mask(E3), E4bb = mask(E4), + * E5bb = mask(E5), E6bb = mask(E6), E7bb = mask(E7), E8bb = mask(E8), + * F1bb = mask(F1), F2bb = mask(F2), F3bb = mask(F3), F4bb = mask(F4), + * F5bb = mask(F5), F6bb = mask(F6), F7bb = mask(F7), F8bb = mask(F8), + * G1bb = mask(G1), G2bb = mask(G2), G3bb = mask(G3), G4bb = mask(G4), + * G5bb = mask(G5), G6bb = mask(G6), G7bb = mask(G7), G8bb = mask(G8), + * H1bb = mask(H1), H2bb = mask(H2), H3bb = mask(H3), H4bb = mask(H4), + * H5bb = mask(H5), H6bb = mask(H6), H7bb = mask(H7), H8bb = mask(H8), + * }; + * + * enum { + * FILE_Abb = 0x0101010101010101ull, FILE_Bbb = 0x0202020202020202ull, + * FILE_Cbb = 0x0404040404040404ull, FILE_Dbb = 0x0808080808080808ull, + * FILE_Ebb = 0x1010101010101010ull, FILE_Fbb = 0x2020202020202020ull, + * FILE_Gbb = 0x4040404040404040ull, FILE_Hbb = 0x8080808080808080ull, + * + * RANK_1bb = 0x00000000000000ffull, RANK_2bb = 0x000000000000ff00ull, + * RANK_3bb = 0x0000000000ff0000ull, RANK_4bb = 0x00000000ff000000ull, + * RANK_5bb = 0x000000ff00000000ull, RANK_6bb = 0x0000ff0000000000ull, + * RANK_7bb = 0x00ff000000000000ull, RANK_8bb = 0xff00000000000000ull + * }; */ -enum { - A1bb = mask(A1), A2bb = mask(A2), A3bb = mask(A3), A4bb = mask(A4), - A5bb = mask(A5), A6bb = mask(A6), A7bb = mask(A7), A8bb = mask(A8), - B1bb = mask(B1), B2bb = mask(B2), B3bb = mask(B3), B4bb = mask(B4), - B5bb = mask(B5), B6bb = mask(B6), B7bb = mask(B7), B8bb = mask(B8), - C1bb = mask(C1), C2bb = mask(C2), C3bb = mask(C3), C4bb = mask(C4), - C5bb = mask(C5), C6bb = mask(C6), C7bb = mask(C7), C8bb = mask(C8), - D1bb = mask(D1), D2bb = mask(D2), D3bb = mask(D3), D4bb = mask(D4), - D5bb = mask(D5), D6bb = mask(D6), D7bb = mask(D7), D8bb = mask(D8), - E1bb = mask(E1), E2bb = mask(E2), E3bb = mask(E3), E4bb = mask(E4), - E5bb = mask(E5), E6bb = mask(E6), E7bb = mask(E7), E8bb = mask(E8), - F1bb = mask(F1), F2bb = mask(F2), F3bb = mask(F3), F4bb = mask(F4), - F5bb = mask(F5), F6bb = mask(F6), F7bb = mask(F7), F8bb = mask(F8), - G1bb = mask(G1), G2bb = mask(G2), G3bb = mask(G3), G4bb = mask(G4), - G5bb = mask(G5), G6bb = mask(G6), G7bb = mask(G7), G8bb = mask(G8), - H1bb = mask(H1), H2bb = mask(H2), H3bb = mask(H3), H4bb = mask(H4), - H5bb = mask(H5), H6bb = mask(H6), H7bb = mask(H7), H8bb = mask(H8), -}; -enum { - FILE_Abb = 0x0101010101010101ULL, FILE_Bbb = 0x0202020202020202ULL, - FILE_Cbb = 0x0404040404040404ULL, FILE_Dbb = 0x0808080808080808ULL, - FILE_Ebb = 0x1010101010101010ULL, FILE_Fbb = 0x2020202020202020ULL, - FILE_Gbb = 0x4040404040404040ULL, FILE_Hbb = 0x8080808080808080ULL, - - RANK_1bb = 0x00000000000000ffULL, RANK_2bb = 0x000000000000ff00ULL, - RANK_3bb = 0x0000000000ff0000ULL, RANK_4bb = 0x00000000ff000000ULL, - RANK_5bb = 0x000000ff00000000ULL, RANK_6bb = 0x0000ff0000000000ULL, - RANK_7bb = 0x00ff000000000000ULL, RANK_8bb = 0xff00000000000000ULL -}; - -/* https://www.chessprogramming.org/Flipping_Mirroring_and_Rotating#Rotation +/* generated with bash: + * R="ABCDEFGH" + * F="12345678" + * for i in {0..63}; do + * printf "#define %c%cbb %#018llxull\n" ${R:i/8:1} ${F:i%8:1} $((1 << i)) + * done */ -static __always_inline bitboard_t bb_rotate_90(bitboard_t b) +#define A1bb 0x0000000000000001ull +#define A2bb 0x0000000000000002ull +#define A3bb 0x0000000000000004ull +#define A4bb 0x0000000000000008ull +#define A5bb 0x0000000000000010ull +#define A6bb 0x0000000000000020ull +#define A7bb 0x0000000000000040ull +#define A8bb 0x0000000000000080ull +#define B1bb 0x0000000000000100ull +#define B2bb 0x0000000000000200ull +#define B3bb 0x0000000000000400ull +#define B4bb 0x0000000000000800ull +#define B5bb 0x0000000000001000ull +#define B6bb 0x0000000000002000ull +#define B7bb 0x0000000000004000ull +#define B8bb 0x0000000000008000ull +#define C1bb 0x0000000000010000ull +#define C2bb 0x0000000000020000ull +#define C3bb 0x0000000000040000ull +#define C4bb 0x0000000000080000ull +#define C5bb 0x0000000000100000ull +#define C6bb 0x0000000000200000ull +#define C7bb 0x0000000000400000ull +#define C8bb 0x0000000000800000ull +#define D1bb 0x0000000001000000ull +#define D2bb 0x0000000002000000ull +#define D3bb 0x0000000004000000ull +#define D4bb 0x0000000008000000ull +#define D5bb 0x0000000010000000ull +#define D6bb 0x0000000020000000ull +#define D7bb 0x0000000040000000ull +#define D8bb 0x0000000080000000ull +#define E1bb 0x0000000100000000ull +#define E2bb 0x0000000200000000ull +#define E3bb 0x0000000400000000ull +#define E4bb 0x0000000800000000ull +#define E5bb 0x0000001000000000ull +#define E6bb 0x0000002000000000ull +#define E7bb 0x0000004000000000ull +#define E8bb 0x0000008000000000ull +#define F1bb 0x0000010000000000ull +#define F2bb 0x0000020000000000ull +#define F3bb 0x0000040000000000ull +#define F4bb 0x0000080000000000ull +#define F5bb 0x0000100000000000ull +#define F6bb 0x0000200000000000ull +#define F7bb 0x0000400000000000ull +#define F8bb 0x0000800000000000ull +#define G1bb 0x0001000000000000ull +#define G2bb 0x0002000000000000ull +#define G3bb 0x0004000000000000ull +#define G4bb 0x0008000000000000ull +#define G5bb 0x0010000000000000ull +#define G6bb 0x0020000000000000ull +#define G7bb 0x0040000000000000ull +#define G8bb 0x0080000000000000ull +#define H1bb 0x0100000000000000ull +#define H2bb 0x0200000000000000ull +#define H3bb 0x0400000000000000ull +#define H4bb 0x0800000000000000ull +#define H5bb 0x1000000000000000ull +#define H6bb 0x2000000000000000ull +#define H7bb 0x4000000000000000ull +#define H8bb 0x8000000000000000ull + +#define FILE_Abb 0x0101010101010101ull +#define FILE_Bbb 0x0202020202020202ull +#define FILE_Cbb 0x0404040404040404ull +#define FILE_Dbb 0x0808080808080808ull +#define FILE_Ebb 0x1010101010101010ull +#define FILE_Fbb 0x2020202020202020ull +#define FILE_Gbb 0x4040404040404040ull +#define FILE_Hbb 0x8080808080808080ull + +#define RANK_1bb 0x00000000000000ffull +#define RANK_2bb 0x000000000000ff00ull +#define RANK_3bb 0x0000000000ff0000ull +#define RANK_4bb 0x00000000ff000000ull +#define RANK_5bb 0x000000ff00000000ull +#define RANK_6bb 0x0000ff0000000000ull +#define RANK_7bb 0x00ff000000000000ull +#define RANK_8bb 0xff00000000000000ull + + +/* +static __always_inline bitboard_t bb_rank(int rank) { - return b; + return RANK_1bb << (rank * 8); +} +static __always_inline bitboard_t bb_file(int file) +{ + return FILE_Abb << file; +} +static __always_inline bitboard_t bb_rel_rank(int rank, color) +{ + return RANK_1bb << (rank * 8); +} +static __always_inline bitboard_t bb_file(int file) +{ + return FILE_Abb << file; +} +*/ + +#define BB_RANK(r) ((u64) RANK_1bb << ((r) * 8)) +#define BB_FILE(f) ((u64) FILE_Abb << (f)) + +#define BB_REL_RANK(r, c) (RANK_1bb << (SQ_REL_RANK((r), (c)) * 8)) +#define BB_REL_FILE(f, c) (FILE_Abb << (SQ_REL_RANK((f), (c)))) + +/** + * bb_sq_aligned() - check if two squares are aligned (same file or rank). + * @sq1: square 1 + * @sq2: square 2 + * + * @sq2 is included in return value, to be non zero if the two squares + * are neighbors. + * + * @return: bitboard of squares between @sq1 and @sq2, including @sq2. + */ +static __always_inline bitboard_t bb_sq_aligned(square_t sq1, square_t sq2) +{ + return bb_between[sq1][sq2]; +} + +/** + * bb_sq_between() - check if a square is between two squares + * @sq: the possibly "in-between" square + * @sq1: square 1 + * @sq2: square 2 + * + * @return: bitboard of @betw if between @sq1 and @sq2. + */ +static __always_inline bitboard_t bb_sq_between(square_t sq, square_t sq1, square_t sq2) +{ + return bb_between_excl[sq1][sq2] & mask(sq); } /* TODO: when OK, replace with macros */ @@ -131,8 +270,9 @@ extern void bitboard_init(void); extern bitboard_t bb_knight_moves(bitboard_t occ, square_t sq); extern bitboard_t bb_king_moves(bitboard_t occ, square_t sq); -extern void bitboard_print(const char *title, const bitboard_t bitboard); -extern void bitboard_print_multi(const char *title, const int n, ...); -extern char *bitboard_rank_sprint(char *str, const uchar bb8); +extern void bb_print(const char *title, const bitboard_t bitboard); +extern void bb_print_multi(const char *title, const int n, ...); +extern char *bb_rank_sprint(char *str, const uchar bb8); +extern char *bb_sq2str(const bitboard_t bb, char *str, int len); #endif /* _BITBOARD_H */ diff --git a/src/board.h b/src/board.h index 3e28cd2..9e5db17 100644 --- a/src/board.h +++ b/src/board.h @@ -20,6 +20,13 @@ #include "piece.h" #include "bitboard.h" +/* 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') + /* a square is defined as * rrrfff */ diff --git a/src/chessdefs.h b/src/chessdefs.h index 7bae0b1..ff17548 100644 --- a/src/chessdefs.h +++ b/src/chessdefs.h @@ -16,21 +16,14 @@ #include "brlib.h" /* brlib types */ -#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 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') +#define ONE 1ull +#define C64(const_u64) const_u64##ULL +#define mask(i) ( (u64) (ONE << (i)) ) +#define BOARDSIZE (8*8) /* relative rank */ -#define REL_RANK(r, c) ((7 * (c)) ^ r) +#define SQ_REL_RANK(r, c) (rank_t)((7 * (c)) ^ r) + /* castle_t bits structure */ typedef enum { @@ -38,10 +31,20 @@ typedef enum { CASTLE_WQ = (1 << 1), /* 0x02 00000010 */ CASTLE_BK = (1 << 2), /* 0x04 00000100 */ CASTLE_BQ = (1 << 3), /* 0x08 00001000 */ + + CASTLE_W = (CASTLE_WK | CASTLE_WQ), /* 00000011 W castle mask */ + CASTLE_B = (CASTLE_BK | CASTLE_BQ), /* 00001100 B castle mask */ + + CASTLE_K = (1 << 0), /* generic K/Q, after doing : */ + CASTLE_Q = (1 << 1), /* flags >> (2 * color) */ } castle_rights_t; -#define CASTLE_W (CASTLE_WK | CASTLE_WQ) /* 00000011 W castle mask */ -#define CASTLE_B (CASTLE_BK | CASTLE_BQ) /* 00001100 B castle mask */ +/* determine is oo or ooo is possible with castle flags f and color c + */ +#define NORM_CASTLE(f, c) ((f) >> (2 * (c))) /* shift flags to bits 0/1 */ +#define CAN_OO(f, c) (NORM_CASTLE(f, c) & CASTLE_K) +#define CAN_OOO(f, c) (NORM_CASTLE(f, c) & CASTLE_Q) +#define CAN_CASTLE(f, c) (CAN_OO(f, c) | CAN_OOO(f, c)) /* game phases */ diff --git a/src/fen.c b/src/fen.c index 605b015..1abf318 100644 --- a/src/fen.c +++ b/src/fen.c @@ -81,9 +81,9 @@ static int fen_check(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 = REL_RANK(RANK_5, pos->turn); - rank_t rank6 = REL_RANK(RANK_6, pos->turn); - rank_t rank7 = REL_RANK(RANK_7, pos->turn); + rank_t rank5 = SQ_REL_RANK(RANK_5, pos->turn); + rank_t rank6 = SQ_REL_RANK(RANK_6, pos->turn); + rank_t rank7 = SQ_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 || @@ -129,11 +129,11 @@ static int fen_check(pos_t *pos) pos->castle &= ~castle_q; } } - } if (!(error = pos_check(pos, 0))) { /* TODO: Should it really be here ? */ pos->checkers = pos_checkers(pos, pos->turn); + pos->pinners = pos_pinners(pos, pos->turn); } return error ? -1: warning; } @@ -256,8 +256,10 @@ end: if (fen_check(&tmppos) < 0) return NULL; if (!pos) - pos = pos_new(); - *pos = tmppos; + pos = pos_dup(&tmppos); + //puts("prout 1"); + //pos_print_raw(&tmppos, 1); + //puts("prout 2"); # ifdef DEBUG_FEN pos_print_raw(&tmppos, 1); # endif diff --git a/src/hyperbola-quintessence.c b/src/hyperbola-quintessence.c index c04953e..ec8f85b 100644 --- a/src/hyperbola-quintessence.c +++ b/src/hyperbola-quintessence.c @@ -132,17 +132,17 @@ static bitboard_t hyperbola_moves(const bitboard_t pieces, const square_t sq, static bitboard_t hyperbola_file_moves(bitboard_t occ, square_t sq) { - return hyperbola_moves(occ, sq, bb_file[sq]); + return hyperbola_moves(occ, sq, bb_sqfile[sq]); } static bitboard_t hyperbola_diag_moves(bitboard_t occ, square_t sq) { - return hyperbola_moves(occ, sq, bb_diag[sq]); + return hyperbola_moves(occ, sq, bb_sqdiag[sq]); } static bitboard_t hyperbola_anti_moves(bitboard_t occ, square_t sq) { - return hyperbola_moves(occ, sq, bb_anti[sq]); + return hyperbola_moves(occ, sq, bb_sqanti[sq]); } bitboard_t hyperbola_bishop_moves(bitboard_t occ, square_t sq) diff --git a/src/move-gen.c b/src/move-gen.c index 8bc5c68..def592f 100644 --- a/src/move-gen.c +++ b/src/move-gen.c @@ -14,42 +14,107 @@ #include #include "bitops.h" +#include "bug.h" +#include "chessdefs.h" #include "board.h" #include "bitboard.h" #include "piece.h" #include "position.h" #include "move.h" #include "hyperbola-quintessence.h" +#include "attack.h" #include "move-gen.h" /** - * gen_pawn_push() - generate pawn push + * gen_castle() - generate c * @pos: position * * Generate all pseudo pawn pushes. */ //int gen_pawn_push(pos_t *pos, bitboard_t occ) - //{ +//{ //} +/** + * pseudo_is_legal() - check if a move is legal. + * @pos: position + * @move: move_t + * + * We check all possible invalid moves: + * (1) King: + * - K moves to a controlled square + * - Castling: + * - K passes a controlled square - already done in pseudomove gen + * + * (2) En-passant: + * - pinned taking pawn, done in (3) + * - taking and taken pawn on same rank than king, discovered check on rank + * + * (3) Pinned pieces: + * - if destination square quits "pinned line" + * + * @return: true if move is valid, false otherwise. + */ +bool pseudo_is_legal(pos_t *pos, move_t move) +{ + int from = move_from(move); + int to = move_to(move); + int us = pos->turn; + int them = OPPONENT(us); + int piece = pos->board[from]; + + /* (1) - castling, skipped */ + + /* (2) - King */ + if (piece == KING) { + /* For castling, we already intermediate and destination squares + * attacks in pseudo move generation, so we only care destination + * square here. + */ + return sq_attackers(pos, to, them) ? false: true; + } + + /* 3 - en-passant */ + + + /* + * if (bb_test(pins, from) && !bb_test(Ray[king][from], to)) + * return false; + * // En-passant special case: also illegal if self-check through the en-passant captured pawn + * if (to == pos->epSquare && piece == PAWN) { + * const int us = pos->turn, them = opposite(us); + * bitboard_t occ = pos_pieces(pos); + * bb_clear(&occ, from); + * bb_set(&occ, to); + * bb_clear(&occ, to + push_inc(them)); + * return !(bb_rook_attacks(king, occ) & pos_pieces_cpp(pos, them, ROOK, QUEEN)) && + * !(bb_bishop_attacks(king, occ) & pos_pieces_cpp(pos, them, BISHOP, QUEEN)); + * } else + * return true; + */ + return true; +} + /** * gen_all_pseudomoves() - generate all pseudo moves * @pos: position * * Generate all moves, no check is done on validity due to castle rules, * or check (pinned pieces, etc...). + * + * @return: The number of moves. */ int gen_all_pseudomoves(pos_t *pos) { - color_t me = pos->turn, enemy = OPPONENT(me); + color_t us = pos->turn, them = OPPONENT(us); - bitboard_t my_pieces = pos->bb[me][ALL_PIECES]; + bitboard_t my_pieces = pos->bb[us][ALL_PIECES]; bitboard_t not_my_pieces = ~my_pieces; - bitboard_t enemy_pieces = pos->bb[enemy][ALL_PIECES]; + bitboard_t enemy_pieces = pos->bb[them][ALL_PIECES]; bitboard_t occ = my_pieces | enemy_pieces; bitboard_t empty = ~occ; @@ -62,78 +127,75 @@ int gen_all_pseudomoves(pos_t *pos) int from, to; + /* king */ + from = pos->king[us]; + movebits = bb_king_moves(not_my_pieces, from); + bit_for_each64(to, tmp2, movebits) { + moves[nmoves++] = move_make(from, to); + } + if (popcount64(pos->checkers) > 1) /* double check, we stop here */ + return (pos->moves.nmoves = nmoves); + /* sliding pieces */ - bit_for_each64(from, tmp1, pos->bb[me][BISHOP]) { + bit_for_each64(from, tmp1, pos->bb[us][BISHOP]) { movebits = hyperbola_bishop_moves(occ, from) & not_my_pieces; bit_for_each64(to, tmp2, movebits) { moves[nmoves++] = move_make(from, to); } } - bit_for_each64(from, tmp1, pos->bb[me][ROOK]) { + bit_for_each64(from, tmp1, pos->bb[us][ROOK]) { // printf("rook=%d/%s\n", from, sq_to_string(from)); movebits = hyperbola_rook_moves(occ, from) & not_my_pieces; bit_for_each64(to, tmp2, movebits) { moves[nmoves++] = move_make(from, to); } } - /* TODO: remove this one */ - bit_for_each64(from, tmp1, pos->bb[me][QUEEN]) { + /* TODO: remove this one, after movegen is validated */ + bit_for_each64(from, tmp1, pos->bb[us][QUEEN]) { movebits = hyperbola_queen_moves(occ, from) & not_my_pieces; - //bitboard_print("bishop", movebits); - //movebits = hyperbola_rook_moves(occ, from); - //bitboard_print("rook", movebits); - //bitboard_print("diag", bb_diag[] movebits); - // & not_my_pieces; bit_for_each64(to, tmp2, movebits) { moves[nmoves++] = move_make(from, to); } } /* knight */ - //bitboard_print("not_my_pieces", not_my_pieces); - bit_for_each64(from, tmp1, pos->bb[me][KNIGHT]) { - //printf("Nfrom=%d=%s\n", from, sq_to_string(from)); + bit_for_each64(from, tmp1, pos->bb[us][KNIGHT]) { movebits = bb_knight_moves(not_my_pieces, from); - //printf("%lx\n", movebits); - //bitboard_print("knight_moves", movebits); - bit_for_each64(to, tmp2, movebits) { - //printf("Nto=%d=%s\n", to, sq_to_string(to)); moves[nmoves++] = move_make(from, to); } } - /* king */ - from = pos->king[me]; - movebits = bb_king_moves(not_my_pieces, from); - bit_for_each64(to, tmp2, movebits) { - moves[nmoves++] = move_make(from, to); - } - /* 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 = us == WHITE ? RANK_7bb : RANK_2bb; + bitboard_t rel_rank3 = us == WHITE ? RANK_3bb : RANK_6bb; + //printf("r7_o = %016lx\nr7_n = %016lx\nsize=%lu\n", rel_rank7, BB_REL_RANK(RANK_7, me), + // sizeof(RANK_3bb)); + //printf("r3_o = %016lx\nr3_n = %016lx\nsize=%lu\n", rel_rank3, BB_REL_RANK(RANK_3, me), + // sizeof(RANK_3bb)); + //printf("fc_o = %016lx\nfc_n = %016lx\nsize=%lu\n", FILE_Cbb, BB_REL_FILE(FILE_C, me), + // sizeof(RANK_3bb)); //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; bitboard_t enemy_avail = bb_sq[en_passant] | enemy_pieces; /* pawn: ranks 2-6 push 1 and 2 squares */ - movebits = pawn_shift_up(pos->bb[me][PAWN] & ~rel_rank7, me) & empty; + movebits = pawn_shift_up(pos->bb[us][PAWN] & ~rel_rank7, us) & empty; bit_for_each64(to, tmp1, movebits) { - from = pawn_push_up(to, enemy); /* reverse push */ + from = pawn_push_up(to, them); /* reverse push */ //printf("push %d->%d %s->%s", from, to, sq_to_string(from), sq_to_string(to)); moves[nmoves++] = move_make(from, to); } - movebits = pawn_shift_up(movebits & rel_rank3, me) & empty; + movebits = pawn_shift_up(movebits & rel_rank3, us) & empty; bit_for_each64(to, tmp1, movebits) { - from = pawn_push_up(pawn_push_up(to, enemy), enemy); + from = pawn_push_up(pawn_push_up(to, them), them); moves[nmoves++] = move_make(from, to); } /* pawn: rank 7 push */ - movebits = pawn_shift_up(pos->bb[me][PAWN] & rel_rank7, me) & empty; + movebits = pawn_shift_up(pos->bb[us][PAWN] & rel_rank7, us) & empty; bit_for_each64(to, tmp1, movebits) { - from = pawn_push_up(to, enemy); /* reverse push */ + from = pawn_push_up(to, them); /* reverse push */ moves[nmoves++] = move_make_promote(from, to, QUEEN); moves[nmoves++] = move_make_promote(from, to, ROOK); moves[nmoves++] = move_make_promote(from, to, BISHOP); @@ -141,17 +203,17 @@ int gen_all_pseudomoves(pos_t *pos) } /* pawn: ranks 2-6 captures left, including en-passant */ - from_pawns = pos->bb[me][PAWN] & ~rel_rank7; // & ~rel_filea; - movebits = pawn_shift_upleft(from_pawns, me) & enemy_avail; + from_pawns = pos->bb[us][PAWN] & ~rel_rank7; // & ~rel_filea; + movebits = pawn_shift_upleft(from_pawns, us) & enemy_avail; bit_for_each64(to, tmp1, movebits) { - from = pawn_push_upleft(to, enemy); /* reverse capture */ + from = pawn_push_upleft(to, them); /* reverse capture */ moves[nmoves++] = move_make(from, to); } /* pawn: rank 7 captures left */ - from_pawns = pos->bb[me][PAWN] & rel_rank7; // & ~rel_filea; - movebits = pawn_shift_upleft(from_pawns, me) & enemy_avail; + from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_filea; + movebits = pawn_shift_upleft(from_pawns, us) & enemy_avail; bit_for_each64(to, tmp1, movebits) { - from = pawn_push_upleft(to, enemy); /* reverse capture */ + from = pawn_push_upleft(to, them); /* reverse capture */ moves[nmoves++] = move_make_promote(from, to, QUEEN); moves[nmoves++] = move_make_promote(from, to, ROOK); moves[nmoves++] = move_make_promote(from, to, BISHOP); @@ -159,17 +221,17 @@ int gen_all_pseudomoves(pos_t *pos) } /* pawn: ranks 2-6 captures right, including en-passant */ - from_pawns = pos->bb[me][PAWN] & ~rel_rank7; // & ~rel_fileh; - movebits = pawn_shift_upright(from_pawns, me) & enemy_avail; + from_pawns = pos->bb[us][PAWN] & ~rel_rank7; // & ~rel_fileh; + movebits = pawn_shift_upright(from_pawns, us) & enemy_avail; bit_for_each64(to, tmp1, movebits) { - from = pawn_push_upright(to, enemy); + from = pawn_push_upright(to, them); moves[nmoves++] = move_make(from, to); } /* pawn: rank 7 captures right */ - from_pawns = pos->bb[me][PAWN] & rel_rank7; // & ~rel_fileh; - movebits = pawn_shift_upright(from_pawns, me) & enemy_pieces; + from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_fileh; + movebits = pawn_shift_upright(from_pawns, us) & enemy_pieces; bit_for_each64(to, tmp1, movebits) { - from = pawn_push_upright(to, enemy); /* reverse capture */ + from = pawn_push_upright(to, them); /* reverse capture */ moves[nmoves++] = move_make_promote(from, to, QUEEN); moves[nmoves++] = move_make_promote(from, to, ROOK); moves[nmoves++] = move_make_promote(from, to, BISHOP); @@ -177,32 +239,29 @@ int gen_all_pseudomoves(pos_t *pos) } /* castle - Attention ! We consider that castle flags are correct, - * only empty squares between K ans R are tested. */ - static struct { - bitboard_t ooo; - bitboard_t oo; - } castle_empty[2] = { - { B1bb | C1bb | D1bb, F1bb | G1bb }, - { B8bb | C8bb | D8bb, F8bb | G8bb } - }; - static struct { - square_t from; - square_t ooo_to; - square_t oo_to; - } king_move[2] = { - { .from=E1, .ooo_to=C1, .oo_to=G1 }, - { .from=E8, .ooo_to=C8, .oo_to=G8 }, - }; - /* so that bit0 is K castle flag, bit1 is Q castle flag */ - int castle_msk = pos->castle >> (2 * me); - - //int castle_msk = pos->castle >> (2 * color); - if (castle_msk & CASTLE_WK && !(occ & castle_empty[me].oo)) { - moves[nmoves++] = move_make(king_move[me].from, king_move[me].oo_to); - } - if (castle_msk & CASTLE_WQ && !(occ & castle_empty[me].ooo)) { - moves[nmoves++] = move_make(king_move[me].from, king_move[me].ooo_to); + if (!pos->checkers) { + bitboard_t rel_rank1 = BB_REL_RANK(RANK_1, us); + from = pos->king[us]; + square_t from_square[2] = { E1, E8 }; /* verify king is on E1/E8 */ + bug_on(CAN_CASTLE(pos->castle, us) && from != from_square[us]); + /* For castle, we check the opponent attacks on squares between from and to. + * To square attack check will be done in gen_is_legal. + */ + if (CAN_OO(pos->castle, us)) { + bitboard_t occmask = rel_rank1 & (FILE_Fbb | FILE_Gbb); + if (!(occ & occmask) && + !sq_attackers(pos, from+1, them)) { /* f1/f8 */ + moves[nmoves++] = move_make_flags(from, from + 2, M_CASTLE_K); + } + } + if (CAN_OOO(pos->castle, us)) { + bitboard_t occmask = rel_rank1 & (FILE_Bbb | FILE_Cbb | FILE_Dbb); + if (!(occ & occmask) && + !sq_attackers(pos, from-1, them)) { /* d1/d8 */ + moves[nmoves++] = move_make_flags(from, from - 2, M_CASTLE_Q); + } + } } /* TODO * DONE. pawn ranks 2-6 advance (1 push, + 2 squares for rank 2) @@ -210,7 +269,7 @@ int gen_all_pseudomoves(pos_t *pos) * DONE. pawns ranks 2-6 captures, left and right * DONE. pawns en-passant (with capture) * DONE. pawns rank 7 capture + promotion - * castle + * DONE. castle * * add function per piece, and type, for easier debug * diff --git a/src/move-gen.h b/src/move-gen.h index 6b765fd..e4ce4bb 100644 --- a/src/move-gen.h +++ b/src/move-gen.h @@ -21,10 +21,7 @@ #include "piece.h" #include "move.h" -/** - * gen_all_pseudomoves() - generate all pseudo moves - * - */ +bool pseudo_is_legal(pos_t *pos, move_t move); int gen_all_pseudomoves(pos_t *pos); #endif /* MOVEGEN_H */ diff --git a/src/move.c b/src/move.c index 6660c7b..4f69035 100644 --- a/src/move.c +++ b/src/move.c @@ -19,6 +19,7 @@ #include "move.h" #include "position.h" + /* * /\** * * move_print() - print a move @@ -114,7 +115,6 @@ void moves_print(pos_t *pos, __unused int flags) printf("\n"); } - static int _moves_cmp_bysquare(const void *p1, const void *p2) { move_t m1 = *(move_t *)p1; @@ -131,6 +131,7 @@ static int _moves_cmp_bysquare(const void *p1, const void *p2) if (t1 > t2) return 1; return 0; } + /** * move_sort_by_sq() - sort moves by from/to squares ascending * @pos: position. diff --git a/src/move.h b/src/move.h index 06b038a..68e3a73 100644 --- a/src/move.h +++ b/src/move.h @@ -37,8 +37,8 @@ #define M_FLAGS_BEG 18 #define M_HAS_FLAGS mask(M_FLAGS_BEG + 0) /* probably unused */ #define M_CAPTURE mask(M_FLAGS_BEG + 1) -#define M_ENPASSANT mask(M_FLAGS_BEG + 2) -#define M_PROMOTION mask(M_FLAGS_BEG + 3) +#define M_EPASSANT mask(M_FLAGS_BEG + 2) +#define M_PROMOTE mask(M_FLAGS_BEG + 3) #define M_CASTLE_K mask(M_FLAGS_BEG + 4) #define M_CASTLE_Q mask(M_FLAGS_BEG + 5) #define M_CHECK mask(M_FLAGS_BEG + 6) /* probably unknown/useless */ @@ -54,9 +54,14 @@ static inline move_t move_make(square_t from, square_t to) return (to << 6) | from; } +static inline move_t move_make_flags(square_t from, square_t to, int flags) +{ + return move_make(from, to) | flags; +} + static inline move_t move_make_promote(square_t from, square_t to, piece_type_t piece) { - return move_make(from, to) | M_ENPASSANT | (piece << 15); + return move_make(from, to) | M_PROMOTE | (piece << 15); } static inline square_t move_from(move_t move) diff --git a/src/position.c b/src/position.c index a328128..00da416 100644 --- a/src/position.c +++ b/src/position.c @@ -111,7 +111,8 @@ pos_t *pos_clear(pos_t *pos) pos->board[sq] = EMPTY; pos->moves.curmove = 0; pos->moves.nmoves = 0; - + pos->checkers = 0; + pos->pinners = 0; return pos; } @@ -131,24 +132,18 @@ bitboard_t pos_checkers(const pos_t *pos, const color_t color) } /** - * pos_checkers2str() - convert checkers to string. - * @pos: &position - * @str: destination string (should be at least 2*3 + 1 = 7 length) + * pos_pinners() - find all pinners on a king. + * @pos: &position + * @color: king color * - * @return: The string. + * Get a bitboard of all pinners on @color king. + * Just a wrapper over @sq_pinners(). + * + * @return: a bitboard of pinners. */ -char *pos_checkers2str(const pos_t *pos, char *str) +bitboard_t pos_pinners(const pos_t *pos, const color_t color) { - 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; + return sq_pinners(pos, pos->king[color], OPPONENT(color)); } /** @@ -164,6 +159,7 @@ char *pos_checkers2str(const pos_t *pos, char *str) * - discrepancy between bitboards per piece and ALL_PIECES per color * - discrepancy between bitboards and board * - side-to-move already checking opponent king. + * - side to move in check more than twice * * In case of errors, and @abort is true, @bug_on() is called, and program will * be terminated. @@ -171,8 +167,8 @@ char *pos_checkers2str(const pos_t *pos, char *str) * (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. + * TODO: add more checks: + * - kings attacking each other * * @Return: 0 if no error detected * the number of detected error if @abort == 0. @@ -212,8 +208,10 @@ int pos_check(const pos_t *pos, const int fatal) } /* occupied occupation is different from bitboards */ error += warn_on(count != bbcount); - /* is color to play in check ? */ + /* is opponent already in check ? */ error += warn_on(pos_checkers(pos, OPPONENT(pos->turn))); + /* is color to play in check more than twice ? */ + error += warn_on(popcount64(pos_checkers(pos, OPPONENT(pos->turn))) > 2); bug_on(fatal && error); return error; @@ -225,11 +223,12 @@ int pos_check(const pos_t *pos, const int fatal) */ void pos_print(const pos_t *pos) { - char str[92]; + char str[128]; board_print(pos->board); printf("fen %s\n", pos2fen(pos, str)); - printf("checkers %s\n", pos_checkers2str(pos, str)); + printf("checkers: %s\n", pos_checkers2str(pos, str, sizeof(str))); + printf("pinners : %s\n", pos_pinners2str(pos, str, sizeof(str))); } /** diff --git a/src/position.h b/src/position.h index 3e9add9..e8d3cac 100644 --- a/src/position.h +++ b/src/position.h @@ -38,6 +38,7 @@ typedef struct __pos_s { bitboard_t bb[2][PIECE_TYPE_MAX]; /* bb[0][PAWN], bb[1][ALL_PIECES] */ bitboard_t controlled[2]; /* unsure */ bitboard_t checkers; /* opponent checkers */ + bitboard_t pinners; /* opponent pinners */ piece_t board[BOARDSIZE]; movelist_t moves; } pos_t; @@ -50,7 +51,7 @@ typedef struct __pos_s { * * Both position board and bitboards are modified. */ -static inline void pos_set_sq(pos_t *pos, square_t square, piece_t piece) +static __always_inline void pos_set_sq(pos_t *pos, square_t square, piece_t piece) { color_t color = COLOR(piece); piece_type_t type = PIECE(piece); @@ -68,7 +69,7 @@ static inline void pos_set_sq(pos_t *pos, square_t square, piece_t piece) * * Both position board and bitboards are modified. */ -static inline void pos_clr_sq(pos_t *pos, square_t square) +static __always_inline void pos_clr_sq(pos_t *pos, square_t square) { piece_t piece = pos->board[square]; piece_type_t type = PIECE(piece); @@ -78,6 +79,58 @@ static inline void pos_clr_sq(pos_t *pos, square_t square) pos->bb[color][ALL_PIECES] &= ~mask(square); } +/** + * pos_occ() - get position occupation (all pieces) + * @pos: position + * + * @return: occupation bitboard. + */ +static __always_inline bitboard_t pos_occ(const pos_t *pos) +{ + return pos->bb[WHITE][ALL_PIECES] | pos->bb[BLACK][ALL_PIECES]; +} + +/** + * pos_between_occ() - find occupation between two squares. + * @pos: position + * @sq1: square 1 + * @sq2: square 2 + * + * @return: bitboard of @betw if between @sq1 and @sq2. + */ +static __always_inline bitboard_t pos_between_occ(const pos_t *pos, + const square_t sq1, const square_t sq2) +{ + return bb_between_excl[sq1][sq2] & pos_occ(pos); +} + +/** + * pos_between_count() - count occupied squares between two squares. + * @pos: position + * @sq1: square 1 + * @sq2: square 2 + * + * @return: bitboard of @betw if between @sq1 and @sq2. + */ +static __always_inline int pos_between_count(const pos_t *pos, + const square_t sq1, const square_t sq2) +{ + return bb_between_excl[sq1][sq2] & pos_occ(pos); +} + +/** + * pos_checkers2str() - get of string of checkers. + * @pos: position + * @str: destination string + * @len: max @str len. + * + * A wrapper over @bb_sq2str() for checkers bitmap. + * + * @return: @str. + */ +#define pos_checkers2str(pos, str, len) bb_sq2str((pos)->checkers, (str), (len)) +#define pos_pinners2str(pos, str, len) bb_sq2str((pos)->pinners, (str), (len)) + //void bitboard_print(bitboard_t bb, char *title); //void bitboard_print2(bitboard_t bb1, bitboard_t bb2, char *title); @@ -87,7 +140,9 @@ extern void pos_del(pos_t *pos); extern pos_t *pos_clear(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 bitboard_t pos_pinners(const pos_t *pos, const color_t color); +//extern char *pos_checkers2str(const pos_t *pos, char *str); +//extern char *pos_pinners2str(const pos_t *pos, char *str); extern int pos_check(const pos_t *pos, const int strict); diff --git a/test/bitboard-test.c b/test/bitboard-test.c index 0f4758d..762056d 100644 --- a/test/bitboard-test.c +++ b/test/bitboard-test.c @@ -30,29 +30,29 @@ int main(int __unused ac, __unused char**av) "sliding", "diagonal", "antidiagonal", "file", "rank", "knight", "king" ); - bitboard_print_multi(str, 7, - bb_file[i] | bb_rank[i] | - bb_diag[i] | bb_anti[i], - bb_diag[i], bb_anti[i], - bb_file[i], bb_rank[i], + bb_print_multi(str, 7, + bb_sqfile[i] | bb_sqrank[i] | + bb_sqdiag[i] | bb_sqanti[i], + bb_sqdiag[i], bb_sqanti[i], + bb_sqfile[i], bb_sqrank[i], bb_knight[i], bb_king[i]); } 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_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]); 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_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]); 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_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], diff --git a/test/common-test.h b/test/common-test.h index eac5e1e..6b8cb3a 100644 --- a/test/common-test.h +++ b/test/common-test.h @@ -14,168 +14,331 @@ #include #include "chessdefs.h" /* when below FENs are in a struct with selection per test */ +#define NOTEST 0 #define FEN 1 #define BITBOARD 2 #define MOVEGEN 4 - +#define ATTACK 8 struct fentest { uint modules; char *comment; char *fen; } fentest[] = { -//static char *fentest[] = { + /* + { ATTACK, + "checkers: ", + "" + }, + */ /* 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" }, + { ATTACK, + "checkers: a1 h1", + "1k6/8/8/8/8/8/8/r2K3r w - - 1 1" + }, + { ATTACK, + "checkers: a8 h8", + "R2k3R/8/8/8/8/8/8/1K6 b - - 1 1" + }, + { ATTACK, + "checkers: b3 g3", + "1k6/8/8/8/8/1r1K2r1/8/8 w - - 1 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" }, + { ATTACK, + "checkers: b6 g6", + "8/8/1R1k2R1/8/8/8/8/1K6 b - - 1 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" }, + { ATTACK, + "checkers: g2 g7", + "8/k5r1/8/8/6K1/8/6r1/8 w - - 1 1" + }, + { ATTACK, + "checkers: g2 g7", + "8/6R1/8/6k1/8/8/K5R1/8 b - - 1 1" + }, + { ATTACK, + "checkers: d5 e3, pinners: a1 h1 a4 h5", + "3k4/8/8/3r3b/b7/1N2n3/4B3/rN1K1R1r w - - 1 0" + }, + { ATTACK, + "checkers: d5 e3, pinners: none (2 pieces between attacker & K)", + "3k4/8/8/3r3b/b7/1N2nn2/2n1B3/rNBK1Rbr w - - 1 1" + }, + + { ATTACK, + "checkers: d4 e6 pinners: h4 a5 a8 h8", + "Rn1k1r1R/4b3/1n2N3/B7/3R3B/8/8/3K4 b - - 1 1" + }, + { ATTACK, + "checkers: d5 e3, pinners: a1 h1 a4 h5", + "3k4/8/8/3r3b/b7/1N2n3/4B3/rN1K1R1r w - - 1 0" + }, + + { MOVEGEN, + "only pawn captures", + "4k3/8/2p1p3/2PpP3/8/pp4pp/PP4PP/4K3 w - d6 0 1" + }, + + { FEN | ATTACK, + "checker: h4", + "4k3/8/8/8/7b/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" }, + { 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" + }, + // + { FEN | MOVEGEN, + "4 castle possible, only K+R", + "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1" + }, + // + { FEN | MOVEGEN, + "only kings on A1/A8, white to play", + "k7/8/8/8/8/8/8/K7 w - - 0 1" + }, + // + { FEN | MOVEGEN, + "only one move possible (Pf2xBg3)", + "k7/8/8/1p1p4/pPpPp3/P1PpPpb1/NBNP1P2/KBB1B3 w - - 0 1" + }, + // + { FEN | MOVEGEN, + "only 2 moves possible (Ph5xg6 e.p., Ph5-h6)", + "k7/8/8/1p1p2pP/pPpPp3/P1PpPp2/NBNP1P2/KBB1B3 w - g6 0 1" + }, + // + { FEN | MOVEGEN, + "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 - { FEN | MOVEGEN, "", - "rnbqk2r/pp1pbpp1/7p/2pPp3/4n3/3B1N2/PPP2PPP/RNBQ1RK1 w kq c6 0 7" }, + { 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" }, + { 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/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 + { 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, + "Illegal ep move #1", + "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1" + }, + { FEN | MOVEGEN, + "Illegal ep move #2", + "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1" + }, + { FEN | MOVEGEN, + "EP Capture Checks Opponent", + "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1" + }, + { FEN | MOVEGEN, + "Short Castling Gives Check", + "5k2/8/8/8/8/8/8/4K2R w K - 0 1" + }, + { FEN | MOVEGEN, + "Long Castling Gives Check", + "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1" + }, + { FEN | MOVEGEN, + "Castle Rights", + "r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1" + }, + { FEN | MOVEGEN, + "Castling Prevented", + "r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1" + }, + { FEN | MOVEGEN, + "Promote out of Check", + "2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1" + }, + { FEN | MOVEGEN, + "Discovered Check", + "8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1" + }, + { FEN | MOVEGEN, + "Promote to give check", + "4k3/1P6/8/8/8/8/K7/8 w - - 0 1" + }, + { FEN | MOVEGEN, + "Under Promote to give check", + "8/P1k5/K7/8/8/8/8/8 w - - 0 1" + }, + { FEN | MOVEGEN, + "Self Stalemate", + "K1k5/8/P7/8/8/8/8/8 w - - 0 1" + }, + { FEN | MOVEGEN, + "Stalemate & Checkmate", + "8/k1P5/8/1K6/8/8/8/8 w - - 0 1" + }, + { FEN | MOVEGEN, + "Stalemate & Checkmate", + "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1" + }, + + { 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" + }, { 0, NULL, NULL } }; static int cur = 0; -static char* next_fen(uint module) +static char *next_fen(uint module) { while (fentest[cur].fen && !(fentest[cur].modules & module)) cur++; diff --git a/test/fen-test.c b/test/fen-test.c index 8526f63..49696d2 100644 --- a/test/fen-test.c +++ b/test/fen-test.c @@ -26,17 +26,23 @@ int main(__unused int ac, __unused char**av) const char *fen; char revfen[128]; + setlinebuf(stdout); /* line-buffered stdout */ + bitboard_init(); while ((fen = next_fen(FEN))) { if (!(pos = fen2pos(NULL, fen))) { - printf("fen = [%s] **INVALID\n", fen); + printf("[%s] **INVALID\n", fen); } else { - pos_print_raw(pos, 1); + pos_print(pos); pos2fen(pos, revfen); - printf("fen = [%s]\nrev = [%s]", fen, revfen); - if (strcmp(fen, revfen)) - printf(" **FIXED\n"); + if (!strcmp(fen, revfen)) { + printf("[%s] OK\n", fen); + } else { + //printf("fen = [%s]\nrev = [%s]", fen, revfen); + //pos_print_raw(pos, 1); + printf("[%s] -> [%s] **FIXED\n", fen, revfen); + } pos_del(pos); } } diff --git a/test/movegen-test.c b/test/movegen-test.c index 707c167..f2d422d 100644 --- a/test/movegen-test.c +++ b/test/movegen-test.c @@ -82,7 +82,9 @@ static void send_stockfish_fen(FILE *desc, pos_t *pos, char *fen) int count, __unused mycount = 0, fishcount; size_t alloc = 0; ssize_t buflen; + pos_clear(pos); + move_t *moves = pos->moves.move; int nmoves = pos->moves.nmoves; //char nodescount[] = "Nodes searched"; @@ -187,24 +189,24 @@ int main(int __unused ac, __unused char**av) { int i = 0; FILE *outfd; - pos_t *pos, *fishpos = pos_new(); char *fen; + pos_t *pos, *fishpos = pos_new(); //bitboard_t wrong = 0x5088000040, tmp, loop; //bit_for_each64(loop, tmp, ) - + //printf("fishpos 1=%p\n", fishpos); bitboard_init(); hyperbola_init(); outfd = open_stockfish(); while ((fen = next_fen(MOVEGEN))) { //printf(">>>>> %s\n", test[i]); - printf("original fen %d: [%s]\n", i, fen); + //printf("fishpos 2=%p\n", fishpos); + printf("original fen %d: [%p][%s]\n", i, fen, fen); if (!(pos = fen2pos(NULL, fen))) { printf("wrong fen %d: [%s]\n", i, fen); continue; } pos_print(pos); - fflush(stdout); /* print movelists */ send_stockfish_fen(outfd, fishpos, fen); @@ -227,7 +229,6 @@ int main(int __unused ac, __unused char**av) // fflush(stdout); /* compare movelists */ - printf("\n"); compare_moves(fishpos, pos); //pos_print_board_raw(pos, 1); //printf("%s\n", pos2fen(pos, str));