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
This commit is contained in:
2024-03-07 10:50:19 +01:00
parent b351d198b8
commit 87e7695873
20 changed files with 913 additions and 373 deletions

View File

@@ -12,6 +12,7 @@
SHELL := /bin/bash SHELL := /bin/bash
CC := gcc CC := gcc
#CC := clang
LD := ld LD := ld
BEAR := bear BEAR := bear
TOUCH := touch TOUCH := touch
@@ -40,6 +41,7 @@ OBJ := $(addprefix $(OBJDIR)/,$(SRC_FN:.c=.o))
TSTSRC := $(wildcard $(TSTDIR)/*.c) TSTSRC := $(wildcard $(TSTDIR)/*.c)
LIB := br_$(shell uname -m) # library name LIB := br_$(shell uname -m) # library name
LIBS := $(strip -l$(LIB) -lreadline)
DEP_FN := $(SRC_FN) DEP_FN := $(SRC_FN)
DEP := $(addprefix $(DEPDIR)/,$(DEP_FN:.c=.d)) DEP := $(addprefix $(DEPDIR)/,$(DEP_FN:.c=.d))
@@ -47,9 +49,6 @@ DEP := $(addprefix $(DEPDIR)/,$(DEP_FN:.c=.d))
TARGET_FN := brchess TARGET_FN := brchess
TARGET := $(addprefix $(BINDIR)/,$(TARGET_FN)) TARGET := $(addprefix $(BINDIR)/,$(TARGET_FN))
LDFLAGS := -L$(BRLIBDIR)
LIBS := $(strip -l$(LIB) -lreadline)
ASMFILES := $(SRC:.c=.s) $(TSTSRC:.c=.s) ASMFILES := $(SRC:.c=.s) $(TSTSRC:.c=.s)
CPPFILES := $(SRC:.c=.i) $(TSTSRC:.c=.i) CPPFILES := $(SRC:.c=.i) $(TSTSRC:.c=.i)
@@ -68,6 +67,12 @@ CPPFLAGS += -DWARN_ON # brlib bug.h
#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
# 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_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
@@ -79,6 +84,7 @@ CPPFLAGS := $(strip $(CPPFLAGS))
##################################### compiler flags ##################################### compiler flags
CFLAGS := -std=gnu11 CFLAGS := -std=gnu11
#CFLAGS += -flto
CFLAGS += -O1 CFLAGS += -O1
CFLAGS += -g CFLAGS += -g
CFLAGS += -Wall CFLAGS += -Wall
@@ -92,6 +98,12 @@ CFLAGS += -Wmissing-declarations
CFLAGS := $(strip $(CFLAGS)) CFLAGS := $(strip $(CFLAGS))
##################################### linker flags
LDFLAGS := -L$(BRLIBDIR)
#LDFLAGS += -flto
LDFLAGS := $(strip $(LDFLAGS))
##################################### archiver/dependency flags ##################################### archiver/dependency flags
ARFLAGS := rcs ARFLAGS := rcs
DEPFLAGS = -MMD -MP -MF $(DEPDIR)/$*.d DEPFLAGS = -MMD -MP -MF $(DEPDIR)/$*.d
@@ -193,7 +205,7 @@ cleanobjdir: cleanobj
# "normal" ones, but do not imply to rebuild target. # "normal" ones, but do not imply to rebuild target.
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR) $(DEPDIR) $(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR) $(DEPDIR)
@echo compiling brchess module: $< "->" $@. @echo compiling brchess module: $< "->" $@.
@$(CC) -c $(ALL_CFLAGS) $< -o $@ $(CC) -c $(ALL_CFLAGS) $< -o $@
##################################### brlib libraries ##################################### brlib libraries
.PHONY: cleanbrlib cleanallbrlib brlib .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 \ BB_OBJS := fen.o position.o piece.o bitboard.o board.o hyperbola-quintessence.o \
attack.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 \
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_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)) TEST := $(addprefix $(BINDIR)/,$(TEST))

2
brlib

Submodule brlib updated: 9162db31e3...8ff163dcf5

View File

@@ -35,60 +35,110 @@
* T on @sq could attack a white T piece. * T on @sq could attack a white T piece.
* *
* @Return: a bitboard of attackers. * @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 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 sqbb = mask(sq);
bitboard_t c_pieces = pos->bb[c][ALL_PIECES]; bitboard_t occ = pos_occ(pos);
bitboard_t occ = c_pieces | pos->bb[OPPONENT(c)][ALL_PIECES];
bitboard_t to; bitboard_t to;
color_t opp = OPPONENT(c); color_t opp = OPPONENT(c);
/* pawn */ /* pawn */
to = pos->bb[c][PAWN]; to = pos->bb[c][PAWN];
attackers |= pawn_shift_upleft(sqbb, opp) & to; tmp = pawn_shift_upleft(sqbb, opp) & to;
# ifdef DEBUG_ATTACK attackers |= tmp;
bitboard_print("sq_attackers after pawn upleft", attackers); # ifdef DEBUG_ATTACK_ATTACKERS1
bb_print("att pawn upleft", tmp);
# endif # endif
attackers |= pawn_shift_upright(sqbb, opp) & to; tmp = pawn_shift_upright(sqbb, opp) & to;
# ifdef DEBUG_ATTACK attackers |= tmp;
bitboard_print("sq_attackers pawn upright", attackers); # ifdef DEBUG_ATTACK_ATTACKERS1
bb_print("att pawn upright", tmp);
# endif # endif
/* knight & king */ /* knight & king */
to = pos->bb[c][KNIGHT]; to = pos->bb[c][KNIGHT];
attackers |= bb_knight_moves(to, sq); tmp = bb_knight_moves(to, sq);
# ifdef DEBUG_ATTACK attackers |= tmp;
bitboard_print("sq_attackers after knight", attackers); # ifdef DEBUG_ATTACK_ATTACKERS1
bb_print("att knight", tmp);
# endif # endif
to = pos->bb[c][KING]; to = pos->bb[c][KING];
attackers |= bb_king_moves(to, sq); tmp = bb_king_moves(to, sq);
# ifdef DEBUG_ATTACK attackers |= tmp;
bitboard_print("sq_attackers after king", attackers); # ifdef DEBUG_ATTACK_ATTACKERS1
bb_print("att king", tmp);
# endif # 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, sq) & to; tmp = hyperbola_bishop_moves(occ, sq) & to;
# ifdef DEBUG_ATTACK attackers |= tmp;
bitboard_print("sq_attackers after bishop/queen", attackers); # ifdef DEBUG_ATTACK_ATTACKERS1
bb_print("att bishop/queen", tmp);
# endif # endif
/* rook / queen */ /* rook / queen */
to = pos->bb[c][ROOK] | pos->bb[c][QUEEN]; to = pos->bb[c][ROOK] | pos->bb[c][QUEEN];
# ifdef DEBUG_ATTACK tmp = hyperbola_rook_moves(occ, sq) & to;
bitboard_print("sq_attackers after queen", attackers); attackers |= tmp;
# endif # ifdef DEBUG_ATTACK_ATTACKERS1
attackers |= hyperbola_rook_moves(occ, sq) & to; bb_print("att rook/queen", tmp);
# ifdef DEBUG_ATTACK bb_print("ATTACKERS", attackers);
bitboard_print("sq_attackers after rook/queen", attackers); printf("attackers=%lx\n", attackers);
# endif # endif
return attackers; 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 * sq_attackers() - find all attackers on a square
* @pos: position * @pos: position

View File

@@ -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(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_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 #endif

View File

@@ -21,6 +21,13 @@
#include "board.h" #include "board.h"
#include "bitboard.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 */ /* vectors are clockwise from N */
static int knight_vector[] = { static int knight_vector[] = {
NORTH_EAST + NORTH, NORTH_EAST + EAST, NORTH_EAST + NORTH, NORTH_EAST + EAST,
@@ -33,14 +40,6 @@ static int king_vector[8] = {
SOUTH, SOUTH_WEST, WEST, NORTH_WEST 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. * bitboard_between_excl() - get bitboard of squares between two squares.
* @sq1, @sq2: The two square_t 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 : * Generate the following bitboards :
* bb_sq[64]: square to bitboard * 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_excl[64][64]: strict squares between two squares
* bb_between[64][64]: squares between two squares including second square * 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: * And the following pseudo move masks:
* bb_knight[64]: knight moves * bb_knight[64]: knight moves
@@ -134,10 +134,10 @@ void bitboard_init(void)
} }
} }
for (square_t sq = 0; sq < 64; ++sq) { for (square_t sq = 0; sq < 64; ++sq) {
bb_file[sq] = tmpbb[sq][0]; bb_sqfile[sq] = tmpbb[sq][0];
bb_rank[sq] = tmpbb[sq][1]; bb_sqrank[sq] = tmpbb[sq][1];
bb_diag[sq] = tmpbb[sq][2]; bb_sqdiag[sq] = tmpbb[sq][2];
bb_anti[sq] = tmpbb[sq][3]; bb_sqanti[sq] = tmpbb[sq][3];
} }
/* 3) knight and king moves */ /* 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 * @title: a string or NULL
* @bitboard: the bitboard * @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'; //char c = p? p: 'X';
if (title) 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 * @title: a string or NULL
* @n: number of bitboards * @n: number of bitboards
* @bb_ptr...: pointers to bitboards * @bb_ptr...: pointers to bitboards
* *
* @n is the number of bitboards to print. If @n > 10, it is reduced to 10 * @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]; bitboard_t bb[8];
va_list ap; 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 * @str: the destination string
* @bb8: the uchar to print * @bb8: the uchar to print
* *
* @return: @str, filled with ascii representation * @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; file_t f;
for (f = FILE_A; f <= FILE_H; ++f) { for (f = FILE_A; f <= FILE_H; ++f) {
@@ -269,3 +269,43 @@ char *bitboard_rank_sprint(char *str, const uchar bb8)
//printf("\n"); //printf("\n");
return str; 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;
}

View File

@@ -30,53 +30,192 @@ extern bitboard_t bb_between_excl[64][64];
/* squares between sq1 and sq2, including sq2 */ /* squares between sq1 and sq2, including sq2 */
extern bitboard_t bb_between[64][64]; extern bitboard_t bb_between[64][64];
/* bb_rank[64]: square to rank /* bb_sqrank[64]: square to rank
* bb_file[64]: square to file * bb_sqfile[64]: square to file
* bb_diag[64]: square to diagonal * bb_sqdiag[64]: square to diagonal
* bb_anti[64]: square to antidiagonal * 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 */ /* knight and king moves */
extern bitboard_t bb_knight[64], bb_king[64]; 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 { /* generated with bash:
FILE_Abb = 0x0101010101010101ULL, FILE_Bbb = 0x0202020202020202ULL, * R="ABCDEFGH"
FILE_Cbb = 0x0404040404040404ULL, FILE_Dbb = 0x0808080808080808ULL, * F="12345678"
FILE_Ebb = 0x1010101010101010ULL, FILE_Fbb = 0x2020202020202020ULL, * for i in {0..63}; do
FILE_Gbb = 0x4040404040404040ULL, FILE_Hbb = 0x8080808080808080ULL, * printf "#define %c%cbb %#018llxull\n" ${R:i/8:1} ${F:i%8:1} $((1 << i))
* done
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
*/ */
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 */ /* 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_knight_moves(bitboard_t occ, square_t sq);
extern bitboard_t bb_king_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 bb_print(const char *title, const bitboard_t bitboard);
extern void bitboard_print_multi(const char *title, const int n, ...); extern void bb_print_multi(const char *title, const int n, ...);
extern char *bitboard_rank_sprint(char *str, const uchar bb8); 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 */ #endif /* _BITBOARD_H */

View File

@@ -20,6 +20,13 @@
#include "piece.h" #include "piece.h"
#include "bitboard.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 /* a square is defined as
* rrrfff * rrrfff
*/ */

View File

@@ -18,19 +18,12 @@
#define ONE 1ull #define ONE 1ull
#define C64(const_u64) const_u64##ULL #define C64(const_u64) const_u64##ULL
#define mask(i) ( (unsigned long long) (ONE << (i)) ) #define mask(i) ( (u64) (ONE << (i)) )
//typedef ushort board;
#define BOARDSIZE (8*8) #define BOARDSIZE (8*8)
/* from human to machin e */
#define C2FILE(c) (tolower(c) - 'a')
#define C2RANK(c) (tolower(c) - '1')
/* from machine to huma n */
#define FILE2C(f) ((f) + 'a')
#define RANK2C(r) ((r) + '1')
/* relative rank */ /* 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 /* castle_t bits structure
*/ */
typedef enum { typedef enum {
@@ -38,10 +31,20 @@ typedef enum {
CASTLE_WQ = (1 << 1), /* 0x02 00000010 */ CASTLE_WQ = (1 << 1), /* 0x02 00000010 */
CASTLE_BK = (1 << 2), /* 0x04 00000100 */ CASTLE_BK = (1 << 2), /* 0x04 00000100 */
CASTLE_BQ = (1 << 3), /* 0x08 00001000 */ 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; } castle_rights_t;
#define CASTLE_W (CASTLE_WK | CASTLE_WQ) /* 00000011 W castle mask */ /* determine is oo or ooo is possible with castle flags f and color c
#define CASTLE_B (CASTLE_BK | CASTLE_BQ) /* 00001100 B castle mask */ */
#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 /* game phases
*/ */

View File

@@ -81,9 +81,9 @@ static int fen_check(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 = REL_RANK(RANK_5, pos->turn); rank_t rank5 = SQ_REL_RANK(RANK_5, pos->turn);
rank_t rank6 = REL_RANK(RANK_6, pos->turn); rank_t rank6 = SQ_REL_RANK(RANK_6, pos->turn);
rank_t rank7 = REL_RANK(RANK_7, pos->turn); rank_t rank7 = SQ_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 ||
@@ -129,11 +129,11 @@ static int fen_check(pos_t *pos)
pos->castle &= ~castle_q; pos->castle &= ~castle_q;
} }
} }
} }
if (!(error = pos_check(pos, 0))) { if (!(error = pos_check(pos, 0))) {
/* TODO: Should it really be here ? */ /* TODO: Should it really be here ? */
pos->checkers = pos_checkers(pos, pos->turn); pos->checkers = pos_checkers(pos, pos->turn);
pos->pinners = pos_pinners(pos, pos->turn);
} }
return error ? -1: warning; return error ? -1: warning;
} }
@@ -256,8 +256,10 @@ end:
if (fen_check(&tmppos) < 0) if (fen_check(&tmppos) < 0)
return NULL; return NULL;
if (!pos) if (!pos)
pos = pos_new(); pos = pos_dup(&tmppos);
*pos = tmppos; //puts("prout 1");
//pos_print_raw(&tmppos, 1);
//puts("prout 2");
# ifdef DEBUG_FEN # ifdef DEBUG_FEN
pos_print_raw(&tmppos, 1); pos_print_raw(&tmppos, 1);
# endif # endif

