4 Commits

8 changed files with 230 additions and 58 deletions

View File

@@ -88,9 +88,9 @@ CFLAGS := -std=gnu11
### dev OR release ### dev OR release
# dev # dev
# CFLAGS += -O1
CFLAGS += -g # symbols (gdb, perf, etc.) CFLAGS += -g # symbols (gdb, perf, etc.)
CFLAGS += -ginline-points # inlined funcs debug info CFLAGS += -ginline-points # inlined funcs debug info
#CFLAGS += -Og
# for gprof # for gprof
#CFLAGS += -pg #CFLAGS += -pg
# Next one may be useful for valgrind (when invalid instructions) # Next one may be useful for valgrind (when invalid instructions)
@@ -110,7 +110,7 @@ CFLAGS += -Wmissing-declarations
CFLAGS := $(strip $(CFLAGS)) CFLAGS := $(strip $(CFLAGS))
# development CFLAGS - unused - TODO # development CFLAGS - unused - TODO
#DEV_CFLAGS := -O1 #DEV_CFLAGS := -Og
#DEV_CFLAGS += -g #DEV_CFLAGS += -g
# release CFLAGS - unused - TODO # release CFLAGS - unused - TODO

View File

@@ -42,6 +42,15 @@ bool sq_is_attacked(const pos_t *pos, const bitboard_t occ, const square_t sq, c
{ {
color_t opp = OPPONENT(c); color_t opp = OPPONENT(c);
/*
* return (hyperbola_bishop_moves(occ, sq) & (pos->bb[c][BISHOP] | pos->bb[c][QUEEN])
* || hyperbola_rook_moves(occ, sq) & (pos->bb[c][ROOK] | pos->bb[c][QUEEN])
* || bb_pawn_attacks[opp][sq] & pos->bb[c][PAWN]
* || bb_knight_moves(pos->bb[c][KNIGHT], sq)
* || bb_king_moves(pos->bb[c][KING], sq)
* )
*/
/* bishop / queen */ /* bishop / queen */
if (hyperbola_bishop_moves(occ, sq) & (pos->bb[c][BISHOP] | pos->bb[c][QUEEN])) if (hyperbola_bishop_moves(occ, sq) & (pos->bb[c][BISHOP] | pos->bb[c][QUEEN]))
return true; return true;
@@ -50,14 +59,14 @@ bool sq_is_attacked(const pos_t *pos, const bitboard_t occ, const square_t sq, c
if (hyperbola_rook_moves(occ, sq) & (pos->bb[c][ROOK] | pos->bb[c][QUEEN])) if (hyperbola_rook_moves(occ, sq) & (pos->bb[c][ROOK] | pos->bb[c][QUEEN]))
return true; return true;
/* pawn */
if (bb_pawn_attacks[opp][sq] & pos->bb[c][PAWN])
return true;
/* knight */ /* knight */
if (bb_knight_moves(pos->bb[c][KNIGHT], sq)) if (bb_knight_moves(pos->bb[c][KNIGHT], sq))
return true; return true;
/* pawn */
if (bb_pawn_attacks[opp][sq] & pos->bb[c][PAWN])
return true;
/* king */ /* king */
if (bb_king_moves(pos->bb[c][KING], sq)) if (bb_king_moves(pos->bb[c][KING], sq))
return true; return true;

View File

@@ -131,6 +131,95 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state)
return pos; return pos;
} }
pos_t *move_do2(pos_t *pos, const move_t move, state_t *state)
{
//# ifdef DEBUG_MOVE_DO
// move_print(move, M_PR_NL | M_PR_LONG);
//# endif
color_t us = pos->turn, them = OPPONENT(us);
square_t from = move_from(move), to = move_to(move);
piece_t piece = pos->board[from];
piece_t captured = pos->board[to];
piece_type_t ptype = PIECE(piece);
piece_t new_piece = piece;
int up = sq_up(us);
*state = pos->state; /* save irreversible changes */
++pos->clock_50;
++pos->plycount;
pos->en_passant = SQUARE_NONE;
pos->turn = them;
pos->captured = captured;
bug_on(COLOR(piece) != us);
if (is_promotion(move)) {
bug_on(sq_rank(to) != sq_rel_rank(RANK_8, us));
new_piece = MAKE_PIECE(move_promoted(move), us);
}
if (captured != EMPTY) {
pos->clock_50 = 0;
//pos->captured = pos->board[to]; /* save capture info */
bug_on(pos->board[to] == EMPTY || COLOR(pos->captured) != them);
pos_clr_sq(pos, to); /* clear square */
} else if (is_castle(move)) { /* handle rook move */
square_t rookfrom, rookto;
if (is_castle_K(move)) {
rookfrom = sq_rel(H1, us);
rookto = sq_rel(F1, us);
} else {
rookfrom = sq_rel(A1, us);
rookto = sq_rel(D1, us);
}
pos_set_sq(pos, rookto, pos->board[rookfrom]);
pos_clr_sq(pos, rookfrom);
pos->castle = clr_castle(pos->castle, us);
} 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 + up;
else if (is_enpassant(move)) { /* clear grabbed pawn */
square_t grabbed = to - up;
pos_clr_sq(pos, grabbed);
}
}
pos_clr_sq(pos, from); /* always clear "from" and set "to" */
pos_set_sq(pos, to, new_piece);
if (ptype == KING)
pos->king[us] = to;
/* update castling flags
* As we always consider flags are valid, we :
* - adjust our flags if relative from is "E1", "A1", H1"
* - adjust opp flags if relative to if "A8", H8"
*/
if (can_castle(pos->castle, us)) { /* do we save time with this test ? */
square_t rel_e1 = sq_rel(E1, us);
square_t rel_a1 = sq_rel(A1, us);
square_t rel_h1 = sq_rel(H1, us);
if (from == rel_e1)
pos->castle = clr_castle(pos->castle, us);
else if (from == rel_a1)
pos->castle = clr_ooo(pos->castle, us);
else if (from == rel_h1)
pos->castle = clr_oo(pos->castle, us);
}
if (can_castle(pos->castle, them)) { /* do we save time with this test ? */
square_t rel_a8 = sq_rel(A8, us);
square_t rel_h8 = sq_rel(H8, us);
if (to == rel_a8)
pos->castle = clr_ooo(pos->castle, them);
else if (to == rel_h8)
pos->castle = clr_oo(pos->castle, them);
}
return pos;
}
/** /**
* move_undo() - undo move. * move_undo() - undo move.
* @pos: &pos_t position * @pos: &pos_t position
@@ -186,3 +275,42 @@ pos_t *move_undo(pos_t *pos, const move_t move)
pos->turn = us; pos->turn = us;
return pos; return pos;
} }
pos_t *move_undo2(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);
pos_clr_sq(pos, to); /* always clear "to" ... */
pos_set_sq(pos, from, piece); /* ... and set "from" */
if (PIECE(piece) == KING)
pos->king[us] = from;
if (pos->captured != EMPTY) {
pos_set_sq(pos, to, pos->captured); /* restore captured piece */
} else if (is_castle(move)) { /* make reverse rook move */
square_t rookfrom, rookto;
if (is_castle_K(move)) {
rookfrom = sq_rel(F1, us);
rookto = sq_rel(H1, us);
} else {
rookfrom = sq_rel(D1, us);
rookto = sq_rel(A1, us);
}
pos_set_sq(pos, rookto, pos->board[rookfrom]);
pos_clr_sq(pos, rookfrom);
} else if (is_enpassant(move)) { /* restore grabbed pawn */
square_t grabbed = to + up;
pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them));
}
pos->state = *state; /* restore irreversible changes */
pos->turn = us;
return pos;
}

