5 Commits

13 changed files with 336 additions and 153 deletions

View File

@@ -205,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
@@ -231,7 +231,7 @@ cleanbindir:
$(call rmdir,$(BINDIR),binaries) $(call rmdir,$(BINDIR),binaries)
$(TARGET): libs $(OBJ) | $(BINDIR) $(TARGET): libs $(OBJ) | $(BINDIR)
@echo generating $@ executable. @echo generating $@.
$(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@
##################################### pre-processed (.i) and assembler (.s) output ##################################### pre-processed (.i) and assembler (.s) output
@@ -241,11 +241,11 @@ cleanasmcpp:
@$(call rmfiles,$(ASMFILES) $(CPPFILES),asm and pre-processed) @$(call rmfiles,$(ASMFILES) $(CPPFILES),asm and pre-processed)
%.i: %.c %.i: %.c
@echo generating $@ @echo generating $@ (cpp processed).
@$(CC) -E $(CPPFLAGS) $(CFLAGS) $< -o $@ @$(CC) -E $(CPPFLAGS) $(CFLAGS) $< -o $@
%.s: %.c %.s: %.c
@echo generating $@ @echo generating $@ (asm).
@$(CC) -S -fverbose-asm $(CPPFLAGS) $(CFLAGS) $< -o $@ @$(CC) -S -fverbose-asm $(CPPFLAGS) $(CFLAGS) $< -o $@
##################################### LSP (ccls) ##################################### LSP (ccls)

View File

@@ -25,6 +25,7 @@ bitboard_t bb_sq[64];
bitboard_t bb_sqrank[64], bb_sqfile[64], bb_sqdiag[64], bb_sqanti[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_excl[64][64];
bitboard_t bb_between[64][64]; bitboard_t bb_between[64][64];
bitboard_t bb_line[64][64];
bitboard_t bb_knight[64], bb_king[64]; bitboard_t bb_knight[64], bb_king[64];
@@ -59,7 +60,7 @@ bitboard_t bitboard_between_excl(square_t sq1, square_t sq2)
btwn_bits = (m1 << sq1) ^ (m1 << sq2); /* includes sq1 and sq2 */ btwn_bits = (m1 << sq1) ^ (m1 << sq2); /* includes sq1 and sq2 */
rank_diff = ((sq2 | 7) - sq1) >> 3, /* signed */ rank_diff = ((sq2 | 7) - sq1) >> 3, /* signed */
file_diff = (sq2 & 7) - (sq1 & 7); /* signed */ file_diff = (sq2 & 7) - (sq1 & 7); /* signed */
anti_diff = rank_diff + file_diff; anti_diff = rank_diff + file_diff;
rank_diff = rank_diff & 15; rank_diff = rank_diff & 15;
@@ -139,6 +140,24 @@ void bitboard_init(void)
bb_sqdiag[sq] = tmpbb[sq][2]; bb_sqdiag[sq] = tmpbb[sq][2];
bb_sqanti[sq] = tmpbb[sq][3]; bb_sqanti[sq] = tmpbb[sq][3];
} }
for (square_t sq1 = 0; sq1 < 64; ++sq1) {
for (square_t sq2 = 0; sq2 < 64; ++sq2) {
if (sq1 != sq2) {
if (bb_sqfile[sq1] == bb_sqfile[sq2])
bb_line[sq1][sq2] = bb_sqfile[sq1];
else if (bb_sqrank[sq1] == bb_sqrank[sq2])
bb_line[sq1][sq2] = bb_sqrank[sq1];
else if (bb_sqdiag[sq1] == bb_sqdiag[sq2])
bb_line[sq1][sq2] = bb_sqdiag[sq1];
else if (bb_sqanti[sq1] == bb_sqanti[sq2])
bb_line[sq1][sq2] = bb_sqanti[sq1];
//if (bb_line[sq1][sq2]) {
// printf("bb_line[%d][%d] = %16lx\n", sq1, sq2, bb_line[sq1][sq2]);
//}
}
}
}
/* 3) knight and king moves */ /* 3) knight and king moves */
for (square_t sq = A1; sq <= H8; ++sq) { for (square_t sq = A1; sq <= H8; ++sq) {

View File

@@ -21,8 +21,6 @@
#include "board.h" #include "board.h"
#include "piece.h" #include "piece.h"
//typedef u64 bitboard_t;
/* mapping square -> bitboard */ /* mapping square -> bitboard */
extern bitboard_t bb_sq[64]; extern bitboard_t bb_sq[64];
/* squares between sq1 and sq2, exclusing both */ /* squares between sq1 and sq2, exclusing both */
@@ -30,12 +28,17 @@ 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_sqrank[64]: square to rank /**
* bb_sqrank[64]: square to rank
* bb_sqfile[64]: square to file * bb_sqfile[64]: square to file
* bb_sqdiag[64]: square to diagonal * bb_sqdiag[64]: square to diagonal
* bb_sqanti[64]: square to antidiagonal * bb_sqanti[64]: square to antidiagonal
*/ */
extern bitboard_t bb_sqrank[64], bb_sqfile[64], bb_sqdiag[64], bb_sqanti[64]; extern bitboard_t bb_sqrank[64], bb_sqfile[64], bb_sqdiag[64], bb_sqanti[64];
/* line (rank, file, diagonal or anti-diagonal) between two squares */
extern bitboard_t bb_line[64][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];
@@ -192,17 +195,24 @@ static __always_inline bitboard_t bb_file(int file)
/** /**
* bb_sq_aligned() - check if two squares are aligned (same file or rank). * bb_sq_aligned() - check if two squares are aligned (same file or rank).
* @sq1: square 1 * @sq1, @sq2: the two squares.
* @sq2: square 2
* *
* @sq2 is included in return value, to be non zero if the two squares * @return: true if @sq1 and @sq2 are on same line, false otherwise.
* 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) static __always_inline bool bb_sq_aligned(square_t sq1, square_t sq2)
{ {
return bb_between[sq1][sq2]; return bb_line[sq1][sq2];
}
/**
* bb_sq_aligned3() - check if 3 squares are aligned (same file or rank).
* @sq1, @sq2, @sq3: the three squares.
*
* @return: true if @sq1, @sq2, and @sq3 are aligned, false otherwise.
*/
static __always_inline bool bb_sq_aligned3(square_t sq1, square_t sq2, square_t sq3)
{
return bb_line[sq1][sq2] & mask(sq3);
} }
/** /**

View File

@@ -59,14 +59,28 @@ static __always_inline rank_t sq_rank(square_t square)
#define sq_ok(sq) ((sq) >= A1 && (sq) <= H8) #define sq_ok(sq) ((sq) >= A1 && (sq) <= H8)
#define sq_coord_ok(c) ((c) >= 0 && (c) < 8) #define sq_coord_ok(c) ((c) >= 0 && (c) < 8)
/* Chebyshev distance: max( |r2 - r1|, |f2 - f1| ) /**
* sq_dist() - Chebyshev (king) distance between two squares (macro).
* @sq1, @sq2: The two squares.
*
* See: https://www.chessprogramming.org/Distance * See: https://www.chessprogramming.org/Distance
* Distance is max( |r2 - r1|, |f2 - f1| )
*
* @Return: the Chebyshev distance.
*/ */
#define sq_dist(sq1, sq2) (max(abs(sq_file(sq2) - sq_file(sq1)), \ #define sq_dist(sq1, sq2) (max(abs(sq_file(sq2) - sq_file(sq1)), \
abs(sq_rank(sq2) - sq_rank(sq1)))) abs(sq_rank(sq2) - sq_rank(sq1))))
/* Manhattan distance: |r2 - r1| + |f2 - f1|
/**
* sq_taxi() - Manhattan (taxi) distance between two squares (macro).
* @sq1, @sq2: The two squares.
*
* See: https://www.chessprogramming.org/Distance
* Distance is |r2 - r1| + |f2 - f1|.
*
* @Return: the Manhattan distance.
*/ */
#define sq_manh(sq1, sq2) (abs(sq_file(sq2) - sq_file(sq1)) + \ #define sq_taxi(sq1, sq2) (abs(sq_file(sq2) - sq_file(sq1)) + \
abs(sq_rank(sq2) - sq_rank(sq1))) abs(sq_rank(sq2) - sq_rank(sq1)))
extern const char *sq_to_string(const square_t sq); extern const char *sq_to_string(const square_t sq);

