5 Commits

Author SHA1 Message Date
9b5c2253b1 Makefile: add -ginline-points 2024-04-10 13:02:23 +02:00
e301e6c726 bug fix in perft() - stupid initialization ! 2024-04-10 12:57:39 +02:00
e78eae21e6 cleanup move_do() 2024-04-10 12:57:08 +02:00
711306c92a simplify/improve pos_set_checkers_pinners_blockers() 2024-04-10 12:54:47 +02:00
660722fadc cleanup 2024-04-10 12:53:31 +02:00
11 changed files with 132 additions and 166 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ vgcore.*
*.i
*.old
*.save
perf.data
/GPATH
/GRTAGS
/GTAGS

View File

@@ -56,7 +56,6 @@ CPPFILES := $(SRC:.c=.i) $(TSTSRC:.c=.i)
CPPFLAGS := -I$(BRINCDIR) -I$(INCDIR)
CPPFLAGS += -DNDEBUG # assert
CPPFLAGS += -DBUG_ON # brlib bug.h
CPPFLAGS += -DWARN_ON # brlib bug.h
@@ -88,9 +87,16 @@ CPPFLAGS := $(strip $(CPPFLAGS))
CFLAGS := -std=gnu11
### dev OR release
# dev
#CFLAGS += -O1
#CFLAGS += -g
# CFLAGS += -O1
CFLAGS += -g # symbols (gdb, perf, etc.)
CFLAGS += -ginline-points # inlined funcs debug info
# for gprof
#CFLAGS += -pg
# Next one may be useful for valgrind (when invalid instructions)
#CFLAGS += -mno-tbm
# release
CFLAGS += -Ofast
@@ -99,10 +105,6 @@ CFLAGS += -flto
CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -Wmissing-declarations
# for gprof
#CFLAGS += -pg
# Next one may be useful for valgrind (when invalid instructions)
# CFLAGS += -mno-tbm
CFLAGS := $(strip $(CFLAGS))

View File

