1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 16:53:09 +00:00

Further speed up bitbase generation

Another trick, along the same lines of previous
patch. This time we first check positions with
white side to move that, becuase we start with
pawn on rank 7, are easily classified as wins,
then black ones.

Number of cycles reduced to 15 !

Becuase now it is faster we can remove a lot of
code to detect theoretical draws. We will calculate
them anyhow, although a bit slower, but the speed
up trick more than compensates it.

Verified that generated bitbases match original ones.

No functional change.
This commit is contained in:
Marco Costalba 2013-02-15 08:56:04 +01:00
parent 10d29add18
commit e0dfb0bc34
3 changed files with 61 additions and 106 deletions

View file

@ -26,22 +26,22 @@
namespace { namespace {
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7 // The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const unsigned IndexMax = 24*64*64*2; // wp_sq * wk_sq * bk_sq * stm = 196608 const unsigned IndexMax = 2*24*64*64; // stm * psq * wksq * bksq = 196608
// Each uint32_t stores results of 32 positions, one per bit // Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32]; uint32_t KPKBitbase[IndexMax / 32];
// A KPK bitbase index is an integer in [0, IndexMax] range // A KPK bitbase index is an integer in [0, IndexMax] range
// //
// Information is mapped in this way // Information is mapped in a way that minimizes number of iterations:
// //
// bit 0: side to move (WHITE or BLACK) // bit 0- 5: white king square (from SQ_A1 to SQ_H8)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8) // bit 6-11: black king square (from SQ_A1 to SQ_H8)
// bit 7-12: white king square (from SQ_A1 to SQ_H8) // bit 12: side to move (WHITE or BLACK)
// bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn 6 - rank (from 6 - RANK_7 to 6 - RANK_2) // bit 15-17: white pawn 6 - rank (from 6 - RANK_7 to 6 - RANK_2)
unsigned index(Color stm, Square bksq, Square wksq, Square psq) { unsigned index(Color us, Square bksq, Square wksq, Square psq) {
return stm + (bksq << 1) + (wksq << 7) + (file_of(psq) << 13) + ((6 - rank_of(psq)) << 15); return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((6 - rank_of(psq)) << 15);
} }
enum Result { enum Result {
@ -55,20 +55,15 @@ namespace {
struct KPKPosition { struct KPKPosition {
void classify_leaf(unsigned idx);
Result classify(const std::vector<KPKPosition>& db)
{ return stm == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
operator Result() const { return res; } operator Result() const { return res; }
Result classify_leaf(unsigned idx);
Result classify(const std::vector<KPKPosition>& db)
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
private: private:
template<Color Us> Bitboard k_attacks() const
{ return StepAttacksBB[KING][Us == WHITE ? wksq : bksq]; }
template<Color Us> Result classify(const std::vector<KPKPosition>& db); template<Color Us> Result classify(const std::vector<KPKPosition>& db);
Color stm; Color us;
Square bksq, wksq, psq; Square bksq, wksq, psq;
Result res; Result res;
}; };
@ -76,12 +71,12 @@ namespace {
} // namespace } // namespace
bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) { bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
assert(file_of(wpsq) <= FILE_D); assert(file_of(wpsq) <= FILE_D);
unsigned idx = index(stm, bksq, wksq, wpsq); unsigned idx = index(us, bksq, wksq, wpsq);
return KPKBitbase[idx / 32] & (1 << (idx & 31)); return KPKBitbase[idx / 32] & (1 << (idx & 0x1F));
} }
@ -94,7 +89,7 @@ void Bitbases::init_kpk() {
for (idx = 0; idx < IndexMax; idx++) for (idx = 0; idx < IndexMax; idx++)
db[idx].classify_leaf(idx); db[idx].classify_leaf(idx);
// Iterate until all positions are classified (26 cycles needed) // Iterate until all positions are classified (15 cycles needed)
while (repeat) while (repeat)
for (repeat = idx = 0; idx < IndexMax; idx++) for (repeat = idx = 0; idx < IndexMax; idx++)
if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN) if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN)
@ -103,101 +98,61 @@ void Bitbases::init_kpk() {
// Map 32 results into one KPKBitbase[] entry // Map 32 results into one KPKBitbase[] entry
for (idx = 0; idx < IndexMax; idx++) for (idx = 0; idx < IndexMax; idx++)
if (db[idx] == WIN) if (db[idx] == WIN)
KPKBitbase[idx / 32] |= 1 << (idx & 31); KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
} }
namespace { namespace {
void KPKPosition::classify_leaf(unsigned idx) { Result KPKPosition::classify_leaf(unsigned idx) {
stm = Color(idx & 1); wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 1) & 0x3F); bksq = Square((idx >> 6) & 0x3F);
wksq = Square((idx >> 7) & 0x3F); us = Color((idx >> 12) & 0x01);
psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15)); psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15));
// Check if two pieces are on the same square or if a king can be captured // Check if two pieces are on the same square or if a king can be captured
if ( wksq == psq || wksq == bksq || bksq == psq if ( wksq == psq || wksq == bksq || bksq == psq
|| (k_attacks<WHITE>() & bksq) || (StepAttacksBB[KING][wksq] & bksq)
|| (stm == WHITE && (StepAttacksBB[PAWN][psq] & bksq))) || (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
res = INVALID; return res = INVALID;
// The position is an immediate win if it is white to move and the white if (us == WHITE)
// pawn can be promoted without getting captured. {
else if ( rank_of(psq) == RANK_7 // Immediate win if pawn can be promoted without getting captured
&& stm == WHITE if ( rank_of(psq) == RANK_7
&& wksq != psq + DELTA_N && wksq != psq + DELTA_N
&& ( square_distance(bksq, psq + DELTA_N) > 1 && ( square_distance(bksq, psq + DELTA_N) > 1
||(k_attacks<WHITE>() & (psq + DELTA_N)))) ||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
res = WIN; return res = WIN;
}
// Immediate draw if is stalemate or king captures undefended pawn
else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq]))
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq]))
return res = DRAW;
// Check for known draw positions return res = UNKNOWN;
//
// Case 1: Stalemate
else if ( stm == BLACK
&& !(k_attacks<BLACK>() & ~(k_attacks<WHITE>() | StepAttacksBB[PAWN][psq])))
res = DRAW;
// Case 2: King can capture undefended pawn
else if ( stm == BLACK
&& (k_attacks<BLACK>() & psq & ~k_attacks<WHITE>()))
res = DRAW;
// Case 3: Black king in front of white pawn
else if ( bksq == psq + DELTA_N
&& rank_of(psq) < RANK_7)
res = DRAW;
// Case 4: White king in front of pawn and black has opposition
else if ( stm == WHITE
&& wksq == psq + DELTA_N
&& bksq == wksq + DELTA_N + DELTA_N
&& rank_of(psq) < RANK_5)
res = DRAW;
// Case 5: Stalemate with rook pawn
else if ( bksq == SQ_A8
&& file_of(psq) == FILE_A)
res = DRAW;
// Case 6: White king trapped on the rook file
else if ( file_of(wksq) == FILE_A
&& file_of(psq) == FILE_A
&& rank_of(wksq) > rank_of(psq)
&& bksq == wksq + 2)
res = DRAW;
else
res = UNKNOWN;
} }
template<Color Us> template<Color Us>
Result KPKPosition::classify(const std::vector<KPKPosition>& db) { Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
// White to Move: If one move leads to a position classified as RESULT_WIN, // White to Move: If one move leads to a position classified as WIN, the result
// the result of the current position is RESULT_WIN. If all moves lead to // of the current position is WIN. If all moves lead to positions classified
// positions classified as RESULT_DRAW, the current position is classified // as DRAW, the current position is classified DRAW otherwise the current
// RESULT_DRAW otherwise the current position is classified as RESULT_UNKNOWN. // position is classified as UNKNOWN.
// //
// Black to Move: If one move leads to a position classified as RESULT_DRAW, // Black to Move: If one move leads to a position classified as DRAW, the result
// the result of the current position is RESULT_DRAW. If all moves lead to // of the current position is DRAW. If all moves lead to positions classified
// positions classified as RESULT_WIN, the position is classified RESULT_WIN. // as WIN, the position is classified WIN otherwise the current position is
// Otherwise, the current position is classified as RESULT_UNKNOWN. // classified UNKNOWN.
Result r = INVALID; Result r = INVALID;
Bitboard b = k_attacks<Us>(); Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq];
while (b) while (b)
{ r |= Us == WHITE ? db[index(~Us, bksq, pop_lsb(&b), psq)]
r |= Us == WHITE ? db[index(BLACK, bksq, pop_lsb(&b), psq)] : db[index(~Us, pop_lsb(&b), wksq, psq)];
: db[index(WHITE, pop_lsb(&b), wksq, psq)];
if (Us == WHITE && (r & WIN))
return res = WIN;
if (Us == BLACK && (r & DRAW))
return res = DRAW;
}
if (Us == WHITE && rank_of(psq) < RANK_7) if (Us == WHITE && rank_of(psq) < RANK_7)
{ {
@ -206,12 +161,12 @@ namespace {
if (rank_of(s) == RANK_3 && s != wksq && s != bksq) if (rank_of(s) == RANK_3 && s != wksq && s != bksq)
r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push
if (r & WIN)
return res = WIN;
} }
return res = r & UNKNOWN ? UNKNOWN : Us == WHITE ? DRAW : WIN; if (Us == WHITE)
return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW;
else
return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN;
} }
} } // namespace