View File

@@ -86,15 +86,16 @@ void hyperbola_init()
} }
/** /**
* hyperbola_rank_moves() - generate rank moves for a sliding piece. * hyperbola_rank_moves() - get rank moves for a sliding piece.
* @pieces: occupation bitboard * @pieces: occupation bitboard
* @sq: piece square * @sq: piece square
* *
* Rank attacks are not handled by HQ, so we do it with a * Rank attacks are not handled by HQ, so this function uses a pre-calculated
* rank attacks table (@bb_rank_attacks).
* *
* @Return: The moves mask for piece * @Return: bitboard of @piece available pseudo-moves.
*/ */
static bitboard_t hyperbola_rank_moves(bitboard_t occ, square_t sq) bitboard_t hyperbola_rank_moves(bitboard_t occ, square_t sq)
{ {
u32 rank = sq & SQ_RANKMASK; u32 rank = sq & SQ_RANKMASK;
u32 file = sq & SQ_FILEMASK; u32 file = sq & SQ_FILEMASK;
@@ -109,16 +110,19 @@ static bitboard_t hyperbola_rank_moves(bitboard_t occ, square_t sq)
} }
/** /**
* hyperbola_moves() - generate hyperbola moves mask for a given sliding piece * hyperbola_moves() - get hyperbola pseudo-moves for a sliding piece
* @pieces: occupation bitboard * @pieces: occupation bitboard
* @sq: piece square * @sq: piece square
* @mask: mask considered * @mask: the appropriate mask (pre-calculated)
* *
* See https://www.chessprogramming.org/Hyperbola_Quintessence * This function can be used for files, diagonal, and anti-diagonal attacks.
* @mask is the corresponding pre-calculated table (@bb_sqfile, @bb_sqdiag,
* or @bb_sqanti).
* See https://www.chessprogramming.org/Hyperbola_Quintessence for details.
* *
* @Return: The moves mask for piece * @Return: bitboard of piece available pseudo-moves.
*/ */
static bitboard_t hyperbola_moves(const bitboard_t pieces, const square_t sq, bitboard_t hyperbola_moves(const bitboard_t pieces, const square_t sq,
const bitboard_t mask) const bitboard_t mask)
{ {
bitboard_t o = pieces & mask; bitboard_t o = pieces & mask;
@@ -130,32 +134,77 @@ static bitboard_t hyperbola_moves(const bitboard_t pieces, const square_t sq,
& mask; & mask;
} }
static bitboard_t hyperbola_file_moves(bitboard_t occ, square_t sq) /**
* hyperbola_file_moves() - get file pseudo-moves for a sliding piece.
* @pieces: occupation bitboard
* @sq: piece square
*
* @Return: bitboard of piece available pseudo-moves on its file.
*/
bitboard_t hyperbola_file_moves(const bitboard_t occ, const square_t sq)
{ {
return hyperbola_moves(occ, sq, bb_sqfile[sq]); return hyperbola_moves(occ, sq, bb_sqfile[sq]);
} }
static bitboard_t hyperbola_diag_moves(bitboard_t occ, square_t sq) /**
* hyperbola_diag_moves() - get diagonal pseudo-moves for a sliding piece.
* @pieces: occupation bitboard
* @sq: piece square
*
* @Return: bitboard of piece available pseudo-moves on its diagonal.
*/
bitboard_t hyperbola_diag_moves(const bitboard_t occ, const square_t sq)
{ {
return hyperbola_moves(occ, sq, bb_sqdiag[sq]); return hyperbola_moves(occ, sq, bb_sqdiag[sq]);
} }
static bitboard_t hyperbola_anti_moves(bitboard_t occ, square_t sq) /**
* hyperbola_anti_moves() - get anti-diagonal pseudo-moves for a sliding piece.
* @pieces: occupation bitboard
* @sq: piece square
*
* @Return: bitboard of piece available pseudo-moves on its anti-diagonal.
*/
bitboard_t hyperbola_anti_moves(const bitboard_t occ, const square_t sq)
{ {
return hyperbola_moves(occ, sq, bb_sqanti[sq]); return hyperbola_moves(occ, sq, bb_sqanti[sq]);
} }
bitboard_t hyperbola_bishop_moves(bitboard_t occ, square_t sq) /**
* hyperbola_bishop_moves() - get bitboard of bishop pseudo-moves
* @occ: occupation bitboard
* @sq: bishop square
*
* @Return: bitboard of bishop available pseudo-moves.
*/
bitboard_t hyperbola_bishop_moves(const bitboard_t occ, const square_t sq)
{ {
return hyperbola_diag_moves(occ, sq) | hyperbola_anti_moves(occ, sq); return hyperbola_diag_moves(occ, sq) | hyperbola_anti_moves(occ, sq);
} }
bitboard_t hyperbola_rook_moves(bitboard_t occ, square_t sq) /**
* hyperbola_rook_moves() - get bitboard of rook pseudo-moves
* @occ: occupation bitboard
* @sq: rook square
*
* @Return: bitboard of rook available pseudo-moves.
*/
bitboard_t hyperbola_rook_moves(const bitboard_t occ, const square_t sq)
{ {
return hyperbola_file_moves(occ, sq) | hyperbola_rank_moves(occ, sq); return hyperbola_file_moves(occ, sq) | hyperbola_rank_moves(occ, sq);
} }
bitboard_t hyperbola_queen_moves(bitboard_t occ, square_t sq) /**
* hyperbola_queen_moves() - get bitboard of queen pseudo-moves
* @occ: occupation bitboard
* @sq: queen square
*
* This function is a wrapper over @hyperbola_bishop_moves() and
* @hyperbola_rook_moves().
*
* @Return: bitboard of queen available pseudo-moves.
*/
bitboard_t hyperbola_queen_moves(const bitboard_t occ, const square_t sq)
{ {
return hyperbola_bishop_moves(occ, sq) | hyperbola_rook_moves(occ, sq); return hyperbola_bishop_moves(occ, sq) | hyperbola_rook_moves(occ, sq);
} }

View File

@@ -17,9 +17,17 @@
#include "board.h" #include "board.h"
#include "bitboard.h" #include "bitboard.h"
void hyperbola_init(void); extern void hyperbola_init(void);
extern bitboard_t hyperbola_bishop_moves(bitboard_t occ, square_t sq);
extern bitboard_t hyperbola_rook_moves(bitboard_t occ, square_t sq); extern bitboard_t hyperbola_rank_moves(const bitboard_t occ, const square_t sq);
extern bitboard_t hyperbola_queen_moves(bitboard_t occ, square_t sq); extern bitboard_t hyperbola_moves(const bitboard_t pieces, const square_t sq,
const bitboard_t mask);
extern bitboard_t hyperbola_file_moves(const bitboard_t occ, const square_t sq);
extern bitboard_t hyperbola_diag_moves(const bitboard_t occ, const square_t sq);
extern bitboard_t hyperbola_anti_moves(const bitboard_t occ, const square_t sq);
extern bitboard_t hyperbola_bishop_moves(const bitboard_t occ, const square_t sq);
extern bitboard_t hyperbola_rook_moves(const bitboard_t occ, const square_t sq);
extern bitboard_t hyperbola_queen_moves(const bitboard_t occ, const square_t sq);
#endif /* _HYPERBOLA_QUINTESSENCE_H */ #endif /* _HYPERBOLA_QUINTESSENCE_H */

View File

@@ -27,18 +27,6 @@
#include "move-gen.h" #include "move-gen.h"
/**
* 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. * pseudo_is_legal() - check if a move is legal.
* @pos: position * @pos: position
@@ -47,10 +35,11 @@
* We check all possible invalid moves: * We check all possible invalid moves:
* (1) King: * (1) King:
* - K moves to a controlled square * - K moves to a controlled square
* - Castling: * (1) Castling:
* - K passes a controlled square - already done in pseudomove gen * - K passes a controlled square - already done in pseudomove gen
* *
* (2) En-passant: *
* (3) En-passant:
* - pinned taking pawn, done in (3) * - pinned taking pawn, done in (3)
* - taking and taken pawn on same rank than king, discovered check on rank * - taking and taken pawn on same rank than king, discovered check on rank
* *
@@ -103,10 +92,21 @@ bool pseudo_is_legal(pos_t *pos, move_t move)
* 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 @pos pseudo moves for player-to-move.
* or check (pinned pieces, etc...). * The @pos->moves table is filled with the moves.
* *
* @return: The number of moves. * Only a few validity checks are done here (i.e. moves are not generated):
* - castling, if king is in check
* - castling, if king passes an enemy-controlled square (not final square).
* When immediately known, a few move flags are also applied in these cases:
* - castling: M_CASTLE_{K,Q}
* - pawn capture (incl. en-passant): M_CAPTURE
* - promotion: M_PROMOTION
* - promotion and capture
*
* TODO: move code to specific functions (especially castling, pawn push/capture)
*
* @Return: The total number of moves.
*/ */
int gen_all_pseudomoves(pos_t *pos) int gen_all_pseudomoves(pos_t *pos)
{ {
@@ -127,7 +127,7 @@ int gen_all_pseudomoves(pos_t *pos)
int from, to; int from, to;
/* king */ /* king - MUST BE FIRST ! */
from = pos->king[us]; from = pos->king[us];
movebits = bb_king_moves(not_my_pieces, from); movebits = bb_king_moves(not_my_pieces, from);
bit_for_each64(to, tmp2, movebits) { bit_for_each64(to, tmp2, movebits) {
@@ -187,11 +187,28 @@ int gen_all_pseudomoves(pos_t *pos)
//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);
} }
/* possible second push */
movebits = pawn_shift_up(movebits & rel_rank3, us) & 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, them), them); from = pawn_push_up(pawn_push_up(to, them), them);
moves[nmoves++] = move_make(from, to); moves[nmoves++] = move_make(from, to);
} }
/* pawn: ranks 2-6 captures left, including en-passant */
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, them); /* reverse capture */
moves[nmoves++] = move_make_capture(from, to);
}
/* pawn: ranks 2-6 captures right, including en-passant */
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, them);
moves[nmoves++] = move_make_capture(from, to);
}
/* pawn: rank 7 push */ /* pawn: rank 7 push */
movebits = pawn_shift_up(pos->bb[us][PAWN] & rel_rank7, us) & 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) {
@@ -201,44 +218,28 @@ int gen_all_pseudomoves(pos_t *pos)
moves[nmoves++] = move_make_promote(from, to, BISHOP); moves[nmoves++] = move_make_promote(from, to, BISHOP);
moves[nmoves++] = move_make_promote(from, to, KNIGHT); moves[nmoves++] = move_make_promote(from, to, KNIGHT);
} }
/* pawn promotion: rank 7 captures left */
/* pawn: ranks 2-6 captures left, including en-passant */
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, them); /* reverse capture */
moves[nmoves++] = move_make(from, to);
}
/* pawn: rank 7 captures left */
from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_filea; from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_filea;
movebits = pawn_shift_upleft(from_pawns, us) & 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, them); /* reverse capture */ from = pawn_push_upleft(to, them); /* reverse capture */
moves[nmoves++] = move_make_promote(from, to, QUEEN); moves[nmoves++] = move_make_promote_capture(from, to, QUEEN);
moves[nmoves++] = move_make_promote(from, to, ROOK); moves[nmoves++] = move_make_promote_capture(from, to, ROOK);
moves[nmoves++] = move_make_promote(from, to, BISHOP); moves[nmoves++] = move_make_promote_capture(from, to, BISHOP);
moves[nmoves++] = move_make_promote(from, to, KNIGHT); moves[nmoves++] = move_make_promote_capture(from, to, KNIGHT);
}
/* pawn: ranks 2-6 captures right, including en-passant */
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, them);
moves[nmoves++] = move_make(from, to);
} }
/* pawn: rank 7 captures right */ /* pawn: rank 7 captures right */
from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_fileh; from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_fileh;
movebits = pawn_shift_upright(from_pawns, us) & 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, them); /* reverse capture */ from = pawn_push_upright(to, them); /* reverse capture */
moves[nmoves++] = move_make_promote(from, to, QUEEN); moves[nmoves++] = move_make_promote_capture(from, to, QUEEN);
moves[nmoves++] = move_make_promote(from, to, ROOK); moves[nmoves++] = move_make_promote_capture(from, to, ROOK);
moves[nmoves++] = move_make_promote(from, to, BISHOP); moves[nmoves++] = move_make_promote_capture(from, to, BISHOP);
moves[nmoves++] = move_make_promote(from, to, KNIGHT); moves[nmoves++] = move_make_promote_capture(from, to, KNIGHT);
} }
/* castle - Attention ! We consider that castle flags are correct, /* castle - Attention ! Castling flags are assumed correct
*/ */
if (!pos->checkers) { if (!pos->checkers) {
bitboard_t rel_rank1 = BB_REL_RANK(RANK_1, us); bitboard_t rel_rank1 = BB_REL_RANK(RANK_1, us);

View File

@@ -19,50 +19,52 @@
#include "board.h" #include "board.h"
/* move structure: /* move structure:
* 3 2 2 1 1 1 1 1 1 * 3 2 2 1 1 1 1 1 1
* 1 5 4 8 7 5 4 2 1 6 5 0 * 1 5 3 8 7 5 4 2 1 6 5 0
* UUUUUUU FFFFFFF ppp ccc tttttt ffffff * UUUUUUUU FFFFFF ppp ccc tttttt ffffff
* *
* bits range type mask get desc * bits len range type mask get desc
* ffffff 0-5 square_t 3f &63 from * ffffff 6 0-5 square_t 3f &63 from
* tttttt 6-11 square_t fc0 (>>6)&63 to * tttttt 6 6-11 square_t fc0 (>>6)&63 to
* ccc 12-14 piece_type_t 7000 (>>12)&7 captured * ccc 3 12-14 piece_type_t 7000 (>>12)&7 captured
* ppp 15-17 piece_type_t 38000 (>>15)&7 promoted * ppp 3 15-17 piece_type_t 38000 (>>15)&7 promoted
* FFFFFFF 18-24 N/A 1fc0000 N/A flags * FFFFFF 6 18-23 move_flags_t fc0000 N/A flags
* UUUUUUU 25-31 unused fe000000 N/A future usage ? * UUUUUUUU 8 24-31 unused fe000000 N/A future usage ?
*/ */
#define move_t u32 typedef u32 move_t;
/* move flags */ typedef enum {
#define M_FLAGS_BEG 18 M_START = 18,
#define M_HAS_FLAGS mask(M_FLAGS_BEG + 0) /* probably unused */ M_CAPTURE = (1 << (M_START + 0)),
#define M_CAPTURE mask(M_FLAGS_BEG + 1) M_ENPASSANT = (1 << (M_START + 1)),
#define M_EPASSANT mask(M_FLAGS_BEG + 2) M_PROMOTION = (1 << (M_START + 2)),
#define M_PROMOTE mask(M_FLAGS_BEG + 3) M_CASTLE_K = (1 << (M_START + 3)), /* maybe only one ? */
#define M_CASTLE_K mask(M_FLAGS_BEG + 4) M_CASTLE_Q = (1 << (M_START + 4)), /* maybe only one ? */
#define M_CASTLE_Q mask(M_FLAGS_BEG + 5) M_CHECK = (1 << (M_START + 5)) /* maybe unknown/useless ? */
#define M_CHECK mask(M_FLAGS_BEG + 6) /* probably unknown/useless */ } move_type_t;
#define M_FLAGS (M_CAPTURE | M_ENPASSANT | M_PROMOTION | \
M_CASTLE_K | M_CASTLE_Q | M_CHECK)
#define M_NORMAL (~M_FLAGS)
#define MOVES_MAX 256 #define MOVES_MAX 256
static inline move_t move_make(square_t from, square_t to) typedef struct __movelist_s {
{ move_t move[MOVES_MAX];
return (to << 6) | from; int nmoves; /* total moves (fill) */
} int curmove; /* current move (use) */
} movelist_t;
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) /* move flags */
{ //#define M_FLAGS_BEG 18
return move_make(from, to) | M_PROMOTE | (piece << 15); //#define M_HAS_FLAGS mask(M_FLAGS_BEG + 0) /* probably useless */
} //#define M_CAPTURE mask(M_FLAGS_BEG + 0)
//#define M_EN_PASSANT mask(M_FLAGS_BEG + 1)
//#define M_PROMOTION mask(M_FLAGS_BEG + 2)
//#define M_CASTLE_K mask(M_FLAGS_BEG + 3) /* maybe only one ? */
//#define M_CASTLE_Q mask(M_FLAGS_BEG + 4) /* maybe only one ? */
//#define M_CHECK mask(M_FLAGS_BEG + 5) /* probably unknown/useless */
//#define M_FLAGS (M_CAPTURE | M_ENPASSANT | M_PROMOTION |
// M_CASTLE_K | M_CASTLE_Q | M_CHECK)
//#define M_NORMAL (~M_FLAGS)
static inline square_t move_from(move_t move) static inline square_t move_from(move_t move)
{ {
@@ -73,6 +75,43 @@ static inline square_t move_to(move_t move)
return (move >> 6) & 077; return (move >> 6) & 077;
} }
static inline piece_t move_promoted(move_t move)
{
return (move >> 15) & 07;
}
static inline piece_t move_captured(move_t move)
{
return (move >> 12) & 07;
}
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, move_type_t flags)
{
return move_make(from, to) | flags;
}
static inline move_t move_make_capture(square_t from, square_t to)
{
return move_make_flags(from, to, M_CAPTURE);
}
static inline move_t move_make_promote(square_t from, square_t to,
piece_type_t promoted)
{
return move_make_flags(from, to, M_PROMOTION) | (promoted << 15);
}
static inline move_t move_make_promote_capture(square_t from, square_t to,
piece_type_t promoted)
{
return move_make_flags(from, to, M_CAPTURE | M_PROMOTION) | (promoted << 15);
}
/* moves_print flags /* moves_print flags
*/ */
#define M_PR_CAPT 0x01 #define M_PR_CAPT 0x01
@@ -83,12 +122,6 @@ static inline square_t move_to(move_t move)
#define M_PR_SEPARATE 0x40 /* separate captures */ #define M_PR_SEPARATE 0x40 /* separate captures */
#define M_PR_LONG 0x80 #define M_PR_LONG 0x80
typedef struct __movelist_s {
move_t move[MOVES_MAX];
int nmoves; /* total moves (fill) */
int curmove; /* current move (use) */
} movelist_t;
//pool_t *moves_pool_init(); //pool_t *moves_pool_init();
//void moves_pool_stats(); //void moves_pool_stats();

