5 Commits

11 changed files with 150 additions and 137 deletions

View File

@@ -89,10 +89,10 @@ CFLAGS := -std=gnu11
### dev OR release
# dev
#CFLAGS += -O1
CFLAGS += -O1
#CFLAGS += -g
# release
CFLAGS += -Ofast
#CFLAGS += -Ofast
CFLAGS += -march=native
CFLAGS += -flto
@@ -100,7 +100,7 @@ CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -Wmissing-declarations
# for gprof
#CFLAGS += -pg
CFLAGS += -pg
# Next one may be useful for valgrind (when invalid instructions)
# CFLAGS += -mno-tbm

View File

@@ -75,7 +75,7 @@ static const char *castle_str = "KQkq";
static int fen_check(pos_t *pos)
{
char *colstr[2] = { "white", "black"};
int error = 0, warning = 0;
int warning = 0;
/* en passant, depends on who plays next */
if (pos->en_passant != SQUARE_NONE) {
@@ -130,13 +130,7 @@ static int fen_check(pos_t *pos)
}
}
}
if (!(error = pos_check(pos, 0))) {
/* TODO: Should it really be here ? */
pos->checkers = pos_checkers(pos, pos->turn);
pos->pinners = pos_king_pinners(pos, pos->turn);
pos->blockers = pos_king_blockers(pos, pos->turn, pos->pinners);
}
return error ? -1: warning;
return pos_ok(pos, 0) ? warning: -1;
}
/**

View File

@@ -42,12 +42,12 @@
*
* @return: pos.
*/
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)
{
//# ifdef DEBUG_MOVE_DO
// move_print(move, M_PR_NL | M_PR_LONG);
//# endif
*state = pos->state; /* save irreversible changes */
//*state = pos->state; /* save irreversible changes */
color_t us = pos->turn, them = OPPONENT(us);
square_t from = move_from(move), to = move_to(move);
@@ -146,7 +146,7 @@ pos_t *move_do(pos_t *pos, const move_t move, state_t *state)
*
* @return: pos.
*/
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)
{
//# ifdef DEBUG_MOVE
//log(1, "new move: ");
@@ -184,7 +184,7 @@ pos_t *move_undo(pos_t *pos, const move_t move, const state_t *state)
pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them));
}
pos->state = *state; /* restore irreversible changes */
//pos->state = *state; /* restore irreversible changes */
pos->turn = us;
return pos;
}

View File

@@ -16,7 +16,7 @@
#include "position.h"
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_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);
#endif /* MOVE_DO_H */

View File