View File

@@ -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) 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) 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) 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) bitboard_t hyperbola_bishop_moves(bitboard_t occ, square_t sq)

View File

@@ -14,18 +14,21 @@
#include <stdio.h> #include <stdio.h>
#include "bitops.h" #include "bitops.h"
#include "bug.h"
#include "chessdefs.h"
#include "board.h" #include "board.h"
#include "bitboard.h" #include "bitboard.h"
#include "piece.h" #include "piece.h"
#include "position.h" #include "position.h"
#include "move.h" #include "move.h"
#include "hyperbola-quintessence.h" #include "hyperbola-quintessence.h"
#include "attack.h"
#include "move-gen.h" #include "move-gen.h"
/** /**
* gen_pawn_push() - generate pawn push * gen_castle() - generate c
* @pos: position * @pos: position
* *
* Generate all pseudo pawn pushes. * Generate all pseudo pawn pushes.
@@ -36,20 +39,82 @@
//} //}
/**
* 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 * gen_all_pseudomoves() - generate all pseudo moves
* @pos: position * @pos: position
* *
* Generate all moves, no check is done on validity due to castle rules, * Generate all moves, no check is done on validity due to castle rules,
* or check (pinned pieces, etc...). * or check (pinned pieces, etc...).
*
* @return: The number of moves.
*/ */
int gen_all_pseudomoves(pos_t *pos) 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 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 occ = my_pieces | enemy_pieces;
bitboard_t empty = ~occ; bitboard_t empty = ~occ;
@@ -62,78 +127,75 @@ int gen_all_pseudomoves(pos_t *pos)
int from, to; 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 */ /* 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; movebits = hyperbola_bishop_moves(occ, from) & not_my_pieces;
bit_for_each64(to, tmp2, movebits) { bit_for_each64(to, tmp2, movebits) {
moves[nmoves++] = move_make(from, to); 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)); // printf("rook=%d/%s\n", from, sq_to_string(from));
movebits = hyperbola_rook_moves(occ, from) & not_my_pieces; movebits = hyperbola_rook_moves(occ, from) & not_my_pieces;
bit_for_each64(to, tmp2, movebits) { bit_for_each64(to, tmp2, movebits) {
moves[nmoves++] = move_make(from, to); moves[nmoves++] = move_make(from, to);
} }
} }
/* TODO: remove this one */ /* TODO: remove this one, after movegen is validated */
bit_for_each64(from, tmp1, pos->bb[me][QUEEN]) { bit_for_each64(from, tmp1, pos->bb[us][QUEEN]) {
movebits = hyperbola_queen_moves(occ, from) & not_my_pieces; 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) { bit_for_each64(to, tmp2, movebits) {
moves[nmoves++] = move_make(from, to); moves[nmoves++] = move_make(from, to);
} }
} }
/* knight */ /* knight */
//bitboard_print("not_my_pieces", not_my_pieces); bit_for_each64(from, tmp1, pos->bb[us][KNIGHT]) {
bit_for_each64(from, tmp1, pos->bb[me][KNIGHT]) {
//printf("Nfrom=%d=%s\n", from, sq_to_string(from));
movebits = bb_knight_moves(not_my_pieces, from); 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) { bit_for_each64(to, tmp2, movebits) {
moves[nmoves++] = move_make(from, to); moves[nmoves++] = move_make(from, to);
} }
}
/* pawn: relative rank and files */ /* pawn: relative rank and files */
bitboard_t rel_rank7 = me == WHITE ? RANK_7bb : RANK_2bb; bitboard_t rel_rank7 = us == WHITE ? RANK_7bb : RANK_2bb;
bitboard_t rel_rank3 = me == WHITE ? RANK_3bb : RANK_6bb; 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_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;
bitboard_t enemy_avail = bb_sq[en_passant] | enemy_pieces; bitboard_t enemy_avail = bb_sq[en_passant] | enemy_pieces;
/* pawn: ranks 2-6 push 1 and 2 squares */ /* 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) { 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)); //printf("push %d->%d %s->%s", from, to, sq_to_string(from), sq_to_string(to));
moves[nmoves++] = move_make(from, 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) { 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); moves[nmoves++] = move_make(from, to);
} }
/* pawn: rank 7 push */ /* 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) { 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, QUEEN);
moves[nmoves++] = move_make_promote(from, to, ROOK); moves[nmoves++] = move_make_promote(from, to, ROOK);
moves[nmoves++] = move_make_promote(from, to, BISHOP); 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 */ /* pawn: ranks 2-6 captures left, including en-passant */
from_pawns = pos->bb[me][PAWN] & ~rel_rank7; // & ~rel_filea; from_pawns = pos->bb[us][PAWN] & ~rel_rank7; // & ~rel_filea;
movebits = pawn_shift_upleft(from_pawns, me) & enemy_avail; movebits = pawn_shift_upleft(from_pawns, us) & enemy_avail;
bit_for_each64(to, tmp1, movebits) { 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); moves[nmoves++] = move_make(from, to);
} }
/* pawn: rank 7 captures left */ /* pawn: rank 7 captures left */
from_pawns = pos->bb[me][PAWN] & rel_rank7; // & ~rel_filea; from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_filea;
movebits = pawn_shift_upleft(from_pawns, me) & enemy_avail; movebits = pawn_shift_upleft(from_pawns, us) & enemy_avail;
bit_for_each64(to, tmp1, movebits) { 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, QUEEN);
moves[nmoves++] = move_make_promote(from, to, ROOK); moves[nmoves++] = move_make_promote(from, to, ROOK);
moves[nmoves++] = move_make_promote(from, to, BISHOP); 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 */ /* pawn: ranks 2-6 captures right, including en-passant */
from_pawns = pos->bb[me][PAWN] & ~rel_rank7; // & ~rel_fileh; from_pawns = pos->bb[us][PAWN] & ~rel_rank7; // & ~rel_fileh;
movebits = pawn_shift_upright(from_pawns, me) & enemy_avail; movebits = pawn_shift_upright(from_pawns, us) & enemy_avail;
bit_for_each64(to, tmp1, movebits) { bit_for_each64(to, tmp1, movebits) {
from = pawn_push_upright(to, enemy); from = pawn_push_upright(to, them);
moves[nmoves++] = move_make(from, to); moves[nmoves++] = move_make(from, to);
} }
/* pawn: rank 7 captures right */ /* pawn: rank 7 captures right */
from_pawns = pos->bb[me][PAWN] & rel_rank7; // & ~rel_fileh; from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_fileh;
movebits = pawn_shift_upright(from_pawns, me) & enemy_pieces; movebits = pawn_shift_upright(from_pawns, us) & enemy_pieces;
bit_for_each64(to, tmp1, movebits) { 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, QUEEN);
moves[nmoves++] = move_make_promote(from, to, ROOK); moves[nmoves++] = move_make_promote(from, to, ROOK);
moves[nmoves++] = move_make_promote(from, to, BISHOP); 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, /* castle - Attention ! We consider that castle flags are correct,
* only empty squares between K ans R are tested.
*/ */
static struct { if (!pos->checkers) {
bitboard_t ooo; bitboard_t rel_rank1 = BB_REL_RANK(RANK_1, us);
bitboard_t oo; from = pos->king[us];
} castle_empty[2] = { square_t from_square[2] = { E1, E8 }; /* verify king is on E1/E8 */
{ B1bb | C1bb | D1bb, F1bb | G1bb }, bug_on(CAN_CASTLE(pos->castle, us) && from != from_square[us]);
{ B8bb | C8bb | D8bb, F8bb | G8bb } /* For castle, we check the opponent attacks on squares between from and to.
}; * To square attack check will be done in gen_is_legal.
static struct { */
square_t from; if (CAN_OO(pos->castle, us)) {
square_t ooo_to; bitboard_t occmask = rel_rank1 & (FILE_Fbb | FILE_Gbb);
square_t oo_to; if (!(occ & occmask) &&
} king_move[2] = { !sq_attackers(pos, from+1, them)) { /* f1/f8 */
{ .from=E1, .ooo_to=C1, .oo_to=G1 }, moves[nmoves++] = move_make_flags(from, from + 2, M_CASTLE_K);
{ .from=E8, .ooo_to=C8, .oo_to=G8 }, }
}; }
/* so that bit0 is K castle flag, bit1 is Q castle flag */ if (CAN_OOO(pos->castle, us)) {
int castle_msk = pos->castle >> (2 * me); bitboard_t occmask = rel_rank1 & (FILE_Bbb | FILE_Cbb | FILE_Dbb);
if (!(occ & occmask) &&
//int castle_msk = pos->castle >> (2 * color); !sq_attackers(pos, from-1, them)) { /* d1/d8 */
if (castle_msk & CASTLE_WK && !(occ & castle_empty[me].oo)) { moves[nmoves++] = move_make_flags(from, from - 2, M_CASTLE_Q);
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);
} }
/* TODO /* TODO
* DONE. pawn ranks 2-6 advance (1 push, + 2 squares for rank 2) * 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 ranks 2-6 captures, left and right
* DONE. pawns en-passant (with capture) * DONE. pawns en-passant (with capture)
* DONE. pawns rank 7 capture + promotion * DONE. pawns rank 7 capture + promotion
* castle * DONE. castle
* *
* add function per piece, and type, for easier debug * add function per piece, and type, for easier debug
* *

View File

@@ -21,10 +21,7 @@
#include "piece.h" #include "piece.h"
#include "move.h" #include "move.h"
/** bool pseudo_is_legal(pos_t *pos, move_t move);
* gen_all_pseudomoves() - generate all pseudo moves
*
*/
int gen_all_pseudomoves(pos_t *pos); int gen_all_pseudomoves(pos_t *pos);
#endif /* MOVEGEN_H */ #endif /* MOVEGEN_H */

View File

@@ -19,6 +19,7 @@
#include "move.h" #include "move.h"
#include "position.h" #include "position.h"
/* /*
* /\** * /\**
* * move_print() - print a move * * move_print() - print a move
@@ -114,7 +115,6 @@ void moves_print(pos_t *pos, __unused int flags)
printf("\n"); printf("\n");
} }
static int _moves_cmp_bysquare(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;
@@ -131,6 +131,7 @@ static int _moves_cmp_bysquare(const void *p1, const void *p2)
if (t1 > t2) return 1; if (t1 > t2) return 1;
return 0; return 0;
} }
/** /**
* move_sort_by_sq() - sort moves by from/to squares ascending * move_sort_by_sq() - sort moves by from/to squares ascending
* @pos: position. * @pos: position.

View File

@@ -37,8 +37,8 @@
#define M_FLAGS_BEG 18 #define M_FLAGS_BEG 18
#define M_HAS_FLAGS mask(M_FLAGS_BEG + 0) /* probably unused */ #define M_HAS_FLAGS mask(M_FLAGS_BEG + 0) /* probably unused */
#define M_CAPTURE mask(M_FLAGS_BEG + 1) #define M_CAPTURE mask(M_FLAGS_BEG + 1)
#define M_ENPASSANT mask(M_FLAGS_BEG + 2) #define M_EPASSANT mask(M_FLAGS_BEG + 2)
#define M_PROMOTION mask(M_FLAGS_BEG + 3) #define M_PROMOTE mask(M_FLAGS_BEG + 3)
#define M_CASTLE_K mask(M_FLAGS_BEG + 4) #define M_CASTLE_K mask(M_FLAGS_BEG + 4)
#define M_CASTLE_Q mask(M_FLAGS_BEG + 5) #define M_CASTLE_Q mask(M_FLAGS_BEG + 5)
#define M_CHECK mask(M_FLAGS_BEG + 6) /* probably unknown/useless */ #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; 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) 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) static inline square_t move_from(move_t move)

View File

@@ -111,7 +111,8 @@ pos_t *pos_clear(pos_t *pos)
pos->board[sq] = EMPTY; pos->board[sq] = EMPTY;
pos->moves.curmove = 0; pos->moves.curmove = 0;
pos->moves.nmoves = 0; pos->moves.nmoves = 0;
pos->checkers = 0;
pos->pinners = 0;
return pos; 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_pinners() - find all pinners on a king.
* @pos: &position * @pos: &position
* @str: destination string (should be at least 2*3 + 1 = 7 length) * @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; return sq_pinners(pos, pos->king[color], OPPONENT(color));
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;
} }
/** /**
@@ -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 per piece and ALL_PIECES per color
* - discrepancy between bitboards and board * - discrepancy between bitboards and board
* - side-to-move already checking opponent king. * - 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 * In case of errors, and @abort is true, @bug_on() is called, and program will
* be terminated. * 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 * (eg after fen parsing), and with @abort != 0 otherwise (as we have some data
* corruption). * corruption).
* *
* TODO: add more checks * TODO: add more checks:
* - en-prise king for side to move. * - kings attacking each other
* *
* @Return: 0 if no error detected * @Return: 0 if no error detected
* the number of detected error if @abort == 0. * 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 */ /* occupied occupation is different from bitboards */
error += warn_on(count != bbcount); 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))); 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); bug_on(fatal && error);
return error; return error;
@@ -225,11 +223,12 @@ int pos_check(const pos_t *pos, const int fatal)
*/ */
void pos_print(const pos_t *pos) void pos_print(const pos_t *pos)
{ {
char str[92]; char str[128];
board_print(pos->board); board_print(pos->board);
printf("fen %s\n", pos2fen(pos, str)); 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)));
} }
/** /**

View File

@@ -38,6 +38,7 @@ typedef struct __pos_s {
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]; /* unsure */ bitboard_t controlled[2]; /* unsure */
bitboard_t checkers; /* opponent checkers */ bitboard_t checkers; /* opponent checkers */
bitboard_t pinners; /* opponent pinners */
piece_t board[BOARDSIZE]; piece_t board[BOARDSIZE];
movelist_t moves; movelist_t moves;
} pos_t; } pos_t;
@@ -50,7 +51,7 @@ typedef struct __pos_s {
* *
* Both position board and bitboards are modified. * 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); color_t color = COLOR(piece);
piece_type_t type = PIECE(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. * 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_t piece = pos->board[square];
piece_type_t type = PIECE(piece); 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->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_print(bitboard_t bb, char *title);
//void bitboard_print2(bitboard_t bb1, bitboard_t bb2, 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 pos_t *pos_clear(pos_t *pos);
extern bitboard_t pos_checkers(const pos_t *pos, const color_t color); 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); extern int pos_check(const pos_t *pos, const int strict);