View File

@@ -19,4 +19,7 @@
pos_t *move_do(pos_t *pos, const move_t move);//, state_t *state); pos_t *move_do(pos_t *pos, const move_t move);//, state_t *state);
pos_t *move_undo(pos_t *pos, const move_t move);//, const state_t *state); pos_t *move_undo(pos_t *pos, const move_t move);//, const state_t *state);
pos_t *move_do2(pos_t *pos, const move_t move, state_t *state);
pos_t *move_undo2(pos_t *pos, const move_t move, const state_t *state);
#endif /* MOVE_DO_H */ #endif /* MOVE_DO_H */

View File

@@ -91,7 +91,7 @@ pos_t *pos_clear(pos_t *pos)
pos->castle = 0; pos->castle = 0;
pos->clock_50 = 0; pos->clock_50 = 0;
pos->plycount = 0; pos->plycount = 0;
//pos->captured = NO_PIECE; pos->captured = NO_PIECE;
for (square_t sq = A1; sq <= H8; ++sq) for (square_t sq = A1; sq <= H8; ++sq)
pos->board[sq] = EMPTY; pos->board[sq] = EMPTY;

View File

@@ -41,7 +41,7 @@
* *
* @return: total moves found at @depth level. * @return: total moves found at @depth level.
*/ */
u64 perft(pos_t *pos, int depth, int ply) u64 perft(pos_t *pos, int depth, int ply, bool output)
{ {
int subnodes; int subnodes;
u64 nodes = 0; u64 nodes = 0;
@@ -60,8 +60,8 @@ u64 perft(pos_t *pos, int depth, int ply)
nodes++; nodes++;
} else { } else {
move_do(pos, *move); move_do(pos, *move);
subnodes = perft(pos, depth - 1, ply + 1); subnodes = perft(pos, depth - 1, ply + 1, output);
if (ply == 1) { if (output && ply == 1) {
char movestr[8]; char movestr[8];
printf("%s: %d\n", move_str(movestr, *move, 0), subnodes); printf("%s: %d\n", move_str(movestr, *move, 0), subnodes);
} }
@@ -71,13 +71,13 @@ u64 perft(pos_t *pos, int depth, int ply)
} }
} }
if (ply == 1) if (output && ply == 1)
printf("Total: %lu\n", nodes); printf("Total: %lu\n", nodes);
return nodes; return nodes;
} }
/** /**
* perft_new_pinners() - Perform perft on position * perft_test() - Perform perft on position, experiment version.
* @pos: &position to search * @pos: &position to search
* @depth: Wanted depth. * @depth: Wanted depth.
* @ply: perft depth level. * @ply: perft depth level.
@@ -85,47 +85,37 @@ u64 perft(pos_t *pos, int depth, int ply)
* Run perft on a position. This function displays the available moves at @depth * Run perft on a position. This function displays the available moves at @depth
* level for each possible first move, and the total of moves. * level for each possible first move, and the total of moves.
* *
* This version uses the algorithm:
* if last depth
* return 1;
* gen pseudo-legal moves
* loop for each legal move in pseudo-legal list
* do-move
* perft (depth -1)
* undo-move
*
* @return: total moves found at @depth level. * @return: total moves found at @depth level.
*/ */
u64 perft_new_pinners(pos_t *pos, int depth, int ply) u64 perft_test(pos_t *pos, int depth, int ply, bool output)
{ {
int subnodes, movetmp = 0; int subnodes;
u64 nodes = 0; u64 nodes = 0;
movelist_t pseudo; movelist_t movelist;
move_t move; move_t *move, *last;
state_t state; state_t state;
pseudo.nmoves = 0; movelist.nmoves = 0;
pos_set_checkers_pinners_blockers(pos); pos_set_checkers_pinners_blockers(pos);
state = pos->state;
pos_gen_pseudo(pos, &pseudo); pos_legal(pos, pos_gen_pseudo(pos, &movelist));
while ((move = pos_next_legal(pos, &pseudo, &movetmp)) != MOVE_NONE) { last = movelist.move + movelist.nmoves;
for (move = movelist.move; move < last; ++move) {
if (depth == 1) { if (depth == 1) {
nodes++; nodes++;
} else { } else {
move_do(pos, move); move_do2(pos, *move, &state);
subnodes = perft_new_pinners(pos, depth - 1, ply + 1); subnodes = perft(pos, depth - 1, ply + 1, output);
if (ply == 1) { if (output && ply == 1) {
char movestr[8]; char movestr[8];
printf("%s: %d\n", move_str(movestr, move, 0), subnodes); printf("%s: %d\n", move_str(movestr, *move, 0), subnodes);
} }
nodes += subnodes; nodes += subnodes;
move_undo(pos, move); move_undo2(pos, *move, &state);
pos->state = state;
} }
} }
if (ply == 1) if (output && ply == 1)
printf("Total: %lu\n", nodes); printf("Total: %lu\n", nodes);
return nodes; return nodes;
} }