@@ -169,7 +169,7 @@ movelist_t *pos_all_legal(const pos_t *pos, movelist_t *movelist, movelist_t *de
* - 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 (excl. en-passant): M_CAPTURE
* - capture (excl. en-passant): M_CAPTURE
* - en-passant: M_EN_PASSANT
* - pawn double push: M_DPUSH
* - promotion: M_PROMOTION
@@ -192,7 +192,6 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist)
bitboard_t movebits, from_pawns;
bitboard_t tmp1, tmp2;
move_t *moves = movelist->move;
int *nmoves = &movelist->nmoves;
int from, to;
@@ -223,7 +222,6 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist)
}
}
bit_for_each64(from, tmp1, pos->bb[us][ROOK] | pos->bb[us][QUEEN]) {
// printf("rook=%d/%s\n", from, sq_to_string(from));
movebits = hyperbola_rook_moves(occ, from) & not_my_pieces;
bit_for_each64(to, tmp2, movebits & empty) {
moves[(*nmoves)++] = move_make(from, to);
@@ -245,16 +243,23 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist)
}
/* pawn: relative rank and files */
bitboard_t rel_rank7 = bb_rel_rank(RANK_7, us);
bitboard_t rel_rank8 = bb_rel_rank(RANK_8, us);
bitboard_t rel_rank3 = bb_rel_rank(RANK_3, us);
/* pawn: ranks 2-6 push 1 and 2 squares */
movebits = pawn_shift_up(pos->bb[us][PAWN] & ~rel_rank7, us) & empty;
bit_for_each64(to, tmp1, movebits) {
movebits = pawn_shift_up(pos->bb[us][PAWN], us) & empty;
bit_for_each64(to, tmp1, movebits & ~rel_rank8) {
from = pawn_push_up(to, them); /* reverse push */
//printf("push %d->%d %s->%s", from, to, sq_to_string(from), sq_to_string(to));
moves[(*nmoves)++] = move_make(from, to);
}
bit_for_each64(to, tmp1, movebits & rel_rank8) { /* promotions */
from = pawn_push_up(to, them); /* reverse push */
moves[(*nmoves)++] = move_make_promote(from, to, QUEEN);
moves[(*nmoves)++] = move_make_promote(from, to, ROOK);
moves[(*nmoves)++] = move_make_promote(from, to, BISHOP);
moves[(*nmoves)++] = move_make_promote(from, to, KNIGHT);
}
/* possible second push */
movebits = pawn_shift_up(movebits & rel_rank3, us) & empty;
bit_for_each64(to, tmp1, movebits) {
@@ -262,20 +267,33 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist)
moves[(*nmoves)++] = move_make_flags(from, to, M_DPUSH);
}
/* pawn: ranks 2-6 captures left */
from_pawns = pos->bb[us][PAWN] & ~rel_rank7; // & ~rel_filea;
movebits = pawn_shift_upleft(from_pawns, us) & enemy_pieces;
bit_for_each64(to, tmp1, movebits) {
/* pawn: captures left */
movebits = pawn_shift_upleft(pos->bb[us][PAWN], us) & enemy_pieces;
bit_for_each64(to, tmp1, movebits & ~rel_rank8) {
from = pawn_push_upleft(to, them); /* reverse capture */
moves[(*nmoves)++] = move_make_capture(from, to);
}
/* pawn: ranks 2-6 captures right */
from_pawns = pos->bb[us][PAWN] & ~rel_rank7; // & ~rel_fileh;
movebits = pawn_shift_upright(from_pawns, us) & enemy_pieces;
bit_for_each64(to, tmp1, movebits) {
from = pawn_push_upright(to, them);
bit_for_each64(to, tmp1, movebits & rel_rank8) {
from = pawn_push_upleft(to, them); /* reverse capture */
moves[(*nmoves)++] = move_make_promote_capture(from, to, QUEEN);
moves[(*nmoves)++] = move_make_promote_capture(from, to, ROOK);
moves[(*nmoves)++] = move_make_promote_capture(from, to, BISHOP);
moves[(*nmoves)++] = move_make_promote_capture(from, to, KNIGHT);
}
/* pawn: captures right */
movebits = pawn_shift_upright(pos->bb[us][PAWN], us) & enemy_pieces;
bit_for_each64(to, tmp1, movebits & ~rel_rank8) {
from = pawn_push_upright(to, them); /* reverse capture */
moves[(*nmoves)++] = move_make_capture(from, to);
}
bit_for_each64(to, tmp1, movebits & rel_rank8) {
from = pawn_push_upright(to, them); /* reverse capture */
moves[(*nmoves)++] = move_make_promote_capture(from, to, QUEEN);
moves[(*nmoves)++] = move_make_promote_capture(from, to, ROOK);
moves[(*nmoves)++] = move_make_promote_capture(from, to, BISHOP);
moves[(*nmoves)++] = move_make_promote_capture(from, to, KNIGHT);
}
/* pawn: en-passant */
if ((to = pos->en_passant) != SQUARE_NONE) {
@@ -287,36 +305,6 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist)
}
}
/* pawn: rank 7 push */
movebits = pawn_shift_up(pos->bb[us][PAWN] & rel_rank7, us) & empty;
bit_for_each64(to, tmp1, movebits) {
from = pawn_push_up(to, them); /* reverse push */
moves[(*nmoves)++] = move_make_promote(from, to, QUEEN);
moves[(*nmoves)++] = move_make_promote(from, to, ROOK);
moves[(*nmoves)++] = move_make_promote(from, to, BISHOP);
moves[(*nmoves)++] = move_make_promote(from, to, KNIGHT);
}
/* pawn promotion: rank 7 captures left */
from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_filea;
movebits = pawn_shift_upleft(from_pawns, us) & enemy_pieces;
bit_for_each64(to, tmp1, movebits) {
from = pawn_push_upleft(to, them); /* reverse capture */
moves[(*nmoves)++] = move_make_promote_capture(from, to, QUEEN);
moves[(*nmoves)++] = move_make_promote_capture(from, to, ROOK);
moves[(*nmoves)++] = move_make_promote_capture(from, to, BISHOP);
moves[(*nmoves)++] = move_make_promote_capture(from, to, KNIGHT);
}
/* pawn: rank 7 captures right */
from_pawns = pos->bb[us][PAWN] & rel_rank7; // & ~rel_fileh;
movebits = pawn_shift_upright(from_pawns, us) & enemy_pieces;
bit_for_each64(to, tmp1, movebits) {
from = pawn_push_upright(to, them); /* reverse capture */
moves[(*nmoves)++] = move_make_promote_capture(from, to, QUEEN);
moves[(*nmoves)++] = move_make_promote_capture(from, to, ROOK);
moves[(*nmoves)++] = move_make_promote_capture(from, to, BISHOP);
moves[(*nmoves)++] = move_make_promote_capture(from, to, KNIGHT);
}
/* castle - Attention ! Castling flags are assumed correct
*/
if (!pos->checkers) {
@@ -342,16 +330,7 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist)
}
}
}
/* TODO
* DONE. pawn ranks 2-6 advance (1 push, + 2 squares for rank 2)
* DONE. pawns rank 7 advance + promotions
* DONE. pawns ranks 2-6 captures, left and right
* DONE. pawns en-passant (with capture)
* DONE. pawns rank 7 capture + promotion
* DONE. castle
*
* add function per piece, and type, for easier debug
*
/* TODO: add function per piece, and type, for easier debug
*/
return *nmoves;
}