View File

@@ -30,29 +30,29 @@ int main(int __unused ac, __unused char**av)
"sliding", "diagonal", "antidiagonal", "file", "rank", "knight", "sliding", "diagonal", "antidiagonal", "file", "rank", "knight",
"king" "king"
); );
bitboard_print_multi(str, 7, bb_print_multi(str, 7,
bb_file[i] | bb_rank[i] | bb_sqfile[i] | bb_sqrank[i] |
bb_diag[i] | bb_anti[i], bb_sqdiag[i] | bb_sqanti[i],
bb_diag[i], bb_anti[i], bb_sqdiag[i], bb_sqanti[i],
bb_file[i], bb_rank[i], bb_sqfile[i], bb_sqrank[i],
bb_knight[i], bb_king[i]); bb_knight[i], bb_king[i]);
} }
sprintf(str, "between: %-22s%-22s%-22s%-22s%-22s%-22s", sprintf(str, "between: %-22s%-22s%-22s%-22s%-22s%-22s",
"a1-a8", "a1-h8", "a1-h1", "a2-a7", "a2-g7", "a2-g2"); "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][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]);
sprintf(str, "between: %-22s%-22s%-22s%-22s%-22s%-22s%-22s%-22s", 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"); "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][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]);
sprintf(str, "between: %-22s%-22s%-22s%-22s%-22s%-22s%-22s%-22s", 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"); "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][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],