@@ -40,7 +40,6 @@
*/
bool sq_is_attacked(const pos_t *pos, const bitboard_t occ, const square_t sq, const color_t c)
{
bitboard_t sqbb = mask(sq);
color_t opp = OPPONENT(c);
/* bishop / queen */

View File

@@ -160,10 +160,10 @@ void bitboard_init(void)
/* 3) pawn, knight and king attacks
*/
for (square_t sq = A1; sq <= H8; ++sq) {
if (sq >= A2 && sq <= H7) {
bb_pawn_attacks[WHITE][sq] = pawn_attacks_bb(mask(sq), WHITE);
if (sq >= A2)
bb_pawn_attacks[BLACK][sq] = pawn_attacks_bb(mask(sq), BLACK);
}
if (sq <= H7)
bb_pawn_attacks[WHITE][sq] = pawn_attacks_bb(mask(sq), WHITE);
for (int vec = 0; vec < 8; ++vec) {
int dst = sq + knight_vector[vec];

View File

@@ -314,6 +314,7 @@ static __always_inline bitboard_t shift_nw(const bitboard_t bb)
#define pawn_shift_up(bb, c) ((c) == WHITE ? shift_n(bb): shift_s(bb))
#define pawn_shift_upleft(bb, c) ((c) == WHITE ? shift_nw(bb): shift_se(bb))
#define pawn_shift_upright(bb, c) ((c) == WHITE ? shift_ne(bb): shift_sw(bb))
#define pawn_attacks_bb(bb, c) (pawn_shift_upleft(bb, c) | \
pawn_shift_upright(bb, c))
@@ -321,8 +322,8 @@ static __always_inline bitboard_t shift_nw(const bitboard_t bb)
* Need to make functions with control instead.
*/
#define pawn_push_up(sq, c) ((sq) + ((c) == WHITE ? NORTH: SOUTH))
#define pawn_push_upleft(sq, c) ((sq) + ((c) == WHITE ? NORTH_WEST: SOUTH_EAST))
#define pawn_push_upright(sq, c) ((sq) + ((c) == WHITE ? NORTH_EAST: SOUTH_WEST))
//#define pawn_push_upleft(sq, c) ((sq) + ((c) == WHITE ? NORTH_WEST: SOUTH_EAST))
//#define pawn_push_upright(sq, c) ((sq) + ((c) == WHITE ? NORTH_EAST: SOUTH_WEST))
bitboard_t bitboard_between_excl(square_t sq1, square_t sq2);
void bitboard_init(void);

View File

@@ -56,6 +56,7 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state)
piece_type_t ptype = PIECE(piece);
color_t pcolor = COLOR(piece);
piece_t new_piece = piece;
int up = sq_up(us);
++pos->clock_50;
++pos->plycount;
@@ -90,9 +91,9 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state)
} else if (ptype == PAWN) { /* pawn non capture or e.p. */
pos->clock_50 = 0;
if (is_dpush(move)) /* if pawn double push, set e.p. */
pos->en_passant = from + sq_up(us);
pos->en_passant = from + up;
else if (is_enpassant(move)) { /* clear grabbed pawn */
square_t grabbed = pawn_push_up(to, them);
square_t grabbed = to - up;
pos_clr_sq(pos, grabbed);
}
}
@@ -157,7 +158,7 @@ pos_t *move_undo(pos_t *pos, const move_t move)//, const state_t *state)
color_t them = pos->turn, us = OPPONENT(them);
square_t from = move_from(move), to = move_to(move);
piece_t piece = pos->board[to];
int up = sq_up(them);
if (is_promotion(move))
piece = MAKE_PIECE(PAWN, us);
@@ -182,7 +183,7 @@ pos_t *move_undo(pos_t *pos, const move_t move)//, const state_t *state)
pos_set_sq(pos, rookto, pos->board[rookfrom]);
pos_clr_sq(pos, rookfrom);
} else if (is_enpassant(move)) { /* restore grabbed pawn */
square_t grabbed = pawn_push_up(to, them);
square_t grabbed = to + up;
pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them));
}

View File