View file

@ -33,7 +33,7 @@ void print(Bitboard b);
namespace Bitbases { namespace Bitbases {
void init_kpk(); void init_kpk();
bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm); bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
} }

View file

@ -199,21 +199,21 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wksq, bksq, wpsq; Square wksq, bksq, wpsq;
Color stm; Color us;
if (strongerSide == WHITE) if (strongerSide == WHITE)
{ {
wksq = pos.king_square(WHITE); wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK); bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN)[0]; wpsq = pos.piece_list(WHITE, PAWN)[0];
stm = pos.side_to_move(); us = pos.side_to_move();
} }
else else
{ {
wksq = ~pos.king_square(BLACK); wksq = ~pos.king_square(BLACK);
bksq = ~pos.king_square(WHITE); bksq = ~pos.king_square(WHITE);
wpsq = ~pos.piece_list(BLACK, PAWN)[0]; wpsq = ~pos.piece_list(BLACK, PAWN)[0];
stm = ~pos.side_to_move(); us = ~pos.side_to_move();
} }
if (file_of(wpsq) >= FILE_E) if (file_of(wpsq) >= FILE_E)
@ -223,7 +223,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
wpsq = mirror(wpsq); wpsq = mirror(wpsq);
} }
if (!Bitbases::probe_kpk(wksq, wpsq, bksq, stm)) if (!Bitbases::probe_kpk(wksq, wpsq, bksq, us))
return VALUE_DRAW; return VALUE_DRAW;
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq)); Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq));
@ -920,14 +920,14 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square wpsq = pos.piece_list(strongerSide, PAWN)[0]; Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Color stm = pos.side_to_move(); Color us = pos.side_to_move();
if (strongerSide == BLACK) if (strongerSide == BLACK)
{ {
wksq = ~wksq; wksq = ~wksq;
bksq = ~bksq; bksq = ~bksq;
wpsq = ~wpsq; wpsq = ~wpsq;
stm = ~stm; us = ~us;
} }
if (file_of(wpsq) >= FILE_E) if (file_of(wpsq) >= FILE_E)
@ -945,5 +945,5 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn. // it's probably at least a draw even with the pawn.
return Bitbases::probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; return Bitbases::probe_kpk(wksq, wpsq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
} }