View File

@@ -187,11 +187,30 @@ bitboard_t pos_checkers(const pos_t *pos, const color_t color)
}
/**
* pos_king_pinners_blockers() - set position "pinners" and "blockers".
* pos_set_checkers_pinners_blockers() - calculate checkers, pinners and blockers.
* @pos: &position
*
* set position "pinners" on player-to-play king.
* Set position checkers, pinners and blockers on player-to-play king.
* It should be faster than @pos_checkers + @pos_set_pinners_blockers, as
* some calculation will be done once.
*/
/*
* void pos_set_checkers_pinners_blockers(pos_t *pos)
* {
* bitboard_t b_bb = pos->bb[WHITE][BISHOP] | pos->bb[BLACK][BISHOP];
* bitboard_t r_bb = pos->bb[WHITE][ROOK] | pos->bb[BLACK][ROOK];
* bitboard_t q_bb = pos->bb[WHITE][QUEEN] | pos->bb[BLACK][QUEEN];
*
* /\* find out first piece on every diagonal *\/
*
* }
*/
/**
* pos_set_pinners_blockers() - set position pinners and blockers.
* @pos: &position
*
* set position pinners and blockers on player-to-play king.
*/
void pos_set_pinners_blockers(pos_t *pos)
{
@@ -253,7 +272,7 @@ bitboard_t pos_king_blockers(const pos_t *pos, const color_t color, const bitboa
}
/**
* pos_check() - extensive position consistency check.
* pos_ok() - extensive position consistency check.
* @pos: &position
* @strict: if true, call bug_on() on any error.
*
@@ -278,9 +297,9 @@ bitboard_t pos_king_blockers(const pos_t *pos, const color_t color, const bitboa
* TODO: add more checks:
* - kings attacking each other
*
* @Return: Number of detected error (only if @strict is false).
* @return: (if @strict is false) return true if check is ok, false otherwise.
*/
int pos_check(const pos_t *pos, const bool strict)
bool pos_ok(const pos_t *pos, const bool strict)
{
int n, count = 0, bbcount = 0, error = 0;
bitboard_t tmp;
@@ -324,7 +343,7 @@ int pos_check(const pos_t *pos, const bool strict)
error += warn_on(sq_dist(pos->king[WHITE], pos->king[BLACK]) < 2);
bug_on(strict && error);
return error;
return error? false: true;
}
/**

View File

@@ -165,7 +165,7 @@ bitboard_t pos_checkers(const pos_t *pos, const color_t color);
bitboard_t pos_king_pinners(const pos_t *pos, const color_t color);
bitboard_t pos_king_blockers(const pos_t *pos, const color_t color, const bitboard_t );
int pos_check(const pos_t *pos, const bool strict);
bool pos_ok(const pos_t *pos, const bool strict);
void pos_print(const pos_t *pos);
void pos_print_mask(const pos_t *pos, const bitboard_t mask);

View File

@@ -37,33 +37,31 @@
*/
u64 perft(pos_t *pos, int depth, int ply)
{
int subnodes, nmove = 0;
int subnodes, movetmp = 0;
u64 nodes = 0;
movelist_t pseudo = { .nmoves = 0 }, legal = { .nmoves = 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);
pos_all_legal(pos, &pseudo, &legal);
for (nmove = 0; nmove < legal.nmoves; ++nmove ) {
//while ((move = pos_next_legal(pos, &pseudo, &movetmp)) != MOVE_NONE) {
//printf("depth=%d movetmp=%d\n", depth, movetmp);
state_t state;
move = legal.move[nmove];
move_do(pos, move, &state);
while ((move = pos_next_legal(pos, &pseudo, &movetmp)) != MOVE_NONE) {
move_do(pos, move);
subnodes = perft(pos, depth - 1, ply + 1);
if (ply == 1) {
char movestr[8];
printf("%s: %d\n", move_str(movestr, move, 0), subnodes);
}
nodes += subnodes;
move_undo(pos, move, &state);
move_undo(pos, move);
pos->state = state;
}
if (ply == 1)
printf("\nTotal: %lu\n", nodes);
return nodes;
@@ -75,32 +73,31 @@ u64 perft2(pos_t *pos, int depth, int ply)
u64 nodes = 0;
movelist_t pseudo = { .nmoves = 0 };
move_t move;
state_t state;
if (is_in_check(pos, OPPONENT(pos->turn)))
return 0;
if (depth == 0)
return 1;
pos->checkers = pos_checkers(pos, pos->turn);
pos->pinners = pos_king_pinners(pos, pos->turn);
pos->blockers = pos_king_blockers(pos, pos->turn, pos->pinners);
pos_set_pinners_blockers(pos);
state = pos->state;
pos_gen_pseudomoves(pos, &pseudo);
//pos_all_legal(pos, &pseudo, &legal);
for (nmove = 0; nmove < pseudo.nmoves; ++nmove ) {
//while ((move = pos_next_legal(pos, &pseudo, &movetmp)) != MOVE_NONE) {
//printf("depth=%d movetmp=%d\n", depth, movetmp);
state_t state;
move = pseudo.move[nmove];
move_do(pos, move, &state);
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);
}
nodes += subnodes;
move_undo(pos, move, &state);
//}
move_undo(pos, move);
pos->state = state;
}
if (ply == 1)
printf("\nTotal: %lu\n", nodes);

View File

@@ -39,6 +39,15 @@ struct fentest {
*/
//"4k3/pppppppp/8/8/8/8/PPPPPPPP/2BRK3 w - - 0 1",
//"4k3/pppppppp/8/8/8/8/PPPPPPPP/1B1R1K2 w - - 0 1",
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
"illegal white e.p.",
"3k4/8/5K2/3pP3/8/2b5/8/8 w - d6 0 1",
},
{ __LINE__, PERFT,
"only K, pawn push and ep moves",
"3k4/8/8/3pPK2/8/8/8/8 w - d6 0 1"
},
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
"illegal white e.p.",
"3k4/8/8/2qpPK2/8/8/8/8 w - d6 0 1",
@@ -47,10 +56,6 @@ struct fentest {
"illegal black e.p.",
"8/8/8/8/2QPpk2/8/8/3K4 b - d3 0 1",
},
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
"illegal white e.p.",
"3k4/8/5K2/3pP3/8/2b5/8/8 w - d6 0 1",
},
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
"illegal black e.p.",
"8/8/2B5/8/3Pp3/5k2/8/3K4 b - d3 0 1",
@@ -382,7 +387,18 @@ struct fentest {
"Stalemate & Checkmate",
"8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1"
},
{ __LINE__, MOVEDO | PERFT,
"simple movedo/undo: only 2 W knights",
"8/1k6/8/8/8/8/6K1/1NN5 w - - 0 1"
},
{ __LINE__, MOVEDO | PERFT,
"simple movedo/undo: only 2 W knights",
"8/1k6/8/8/8/8/6K1/1NN5 w - - 0 1"
},
{ __LINE__, MOVEDO | PERFT,
"simple movedo/undo: only 2 W knights",
"5n2/1k6/8/8/5K2/8/P7/1N6 w - - 0 1"
},
{ __LINE__, 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"
@@ -399,18 +415,6 @@ struct fentest {
"illegal, SF crash",
"2r1k3/3P4/8/8/8/8/8/4K3 w - - 0 1"
},
{ __LINE__, MOVEDO | PERFT,
"simple movedo/undo: only 2 W knights",
"8/1k6/8/8/8/8/6K1/1NN5 w - - 0 1"
},
{ __LINE__, MOVEDO | PERFT,
"simple movedo/undo: only 2 W knights",
"8/1k6/8/8/8/8/6K1/1NN5 w - - 0 1"
},
{ __LINE__, MOVEDO | PERFT,
"simple movedo/undo: only 2 W knights",
"5n2/1k6/8/8/5K2/8/P7/1N6 w - - 0 1"
},
{ __LINE__, 0, NULL, NULL }
};