View File

@@ -19,8 +19,7 @@
//eval_t negamax(pos_t *pos, int depth, int color); //eval_t negamax(pos_t *pos, int depth, int color);
//eval_t pvs(pos_t *pos, int depth, int alpha, int beta, int color); //eval_t pvs(pos_t *pos, int depth, int alpha, int beta, int color);
u64 perft(pos_t *pos, int depth, int ply); u64 perft(pos_t *pos, int depth, int ply, bool output);
u64 perft2(pos_t *pos, int depth, int ply); u64 perft_test(pos_t *pos, int depth, int ply, bool output);
u64 perft_new_pinners(pos_t *pos, int depth, int ply);
#endif /* SEARCH_H */ #endif /* SEARCH_H */

View File

@@ -16,6 +16,7 @@
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#include <locale.h> #include <locale.h>
#include <limits.h>
#include "chessdefs.h" #include "chessdefs.h"
#include "fen.h" #include "fen.h"
@@ -105,6 +106,7 @@ static u64 send_stockfish_fen(FILE *desc, pos_t *pos, movelist_t *movelist,
//sprintf(str, "stockfish \"position fen %s\ngo perft depth\n\"", fen); //sprintf(str, "stockfish \"position fen %s\ngo perft depth\n\"", fen);
fprintf(desc, "position fen %s\ngo perft %d\n", fen, depth); fprintf(desc, "position fen %s\ngo perft %d\n", fen, depth);
//fflush(desc); //fflush(desc);
while ((buflen = getline(&buf, &alloc, stdin)) > 0) { while ((buflen = getline(&buf, &alloc, stdin)) > 0) {
buf[--buflen] = 0; buf[--buflen] = 0;
if (buflen == 0) if (buflen == 0)
@@ -233,17 +235,25 @@ int main(int __unused ac, __unused char**av)
int i = 0, test_line; int i = 0, test_line;
u64 sf_count = 0, my_count; u64 sf_count = 0, my_count;
char *fen; char *fen;
pos_t *pos, *savepos, *fishpos = pos_new(); pos_t *pos;
pos_t *fishpos = pos_new();
movelist_t fishmoves; movelist_t fishmoves;
//move_t move; //move_t move;
FILE *outfd = NULL; FILE *outfd = NULL;
s64 ms1 = 0, ms1_total = 0; struct {
s64 ms2 = 0, ms2_total = 0; s64 count, ms;
s64 minlps, maxlps;
int skipped;
} res[2] = {
{ .minlps=LONG_MAX },
{ .minlps=LONG_MAX },
};
s64 ms, lps;
int opt, depth = 6, run = 3; int opt, depth = 6, run = 3;
bool sf_run = true; bool sf_run = true, perft_output = false;
while ((opt = getopt(ac, av, "nd:p:")) != -1) { while ((opt = getopt(ac, av, "vnd:p:")) != -1) {
switch (opt) { switch (opt) {
case 'd': case 'd':
depth = atoi(optarg); depth = atoi(optarg);
@@ -254,6 +264,9 @@ int main(int __unused ac, __unused char**av)
case 'n': case 'n':
sf_run = false; sf_run = false;
break; break;
case 'v':
perft_output = true;
break;
default: default:
return usage(*av); return usage(*av);
} }
@@ -285,18 +298,29 @@ int main(int __unused ac, __unused char**av)
if (sf_run) if (sf_run)
sf_count = send_stockfish_fen(outfd, fishpos, &fishmoves, fen, depth); sf_count = send_stockfish_fen(outfd, fishpos, &fishmoves, fen, depth);
savepos = pos_dup(pos); // savepos = pos_dup(pos);
if (run & 1) { if (run & 1) {
clock_start(&clock); clock_start(&clock);
my_count = perft(pos, depth, 1); my_count = perft(pos, depth, 1, perft_output);
ms1 = clock_elapsed_ms(&clock); ms = clock_elapsed_ms(&clock);
ms1_total += ms1; if (!ms) {
res[0].skipped++;
lps = 0;
} else {
lps = my_count * 1000l / ms;
res[0].ms += ms;
res[0].count += my_count;
if (lps > res[0].maxlps)
res[0].maxlps = lps;
if (lps < res[0].minlps)
res[0].minlps = lps;
}
if (!sf_run || sf_count == my_count) { if (!sf_run || 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, test_line, my_count, ms,
ms1? my_count*1000l/ms1: 0, lps,
fen); fen);
} else { } else {
printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n",
@@ -306,14 +330,25 @@ int main(int __unused ac, __unused char**av)
if (run & 2) { if (run & 2) {
clock_start(&clock); clock_start(&clock);
my_count = perft_new_pinners(pos, depth, 1); my_count = perft_test(pos, depth, 1, perft_output);
ms2 = clock_elapsed_ms(&clock); ms = clock_elapsed_ms(&clock);
ms2_total += ms2; if (!ms) {
res[1].skipped++;
lps = 0;
} else {
lps = my_count * 1000l / ms;
res[1].ms += ms;
res[1].count += my_count;
if (lps > res[1].maxlps)
res[1].maxlps = lps;
if (lps < res[1].minlps)
res[1].minlps = lps;
}
if (!sf_run || sf_count == my_count) { if (!sf_run || 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, test_line, my_count, ms,
ms2? my_count*1000l/ms2: 0, lps,
fen); fen);
} else { } else {
printf("pt2 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", printf("pt2 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n",
@@ -321,15 +356,23 @@ int main(int __unused ac, __unused char**av)
} }
} }
printf("\n"); printf("\n");
pos_del(savepos); // pos_del(savepos);
pos_del(pos); pos_del(pos);
i++; i++;
/* to run first test only */ /* to run first test only */
// exit(0); // exit(0);
} }
if (run & 1) if (run & 1)
printf("total perft %'ldms\n", ms1_total); printf("total perft %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n",
res[0].count, res[0].ms,
res[0].count * 1000l / res[0].ms,
res[0].minlps, res[0].maxlps,
res[0].skipped);
if (run & 2) if (run & 2)
printf("total perft2 %'ldms\n", ms2_total); printf("total perft2 %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n",
res[1].count, res[1].ms,
res[1].count * 1000l / res[1].ms,
res[1].minlps, res[1].maxlps,
res[1].skipped);
return 0; return 0;
} }