View File

@@ -147,41 +147,41 @@ bitboard_t pos_pinners(const pos_t *pos, const color_t color)
} }
/** /**
* pos_check() - extensive position consistenci check. * pos_check() - extensive position consistency check.
* @pos: &position * @pos: &position
* @strict: if not zero, call bug_on() on any error. * @strict: if true, call bug_on() on any error.
* *
* Check (hopefully) if position is valid: * Perform some validity check on position @pos:
* - pawns on first or 8th rank * - pawns on 1st or 8th rank
* - number of pawns per color > 8 * - number of pawns > 8 (per color)
* - total number of pieces per color > 16 or zero * - total number of pieces > 16 or zero (per color)
* - number of kings per color != 1 * - number of kings != 1 (per color)
* - discrepancy between bitboards per piece and ALL_PIECES per color * - discrepancy between board and king (per color)
* - discrepancy between bitboards and board * - discrepancy between piece bitboards and ALL_PIECES bitboards (per color)
* - side-to-move already checking opponent king. * - discrepancy between bitboards and board (per color)
* - side to move in check more than twice * - side-to-move already checking opponent king
* - side-to-move in check more than twice
* - kings distance is 1
* *
* In case of errors, and @abort is true, @bug_on() is called, and program will * In case of errors, and @strict is true, @bug_on() is called, and program will
* be terminated. * be terminated.
* This function should be called with @abort == 0 during initialization phase * This function should be called with @strict == false during initialization phase
* (eg after fen parsing), and with @abort != 0 otherwise (as we have some data * (eg after fen parsing), and with @strict == true otherwise (as we have some data
* corruption). * corruption).
* *
* TODO: add more checks: * TODO: add more checks:
* - kings attacking each other * - kings attacking each other
* *
* @Return: 0 if no error detected * @Return: Number of detected error (only if @strict is false).
* the number of detected error if @abort == 0.
* this function does not return if @abort != 0 and errors are found.
*/ */
int pos_check(const pos_t *pos, const int fatal) int pos_check(const pos_t *pos, const bool strict)
{ {
int n, count = 0, bbcount = 0, error = 0; int n, count = 0, bbcount = 0, error = 0;
bitboard_t tmp;
/* pawns on 1st ot 8th rank */ /* pawns on 1st ot 8th rank */
n = popcount64((pos->bb[WHITE][PAWN] | pos->bb[BLACK][PAWN]) & tmp = (pos->bb[WHITE][PAWN] | pos->bb[BLACK][PAWN]) & (RANK_1bb | RANK_8bb);
(RANK_1bb | RANK_8bb)); error += warn_on(tmp);
error += warn_on(n != 0);
for (color_t color = WHITE; color <= BLACK; ++color) { for (color_t color = WHITE; color <= BLACK; ++color) {
/* pawn count */ /* pawn count */
@@ -190,6 +190,8 @@ int pos_check(const pos_t *pos, const int fatal)
/* king count */ /* king count */
n = popcount64(pos->bb[color][KING]); n = popcount64(pos->bb[color][KING]);
error += warn_on(n != 1); error += warn_on(n != 1);
/* king mismatch with board */
error += warn_on(PIECE(pos->board[pos->king[color]]) != KING);
/* pieces count */ /* pieces count */
n = popcount64(pos->bb[color][ALL_PIECES]); n = popcount64(pos->bb[color][ALL_PIECES]);
error += warn_on(n == 0 || n > 16); error += warn_on(n == 0 || n > 16);
@@ -212,8 +214,10 @@ int pos_check(const pos_t *pos, const int fatal)
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 ? */ /* is color to play in check more than twice ? */
error += warn_on(popcount64(pos_checkers(pos, OPPONENT(pos->turn))) > 2); error += warn_on(popcount64(pos_checkers(pos, OPPONENT(pos->turn))) > 2);
/* kings distance is less than 2 */
error += warn_on(sq_dist(pos->king[WHITE], pos->king[BLACK]) < 2);
bug_on(fatal && error); bug_on(strict && error);
return error; return error;
} }

View File

@@ -144,7 +144,7 @@ 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_checkers2str(const pos_t *pos, char *str);
//extern char *pos_pinners2str(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 bool strict);
extern void pos_print(const pos_t *pos); extern void pos_print(const pos_t *pos);
extern void pos_print_mask(const pos_t *pos, const bitboard_t mask); extern void pos_print_mask(const pos_t *pos, const bitboard_t mask);

50
test/attack-test.c Normal file
View File

@@ -0,0 +1,50 @@
/* attack-test.c - basic square attack tests.
*
* Copyright (C) 2024 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "chessdefs.h"
#include "fen.h"
#include "position.h"
#include "move-gen.h"
#include "attack.h"
#include "common-test.h"
int main(int __unused ac, __unused char**av)
{
int i = 0;
char *fen;
pos_t *pos;//, *fishpos = pos_new();
setlinebuf(stdout); /* line-buffered stdout */
bitboard_init();
hyperbola_init();
while ((fen = next_fen(ATTACK))) {
//printf(">>>>> %s\n", test[i]);
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);
pos_del(pos);
i++;
}
return 0;
}

View File

@@ -61,10 +61,6 @@ struct fentest {
"checkers: g2 g7", "checkers: g2 g7",
"8/6R1/8/6k1/8/8/K5R1/8 b - - 1 1" "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, { ATTACK,
"checkers: d5 e3, pinners: none (2 pieces between attacker & K)", "checkers: d5 e3, pinners: none (2 pieces between attacker & K)",
"3k4/8/8/3r3b/b7/1N2nn2/2n1B3/rNBK1Rbr w - - 1 1" "3k4/8/8/3r3b/b7/1N2nn2/2n1B3/rNBK1Rbr w - - 1 1"

View File

@@ -207,7 +207,6 @@ int main(int __unused ac, __unused char**av)
continue; continue;
} }
pos_print(pos); pos_print(pos);
/* print movelists */ /* print movelists */
send_stockfish_fen(outfd, fishpos, fen); send_stockfish_fen(outfd, fishpos, fen);
gen_all_pseudomoves(pos); gen_all_pseudomoves(pos);