@@ -42,6 +42,7 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move)
bitboard_t kingbb = pos->bb[us][KING];
bitboard_t occ = pos_occ(pos);
u64 pinned = mask(from) & pos->blockers;
u64 checkers = pos->checkers;
/* (1) - Castling & King
* For castling, we need to check intermediate squares attacks only.
@@ -49,12 +50,12 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move)
* king from occupation bitboard (to catch king moving away from checker
* on same line) !
*/
if (is_castle(move)) {
square_t dir = to > from? 1: -1;
if (sq_attackers(pos, occ, from + dir, them))
return false;
}
if (from == king) {
if (unlikely(from == king)) {
if (unlikely(is_castle(move))) {
square_t dir = to > from? 1: -1;
if (sq_attackers(pos, occ, from + dir, them))
return false;
}
return !sq_attackers(pos, occ ^ kingbb, to, them);
}
@@ -68,18 +69,18 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move)
* e.p., legal if the grabbed pawn is giving check
* pinned piece: always illegal
*/
if (pos->checkers) {
if (checkers) {
if (pinned)
return false;
if (bb_multiple(pos->checkers))
if (bb_multiple(checkers))
return false;
square_t checker = ctz64(pos->checkers);
square_t checker = ctz64(checkers);
if (is_enpassant(move)) {
return pos->en_passant + sq_up(them) == checker;
}
return true;
bitboard_t between = bb_between[king][checker] | pos->checkers;
return mask(to) & between;
//bitboard_t between = bb_between[king][checker] | pos->checkers;
//return mask(to) & between;
}
/* (3) - pinned pieces
@@ -388,6 +389,30 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist)
*moves++ = move_make_flags(from, to, M_DPUSH);
}
/* pawn: captures */
/*
* tmp_bb = pawn_attacks_bb(pos->bb[us][PAWN], us) & enemy_pieces;
* //bb_print("FAIL", tmp_bb);
* to_bb = tmp_bb & ~rel_rank8;
* while (to_bb) {
* to = bb_next(&to_bb);
* from_bb = bb_pawn_attacks[them][to] & pos->bb[us][PAWN];
* while (from_bb) {
* from = bb_next(&from_bb);
* *moves++ = move_make(from, to);
* }
* }
* to_bb = tmp_bb & rel_rank8;
* while (to_bb) {
* to = bb_next(&to_bb);
* from_bb = bb_pawn_attacks[them][to] & pos->bb[us][PAWN];
* while (from_bb) {
* from = bb_next(&from_bb);
* moves = move_make_promotions(moves, from, to);
* }
* }
*/
/* pawn: captures left */
bitboard_t filter = ~bb_rel_file(FILE_A, us);
shift = sq_upleft(us);
@@ -427,18 +452,26 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist)
* TODO: special case when in-check here ?
*/
if ((to = pos->en_passant) != SQUARE_NONE) {
to_bb = mask(to);
/* if e.p not on file H, we may add an e.p move to "up-left" */
filter = ~bb_rel_file(FILE_A, us);
shift = sq_upleft(us);
if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb)
*moves++ = move_make_enpassant(to - shift, to);
filter = ~bb_rel_file(FILE_H, us);
shift = sq_upright(us);
if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb)
*moves++ = move_make_enpassant(to - shift, to);
from_bb = bb_pawn_attacks[them][to] & pos->bb[us][PAWN];
while (from_bb) {
from = bb_next(&from_bb);
*moves++ = move_make_enpassant(from, to);
}
}
/*
* to_bb = mask(to);
* /\* if e.p not on file H, we may add an e.p move to "up-left" *\/
* filter = ~bb_rel_file(FILE_A, us);
* shift = sq_upleft(us);
* if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb)
* *moves++ = move_make_enpassant(to - shift, to);
*
* filter = ~bb_rel_file(FILE_H, us);
* shift = sq_upright(us);
* if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb)
* *moves++ = move_make_enpassant(to - shift, to);
* }
*/
/* TODO: add function per piece, and type, for easier debug
*/

View File