View File

@@ -14,161 +14,324 @@
#include <stdio.h> #include <stdio.h>
#include "chessdefs.h" #include "chessdefs.h"
/* when below FENs are in a struct with selection per test */ /* when below FENs are in a struct with selection per test */
#define NOTEST 0
#define FEN 1 #define FEN 1
#define BITBOARD 2 #define BITBOARD 2
#define MOVEGEN 4 #define MOVEGEN 4
#define ATTACK 8
struct fentest { struct fentest {
uint modules; uint modules;
char *comment; char *comment;
char *fen; char *fen;
} fentest[] = { } fentest[] = {
//static char *fentest[] = { /*
{ ATTACK,
"checkers: ",
""
},
*/
/* tests rank movegen bug - FIXED /* tests rank movegen bug - FIXED
*/ */
//"4k3/pppppppp/8/8/8/8/PPPPPPPP/2BRK3 w - - 0 1", //"4k3/pppppppp/8/8/8/8/PPPPPPPP/2BRK3 w - - 0 1",
//"4k3/pppppppp/8/8/8/8/PPPPPPPP/1B1R1K2 w - - 0 1", //"4k3/pppppppp/8/8/8/8/PPPPPPPP/1B1R1K2 w - - 0 1",
{ MOVEGEN, "onli pawn captures", { ATTACK,
"4k3/8/2p1p3/2PpP3/8/pp4pp/PP4PP/4K3 w - d6 0 1" }, "checkers: a1 h1",
{ FEN , "king in check", "1k6/8/8/8/8/8/8/r2K3r w - - 1 1"
"4k3/8/8/8/7b/8/8/4K3 w - - 0 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, ...), { ATTACK,
* sometimes crash Stockfish "checkers: b6 g6",
*/ "8/8/1R1k2R1/8/8/8/8/1K6 b - - 1 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" },
{ 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 // First game moves
{ FEN | MOVEGEN, "initial pos", { FEN | MOVEGEN,
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" }, "initial pos",
{ FEN | MOVEGEN, "1.e4", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1" }, },
{ FEN | MOVEGEN, "1.Nh3", { FEN | MOVEGEN,
"rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1" }, "1.e4",
{ FEN | MOVEGEN, "1.e4 e5 2.Nf3 Nc6", "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
"r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w 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 // castling test
// both can castle queen only // both can castle queen only
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"r3k2r/8/3B4/8/8/3b4/8/R3K2R w KQkq - 0 1" }, "",
{ FEN | MOVEGEN, "", "r3k2r/8/3B4/8/8/3b4/8/R3K2R w KQkq - 0 1"
"r3k2r/8/3BB3/8/8/3bb3/8/R3K2R w KQkq - 0 1" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"r2bkb1r/8/8/8/8/3bb3/8/R2BKB1R w KQkq - 0 1" }, "",
// 4 castle possible, only K+R "r3k2r/8/3BB3/8/8/3bb3/8/R3K2R w KQkq - 0 1"
{ FEN | MOVEGEN, "", },
"r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1" }, { FEN | MOVEGEN,
// only kings on A1/A8, white to play "",
{ FEN | MOVEGEN, "", "r2bkb1r/8/8/8/8/3bb3/8/R2BKB1R w KQkq - 0 1"
"k7/8/8/8/8/8/8/K7 w - - 0 1" }, },
// only one move possible (Pf2xBg3) //
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"k7/8/8/1p1p4/pPpPp3/P1PpPpb1/NBNP1P2/KBB1B3 w - - 0 1" }, "4 castle possible, only K+R",
// only 2 moves possible (Ph5xg6 e.p., Ph5-h6) "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1"
{ 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,
{ FEN | MOVEGEN, "", "only kings on A1/A8, white to play",
"k4n2/4P3/8/8/8/8/4p3/K4N2 w - - 0 1" }, "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 castled, and can e.p. on c6 black can castle
// white is a pawn down // 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. // 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 // black has 33 moves: P=11 N=2+7 B=5 R=3 Q=3 K=1 + castle
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"rnbqk2r/pp1pbpp1/7p/2pPp3/4n3/3B1N2/PPP2PPP/RNBQ1RK1 w kq c6 0 7" }, "",
"rnbqk2r/pp1pbpp1/7p/2pPp3/4n3/3B1N2/PPP2PPP/RNBQ1RK1 w kq c6 0 7"
},
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"4k3/4p3/8/b7/1BR1p2p/1Q3P2/5N2/4K3 w - - 0 1" }, "",
{ FEN | MOVEGEN, "", "4k3/4p3/8/b7/1BR1p2p/1Q3P2/5N2/4K3 w - - 0 1"
"r1bq1rk1/pppp1ppp/2n2n2/4p3/2B1P3/3PPN2/PPP3PP/RN1QK2R b KQ - 1 7" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"6k1/6pp/R2p4/p1p5/8/1P1r3P/6P1/6K1 b - - 3 3" }, "",
"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: // below tests are from:
// - Rodent IV // - Rodent IV
// - https://www.chessprogramming.net/perfect-perft/ // - https://www.chessprogramming.net/perfect-perft/
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1" }, "",
{ FEN | MOVEGEN, "", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19" }, "",
{ FEN | MOVEGEN, "", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1"
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14" }, "",
{ FEN | MOVEGEN, "", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19"
"r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"1rbqk1nr/p3ppbp/2np2p1/2p5/1p2PP2/3PB1P1/PPPQ2BP/R2NK1NR b KQk - 0 1" }, "",
{ FEN | MOVEGEN, "", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14"
"r1bqk2r/pp1p1ppp/2n1pn2/2p5/1bPP4/2NBP3/PP2NPPP/R1BQK2R b KQkq - 0 1" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"rnb1kb1r/ppp2ppp/1q2p3/4P3/2P1Q3/5N2/PP1P1PPP/R1B1KB1R b KQkq - 0 1" }, "",
{ FEN | MOVEGEN, "", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14"
"r1b2rk1/pp2nppp/1b2p3/3p4/3N1P2/2P2NP1/PP3PBP/R3R1K1 b - - 0 1" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"n1q1r1k1/3b3n/p2p1bp1/P1pPp2p/2P1P3/2NBB2P/3Q1PK1/1R4N1 b - - 0 1" }, "",
{ FEN | MOVEGEN, "", "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15"
"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22" }, "",
{ FEN | MOVEGEN, "", "1rbqk1nr/p3ppbp/2np2p1/2p5/1p2PP2/3PB1P1/PPPQ2BP/R2NK1NR b KQk - 0 1"
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22" }, "",
{ FEN | MOVEGEN, "", "r1bqk2r/pp1p1ppp/2n1pn2/2p5/1bPP4/2NBP3/PP2NPPP/R1BQK2R b KQkq - 0 1"
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"2r5/8/1n6/1P1p1pkp/p2P4/R1P1PKP1/8/1R6 w - - 0 1" }, "",
{ FEN | MOVEGEN, "", "rnb1kb1r/ppp2ppp/1q2p3/4P3/2P1Q3/5N2/PP1P1PPP/R1B1KB1R b KQkq - 0 1"
"r2q1rk1/1b1nbppp/4p3/3pP3/p1pP4/PpP2N1P/1P3PP1/R1BQRNK1 b - - 0 1" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"6k1/5pp1/7p/p1p2n1P/P4N2/6P1/1P3P1K/8 w - - 0 35" }, "",
{ FEN | MOVEGEN, "", "r1b2rk1/pp2nppp/1b2p3/3p4/3N1P2/2P2NP1/PP3PBP/R3R1K1 b - - 0 1"
"r4rk1/1pp1q1pp/p2p4/3Pn3/1PP1Pp2/P7/3QB1PP/2R2RK1 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, "", { FEN | MOVEGEN,
"r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" }, "",
{ FEN | MOVEGEN, "", "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1"
"1k6/1b6/8/8/7R/8/8/4K2R b K - 0 1" }, },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1" }, // Illegal ep move #1 "",
{ FEN | MOVEGEN, "", "1k6/1b6/8/8/7R/8/8/4K2R b K - 0 1"
"8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1" }, // Illegal ep move #2 },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1" }, // EP Capture Checks Opponent "Illegal ep move #1",
{ FEN | MOVEGEN, "", "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1"
"5k2/8/8/8/8/8/8/4K2R w K - 0 1" }, // Short Castling Gives Check },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"3k4/8/8/8/8/8/8/R3K3 w Q - 0 1" }, // Long Castling Gives Check "Illegal ep move #2",
{ FEN | MOVEGEN, "", "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1"
"r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1" }, // Castle Rights },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1" }, // Castling Prevented "EP Capture Checks Opponent",
{ FEN | MOVEGEN, "", "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1"
"2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1" }, // Promote out of Check },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1" }, // Discovered Check "Short Castling Gives Check",
{ FEN | MOVEGEN, "", "5k2/8/8/8/8/8/8/4K2R w K - 0 1"
"4k3/1P6/8/8/8/8/K7/8 w - - 0 1" }, // Promote to give check },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"8/P1k5/K7/8/8/8/8/8 w - - 0 1" }, // Under Promote to give check "Long Castling Gives Check",
{ FEN | MOVEGEN, "", "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1"
"K1k5/8/P7/8/8/8/8/8 w - - 0 1" }, // Self Stalemate },
{ FEN | MOVEGEN, "", { FEN | MOVEGEN,
"8/k1P5/8/1K6/8/8/8/8 w - - 0 1" }, // Stalemate & Checkmate "Castle Rights",
{ FEN | MOVEGEN, "", "r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1"
"8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1" }, // Stalemate & Checkmate },
{ 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 } { 0, NULL, NULL }
}; };

View File

@@ -26,17 +26,23 @@ int main(__unused int ac, __unused char**av)
const char *fen; const char *fen;
char revfen[128]; char revfen[128];
setlinebuf(stdout); /* line-buffered stdout */
bitboard_init(); bitboard_init();
while ((fen = next_fen(FEN))) { while ((fen = next_fen(FEN))) {
if (!(pos = fen2pos(NULL, fen))) { if (!(pos = fen2pos(NULL, fen))) {
printf("fen = [%s] **INVALID\n", fen); printf("[%s] **INVALID\n", fen);
} else { } else {
pos_print_raw(pos, 1); pos_print(pos);
pos2fen(pos, revfen); pos2fen(pos, revfen);
printf("fen = [%s]\nrev = [%s]", fen, revfen); if (!strcmp(fen, revfen)) {
if (strcmp(fen, revfen)) printf("[%s] OK\n", fen);
printf(" **FIXED\n"); } else {
//printf("fen = [%s]\nrev = [%s]", fen, revfen);
//pos_print_raw(pos, 1);
printf("[%s] -> [%s] **FIXED\n", fen, revfen);
}
pos_del(pos); pos_del(pos);
} }
} }

View File

@@ -82,7 +82,9 @@ static void send_stockfish_fen(FILE *desc, pos_t *pos, char *fen)
int count, __unused mycount = 0, fishcount; int count, __unused mycount = 0, fishcount;
size_t alloc = 0; size_t alloc = 0;
ssize_t buflen; ssize_t buflen;
pos_clear(pos); pos_clear(pos);
move_t *moves = pos->moves.move; move_t *moves = pos->moves.move;
int nmoves = pos->moves.nmoves; int nmoves = pos->moves.nmoves;
//char nodescount[] = "Nodes searched"; //char nodescount[] = "Nodes searched";
@@ -187,24 +189,24 @@ int main(int __unused ac, __unused char**av)
{ {
int i = 0; int i = 0;
FILE *outfd; FILE *outfd;
pos_t *pos, *fishpos = pos_new();
char *fen; char *fen;
pos_t *pos, *fishpos = pos_new();
//bitboard_t wrong = 0x5088000040, tmp, loop; //bitboard_t wrong = 0x5088000040, tmp, loop;
//bit_for_each64(loop, tmp, ) //bit_for_each64(loop, tmp, )
//printf("fishpos 1=%p\n", fishpos);
bitboard_init(); bitboard_init();
hyperbola_init(); hyperbola_init();
outfd = open_stockfish(); outfd = open_stockfish();
while ((fen = next_fen(MOVEGEN))) { while ((fen = next_fen(MOVEGEN))) {
//printf(">>>>> %s\n", test[i]); //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))) { if (!(pos = fen2pos(NULL, fen))) {
printf("wrong fen %d: [%s]\n", i, fen); printf("wrong fen %d: [%s]\n", i, fen);
continue; continue;
} }
pos_print(pos); pos_print(pos);
fflush(stdout);
/* print movelists */ /* print movelists */
send_stockfish_fen(outfd, fishpos, fen); send_stockfish_fen(outfd, fishpos, fen);
@@ -227,7 +229,6 @@ int main(int __unused ac, __unused char**av)
// fflush(stdout); // fflush(stdout);
/* compare movelists */ /* compare movelists */
printf("\n");
compare_moves(fishpos, pos); 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));