View File

@@ -43,28 +43,33 @@ int main(int __unused ac, __unused char**av)
printf("wrong fen %d: [%s]\n", i, fen);
continue;
}
pos->checkers = pos_checkers(pos, pos->turn);
pos_set_pinners_blockers(pos);
pos_gen_pseudomoves(pos, &pseudo);
savepos = pos_dup(pos);
state_t state = pos->state;
int tmp = 0, j = 0;
while ((move = pos_next_legal(pos, &pseudo, &tmp)) != MOVE_NONE) {
state_t state;
move_str(movebuf, move, 0);
//pos_print(pos);
//printf("i=%d j=%d turn=%d move=[%s]\n", i, j, pos->turn,
// move_str(movebuf, move, 0));
//move_p
move_do(pos, move, &state);
move_do(pos, move);
//pos_print(pos);
//fflush(stdout);
if (pos_check(pos, false)) {
if (!pos_ok(pos, false)) {
printf("*** fen %d move %d [%s] invalid position after move_do\n",
test_line, j, movebuf);
exit(0);
}
//printf("%d/%d move_do check ok\n", i, j);
move_undo(pos, move, &state);
if (pos_check(pos, false)) {
move_undo(pos, move);
pos->state = state;
if (!pos_ok(pos, false)) {
printf("*** fen %d move %d [%s] invalid position after move_undo\n",
test_line, j, movebuf);
exit(0);

View File

@@ -259,26 +259,41 @@ int main(int __unused ac, __unused char**av)
#define NANOSEC 1000000000 /* nano sec in sec */
#define MILLISEC 1000000 /* milli sec in sec */
struct timespec ts1, ts2;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts1);
my_count = perft(pos, depth, 1);
clock_gettime(CLOCK_MONOTONIC_RAW, &ts2);
// 1 sec = 1000 millisec
// 1 millisec = 1000 microsec
// 1 microsec = 1000 nanosec
// milli = sec * 1000 + nanosec / 1000000
s64 microsecs = ((s64)ts2.tv_sec - (s64)ts1.tv_sec) * 1000000l
struct timespec ts1, ts2;
s64 microsecs;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts1);
my_count = perft(pos, depth, 1);
clock_gettime(CLOCK_MONOTONIC_RAW, &ts2);
microsecs = ((s64)ts2.tv_sec - (s64)ts1.tv_sec) * 1000000l
+ ((s64)ts2.tv_nsec - (s64)ts1.tv_nsec) / 1000l ;
if (sf_count == my_count) {
printf("OK : fen line=%03d \"%s\" perft=%lu in %'ldms (LPS: %'lu)\n",
test_line, fen, my_count, microsecs/1000l,
my_count*1000000l/microsecs);
printf("pt1 OK : line=%03d perft=%lu ms=%'ldms lps=%'lu \"%s\"\n",
test_line, my_count, microsecs/1000l,
my_count*1000000l/microsecs, fen);
} else {
printf("ERR: fen line=%03d [%s] sf=%lu me=%lu\n",
test_line, fen, sf_count, my_count);
printf("pt1 ERR: line=%03d sf=%lu me=%lu \"%s\"\n",
test_line, sf_count, my_count, fen);
}
clock_gettime(CLOCK_MONOTONIC_RAW, &ts1);
my_count = perft2(pos, depth, 1);
clock_gettime(CLOCK_MONOTONIC_RAW, &ts2);
microsecs = ((s64)ts2.tv_sec - (s64)ts1.tv_sec) * 1000000l
+ ((s64)ts2.tv_nsec - (s64)ts1.tv_nsec) / 1000l ;
if (sf_count == my_count) {
printf("pt2 OK : line=%03d perft=%lu ms=%'ldms lps=%'lu \"%s\"\n\n",
test_line, my_count, microsecs/1000l,
my_count*1000000l/microsecs, fen);
} else {
printf("pt2 ERR: line=%03d sf=%lu me=%lu \"%s\"\n\n",
test_line, sf_count, my_count, fen);
}
pos_del(savepos);
pos_del(pos);