@@ -116,7 +116,7 @@ pos_t *pos_clear(pos_t *pos)
*
* @return: true if equal, false otherwise.
*/
bool pos_cmp(const pos_t *pos1, const pos_t *pos2)
bool pos_cmp(__unused const pos_t *pos1, __unused const pos_t *pos2)
{
#define _cmpf(a) (pos1->a != pos2->a)
bool ret = false;
@@ -201,7 +201,7 @@ void pos_set_checkers_pinners_blockers(pos_t *pos)
bitboard_t occ = pos_occ(pos);
bitboard_t attackers;
bitboard_t checkers = 0, blockers = 0, pinners = 0;
bitboard_t targets, tmpcheckers, tmpblockers, tmppinners, tmpbb;
bitboard_t targets, tmpcheckers, maybeblockers, tmppinners, tmpbb;
square_t king = pos->king[us];
bitboard_t king_bb = mask(king);
int pinner;
@@ -217,55 +217,47 @@ void pos_set_checkers_pinners_blockers(pos_t *pos)
checkers |= tmpcheckers;
/* maybe blockers = not checkers */
tmpblockers = targets & ~tmpcheckers;
maybeblockers = targets & ~tmpcheckers;
/* we find second targets, by removing only first ones (excl. checkers) */
targets = hyperbola_bishop_moves(occ ^ tmpblockers, king) ^ tmpcheckers;
/* we find second targets, by removing first ones (excl. checkers) */
if (maybeblockers) {
targets = hyperbola_bishop_moves(occ ^ maybeblockers, king) ^ tmpcheckers;
/* pinners = only B/Q */
tmppinners = targets & attackers;
pinners |= tmppinners;
//tmpblockers = 0;
/* pinners = only B/Q */
tmppinners = targets & attackers;
/* blockers = we find occupied squares between pinner and king */
bit_for_each64(pinner, tmpbb, tmppinners)
blockers |= bb_between[pinner][king] & tmpblockers;
//blockers |= tmpblockers;
/* blockers = we find occupied squares between pinner and king */
while (tmppinners) {
pinner = bb_next(&tmppinners);
pinners |= mask(pinner);
blockers |= bb_between[pinner][king] & maybeblockers;
}
}
/* same for rook type */
attackers = pos->bb[them][ROOK] | pos->bb[them][QUEEN];
/* targets is all "target" pieces if K was a bishop */
targets = hyperbola_rook_moves(occ, king) & occ;
/* checkers = only opponent B/Q */
tmpcheckers = targets & attackers;
checkers |= tmpcheckers;
/* maybe blockers = not checkers */
tmpblockers = targets & ~tmpcheckers;
/* we find second targets, by removing only first ones (excl. checkers) */
targets = hyperbola_rook_moves(occ ^ tmpblockers, king) ^ tmpcheckers;
/* pinners = only B/Q */
tmppinners = targets & attackers;
pinners |= tmppinners;
//tmpblockers = 0;
/* blockers = we find occupied squares between pinner and king */
bit_for_each64(pinner, tmpbb, tmppinners)
blockers |= bb_between[pinner][king] & tmpblockers;
//blockers |= tmpblockers;
maybeblockers = targets & ~tmpcheckers;
if (maybeblockers) {
targets = hyperbola_rook_moves(occ ^ maybeblockers, king) ^ tmpcheckers;
tmppinners = targets & attackers;
while (tmppinners) {
pinner = bb_next(&tmppinners);
pinners |= mask(pinner);
blockers |= bb_between[pinner][king] & maybeblockers;
}
}
/* pawns */
attackers = pos->bb[them][PAWN];
targets = pawn_shift_upleft(king_bb, us) | pawn_shift_upright(king_bb, us);
checkers |= targets & attackers;
checkers |= bb_pawn_attacks[us][king] & pos->bb[them][PAWN];
/* knight */
attackers = pos->bb[them][KNIGHT];
targets = bb_knight_moves(attackers, king);
targets = bb_knight[king] & attackers;
checkers |= targets;
pos->checkers = checkers;
@@ -366,10 +358,10 @@ bitboard_t pos_king_blockers(const pos_t *pos, const color_t color, const bitboa
*
* @return: (if @strict is false) return true if check is ok, false otherwise.
*/
bool pos_ok(const pos_t *pos, const bool strict)
bool pos_ok(__unused const pos_t *pos, __unused const bool strict)
{
int n, count = 0, bbcount = 0, error = 0;
bitboard_t tmp;
__unused bitboard_t tmp;
/* pawns on 1st ot 8th rank */
tmp = (pos->bb[WHITE][PAWN] | pos->bb[BLACK][PAWN]) & (RANK_1bb | RANK_8bb);
@@ -391,7 +383,7 @@ bool pos_ok(const pos_t *pos, const bool strict)
}
for (square_t sq = 0; sq < 64; ++sq) {
piece_t piece = pos->board[sq];
bitboard_t match;
__unused bitboard_t match;
if (piece == EMPTY)
continue;
color_t c = COLOR(piece);

View File

@@ -45,12 +45,13 @@ u64 perft(pos_t *pos, int depth, int ply)
{
int subnodes, movetmp = 0;
u64 nodes = 0;
movelist_t pseudo = { .nmoves = 0 };
movelist_t pseudo;
move_t move;
state_t state;
if (depth == 0)
return 1;
pseudo.nmoves = 0;
pos->checkers = pos_checkers(pos, pos->turn);
pos_set_pinners_blockers(pos);
state = pos->state;
@@ -74,7 +75,7 @@ u64 perft(pos_t *pos, int depth, int ply)
}
/**
* perft2() - Perform perft on position
* perft_new_pinners() - Perform perft on position
* @pos: &position to search
* @depth: Wanted depth.
* @ply: perft depth level.
@@ -82,61 +83,19 @@ u64 perft(pos_t *pos, int depth, int ply)
* Run perft on a position. This function displays the available moves at @depth
* level for each possible first move, and the total of moves.
*
* This version uses the algorithm:
* if (king in check)
* finish;
* if last depth
* return 1;
* gen pseudo-legal moves
* foreach pseudo-legal move...
*
* @return: total moves found at @depth level.
*/
u64 perft2(pos_t *pos, int depth, int ply)
{
int subnodes, nmove = 0;
u64 nodes = 0;
movelist_t pseudo = { .nmoves = 0 };
move_t move;
state_t state;
if (depth == 0)
return 1;
pos->checkers = pos_checkers(pos, pos->turn);
pos_set_pinners_blockers(pos);
state = pos->state;
pos_gen_pseudomoves(pos, &pseudo);
for (nmove = 0; nmove < pseudo.nmoves; ++nmove ) {
move = pseudo.move[nmove];
move_do(pos, move);
if (!is_in_check(pos, OPPONENT(pos->turn))) {
subnodes = perft2(pos, depth - 1, ply + 1);
nodes += subnodes;
if (ply == 1) {
char movestr[8];
printf("%s: %d\n", move_str(movestr, move, 0), subnodes);
}
}
move_undo(pos, move);
pos->state = state;
}
if (ply == 1)
printf("Total: %lu\n", nodes);
return nodes;
}
u64 perft_new_pinners(pos_t *pos, int depth, int ply)
{
int subnodes, movetmp = 0;
u64 nodes = 0;
movelist_t pseudo = { .nmoves = 0 };
movelist_t pseudo;
move_t move;
state_t state;
if (depth == 0)
return 1;
pseudo.nmoves = 0;
pos_set_checkers_pinners_blockers(pos);
state = pos->state;

View File

@@ -35,10 +35,9 @@ struct fentest {
""
},
*/
/* tests rank movegen bug - FIXED
*/
//"4k3/pppppppp/8/8/8/8/PPPPPPPP/2BRK3 w - - 0 1",
//"4k3/pppppppp/8/8/8/8/PPPPPPPP/1B1R1K2 w - - 0 1",
/* ***************** TEMP TESTS ABOVE ************************** */
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
"illegal white e.p.",
"3k4/8/5K2/3pP3/8/2b5/8/8 w - d6 0 1",
@@ -92,43 +91,43 @@ struct fentest {
* },
*/
{ __LINE__, ATTACK,
"checkers: a1 h1",
"1k6/8/8/8/8/8/8/r2K3r w - - 1 1"
"only 3 K moves (but impossible situation)",
"1k6/8/8/8/8/8/8/r2K3r w - - 0 1"
},
{ __LINE__, ATTACK,
"checkers: a8 h8",
"R2k3R/8/8/8/8/8/8/1K6 b - - 1 1"
"R2k3R/8/8/8/8/8/8/1K6 b - - 0 1"
},
{ __LINE__, ATTACK,
"checkers: b3 g3",
"1k6/8/8/8/8/1r1K2r1/8/8 w - - 1 1"
"1k6/8/8/8/8/1r1K2r1/8/8 w - - 0 1"
},
{ __LINE__, ATTACK,
"checkers: b6 g6",
"8/8/1R1k2R1/8/8/8/8/1K6 b - - 1 1"
"8/8/1R1k2R1/8/8/8/8/1K6 b - - 0 1"
},
{ __LINE__, ATTACK,
"checkers: g2 g7",
"8/k5r1/8/8/6K1/8/6r1/8 w - - 1 1"
"8/k5r1/8/8/6K1/8/6r1/8 w - - 0 1"
},
{ __LINE__, ATTACK,
"checkers: g2 g7",
"8/6R1/8/6k1/8/8/K5R1/8 b - - 1 1"
"8/6R1/8/6k1/8/8/K5R1/8 b - - 0 1"
},
{ __LINE__, ATTACK,
"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 - - 0 1"
},
{ __LINE__, ATTACK,
"checkers: d4 e6 pinners: h4 a5 a8 h8",
"Rn1k1r1R/4b3/1n2N3/B7/3R3B/8/8/3K4 b - - 1 1"
"Rn1k1r1R/4b3/1n2N3/B7/3R3B/8/8/3K4 b - - 0 1"
},
{ __LINE__, ATTACK,
"checkers: d5 e3, pinners: a1 h1 a4 h5",
"3k4/8/8/3r3b/b7/1N2n3/4B3/rN1K1R1r w - - 1 0"
"3k4/8/8/3r3b/b7/1N2n3/4B3/rN1K1R1r w - - 0 1"
},
{ __LINE__, MOVEGEN | MOVEDO | PERFT,

View File

@@ -232,16 +232,15 @@ int main(int __unused ac, __unused char**av)
movelist_t fishmoves;
//move_t move;
FILE *outfd;
int depth = 6;
s64 ms1 = 0, ms1_total = 0;
s64 ms2 = 0, ms2_total = 0;
s64 ms3 = 0, ms3_total = 0;
int run = 7;
int depth = 6, run = 3;
if (ac > 1)
depth = atoi(av[1]);
if (ac > 2)
run = atoi(av[2]);
run = atoi(av[2]) & 3;
printf("depth = %d run=%d\n", depth, run);
if (!run)
@@ -253,7 +252,6 @@ int main(int __unused ac, __unused char**av)
bitboard_init();
hyperbola_init();
CLOCK_DEFINE(clock, CLOCK_PROCESS);
while ((fen = next_fen(PERFT | MOVEDO))) {
test_line = cur_line();
@@ -271,46 +269,29 @@ int main(int __unused ac, __unused char**av)
ms1_total += ms1;
if (sf_count == my_count) {
printf("pt1 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n",
printf("pt1 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n",
test_line, my_count, ms1,
ms1? my_count*1000l/ms1: 0,
fen);
} else {
printf("pt1 ERR: line=%3d sf=%lu me=%lu \"%s\"\n",
printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n",
test_line, sf_count, my_count, fen);
}
}
if (run & 2) {
clock_start(&clock);
my_count = perft2(pos, depth, 1);
my_count = perft_new_pinners(pos, depth, 1);
ms2 = clock_elapsed_ms(&clock);
ms2_total += ms2;
if (sf_count == my_count) {
printf("pt2 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n",
printf("pt2 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n",
test_line, my_count, ms2,
ms2? my_count*1000l/ms2: 0,
fen);
} else {
printf("pt2 ERR: line=%3d sf=%lu me=%lu \"%s\"\n",
test_line, sf_count, my_count, fen);
}
}
if (run & 4) {
clock_start(&clock);
my_count = perft_new_pinners(pos, depth, 1);
ms3 = clock_elapsed_ms(&clock);
ms3_total += ms3;
if (sf_count == my_count) {
printf("pt3 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n",
test_line, my_count, ms3,
ms3? my_count*1000l/ms3: 0,
fen);
} else {
printf("pt3 ERR: line=%3d sf=%lu me=%lu \"%s\"\n",
printf("pt2 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n",
test_line, sf_count, my_count, fen);
}
}
@@ -323,7 +304,5 @@ int main(int __unused ac, __unused char**av)
printf("total perft %'ldms\n", ms1_total);
if (run & 2)
printf("total perft2 %'ldms\n", ms2_total);
if (run & 4)
printf("total perft3 %'ldms\n", ms3_total);
return 0;
}