// scid_TB_Available: // Given a material configuration, returns a boolean indicating // if the tablebase for that material is registered for use. // Note: there are actually TWO tablebases for any material // combination, one for each side to move (file suffixes .nbw.emd // and .nbb.emd); this function returns true if EITHER one is // registered (since having only one of the two is usually good // enough to solve the endgame). bool scid_TB_Available (matSigT matsig) { if (EGTB_maxpieces == 0) { return 0; } int counts [10]; counts [0] = matsig_getCount (matsig, WP); counts [1] = matsig_getCount (matsig, WN); counts [2] = matsig_getCount (matsig, WB); counts [3] = matsig_getCount (matsig, WR); counts [4] = matsig_getCount (matsig, WQ); counts [5] = matsig_getCount (matsig, BP); counts [6] = matsig_getCount (matsig, BN); counts [7] = matsig_getCount (matsig, BB); counts [8] = matsig_getCount (matsig, BR); counts [9] = matsig_getCount (matsig, BQ); // Quickly check that there is not too much material: uint wc = 1 + counts[0] + counts[1] + counts[2] + counts[3] + counts[4]; uint bc = 1 + counts[5] + counts[6] + counts[7] + counts[8] + counts[9]; uint bothc = wc + bc; if (bothc > EGTB_maxpieces || wc > PROBE_MAX_PER_SIDE || bc > PROBE_MAX_PER_SIDE) { return false; } // If two lone Kings, just return true: if (bothc == 2) { return true; } // If KB-K or KN-K, return true because they are all-drawn tablebases: if (bothc == 3) { if (counts[1] == 1 || counts[2] == 1 || counts[6] == 1 || counts[7] == 1) { return true; } } int iTb = IDescFindFromCounters (counts); if (iTb == 0) { return false; } if (iTb < 0) { iTb = -iTb; } // Return true if either of the two TBs for this material is available: if (FRegistered (iTb, 0)) { return true; } if (FRegistered (iTb, 1)) { return true; } return false; }
bool CTablebases::probe(eval_t& eval, const CBoard& board) { if (loaded()) { int rgiCounters[10]; for (int32 i = 0; i < 10; ++i) rgiCounters[i] = 0; const int rgsq_cnt = C_PIECES * 5 + 1; square rgsqWhite[rgsq_cnt]; square rgsqBlack[rgsq_cnt]; for (int32 i = 0; i < rgsq_cnt; ++i) rgsqWhite[i] = rgsqBlack[i] = XX; VInitSqCtr(rgiCounters, rgsqWhite, 0, board.getPieceBits(eW_P)); VInitSqCtr(rgiCounters, rgsqWhite, 1, board.getPieceBits(eW_N)); VInitSqCtr(rgiCounters, rgsqWhite, 2, board.getPieceBits(eW_B)); VInitSqCtr(rgiCounters, rgsqWhite, 3, board.getPieceBits(eW_R)); VInitSqCtr(rgiCounters, rgsqWhite, 4, board.getPieceBits(eW_Q)); VInitSqCtr(rgiCounters + 5, rgsqBlack, 0, board.getPieceBits(eB_P)); VInitSqCtr(rgiCounters + 5, rgsqBlack, 1, board.getPieceBits(eB_N)); VInitSqCtr(rgiCounters + 5, rgsqBlack, 2, board.getPieceBits(eB_B)); VInitSqCtr(rgiCounters + 5, rgsqBlack, 3, board.getPieceBits(eB_R)); VInitSqCtr(rgiCounters + 5, rgsqBlack, 4, board.getPieceBits(eB_Q)); int iTb = IDescFindFromCounters(rgiCounters); if (iTb) { bb_t wk = board.getPieceBits(eW_K); while (wk) rgsqWhite[C_PIECES * 5] = popFirstBit(wk); bb_t bk = board.getPieceBits(eB_K); while (bk) rgsqBlack[C_PIECES * 5] = popFirstBit(bk); color side; int fInvert; square* psqW; square* psqB; if (iTb > 0) { side = eWhite == board.getSide() ? x_colorWhite : x_colorBlack; fInvert = 0; psqW = rgsqWhite; psqB = rgsqBlack; } else { side = eWhite == board.getSide() ? x_colorBlack : x_colorWhite; fInvert = 1; psqW = rgsqBlack; psqB = rgsqWhite; iTb = -iTb; } if (FRegisteredFun(iTb, side)) { square sqEnP = eNF != board.getEp() ? board.getEp() : XX; INDEX ind = PfnIndCalcFun(iTb, side) (psqW, psqB, sqEnP, fInvert); int tbValue = L_TbtProbeTable(iTb, side, ind); if (tb_broken != tbValue) { if (tbValue > 0) eval = MATE_VALUE + 2 * (-tb_mate_in_1 + tbValue - 1); else if (tbValue < 0) eval = -MATE_VALUE + 2 * (tb_mate_in_1 + tbValue); else eval = DRAW_VALUE; ++m_hits; return (true); } } } } return(false); }
// scid_TB_Probe: // Given a position, probes the appropriate tablebase and puts the // result in the integer pointed to by <score>. // Returns OK if the probe was successful, or ERROR_NotFound otherwise. // // The value placed in score is as follows, where STM is the side to move: // 3 STM mates in 3, etc. // 2 STM mates in 2. // 1 STM mates in 1. // 0 Draw. // -1 STM is checkmated. // -2 STM mated in 1. // -3 STM mated in 2, etc. // errorT scid_TB_Probe (Position * pos, int * score) { int pieceCounts [10]; uint wSquares [C_PIECES * 6], bSquares [C_PIECES * 6]; uint * wSqs, * bSqs; int iTb, color, flip; uint npieces = pos->GetCount(WHITE) + pos->GetCount(BLACK); // Check that position has few enough pieces on each side: if (npieces > EGTB_maxpieces) { return ERROR_NotFound; } if (pos->GetCount(WHITE) > PROBE_MAX_PER_SIDE) { return ERROR_NotFound; } if (pos->GetCount(BLACK) > PROBE_MAX_PER_SIDE) { return ERROR_NotFound; } // If just two Kings, return "draw" now: if (npieces <= 2) { *score = 0; return OK; } // If just a lone bishop or knight and kings, return draw now: if (npieces == 3) { if (pos->PieceCount(WB) == 1 || pos->PieceCount(BB) == 1 || pos->PieceCount(WN) == 1 || pos->PieceCount(WN) == 1) { *score = 0; return OK; } } // Fill in array of piece counts and find if the tablebase for this // material configuration and side to move is registered: pieceCounts [0] = pos->PieceCount(WP); pieceCounts [1] = pos->PieceCount(WN); pieceCounts [2] = pos->PieceCount(WB); pieceCounts [3] = pos->PieceCount(WR); pieceCounts [4] = pos->PieceCount(WQ); pieceCounts [5] = pos->PieceCount(BP); pieceCounts [6] = pos->PieceCount(BN); pieceCounts [7] = pos->PieceCount(BB); pieceCounts [8] = pos->PieceCount(BR); pieceCounts [9] = pos->PieceCount(BQ); iTb = IDescFindFromCounters (pieceCounts); if (iTb == 0) { return ERROR_NotFound; } if (iTb > 0) { color = (pos->GetToMove() == WHITE) ? 0 : 1; flip = 0; wSqs = wSquares; bSqs = bSquares; } else { color = (pos->GetToMove() == WHITE) ? 1 : 0; flip = 1; wSqs = bSquares; bSqs = wSquares; iTb = - iTb; } // macro that returns true if corresponding TB was found during initializing if (! FRegistered (iTb, color)) { return ERROR_NotFound; } // Now we know the tablebase is registered. Fill in the array of // square values for each piece: uint * firstSq[16]; firstSq[EMPTY] = NULL; firstSq[WK] = &(wSquares [C_PIECES * (x_pieceKing - 1) ]); firstSq[BK] = &(bSquares [C_PIECES * (x_pieceKing - 1) ]); firstSq[WQ] = &(wSquares [C_PIECES * (x_pieceQueen - 1) ]); firstSq[BQ] = &(bSquares [C_PIECES * (x_pieceQueen - 1) ]); firstSq[WR] = &(wSquares [C_PIECES * (x_pieceRook - 1) ]); firstSq[BR] = &(bSquares [C_PIECES * (x_pieceRook - 1) ]); firstSq[WB] = &(wSquares [C_PIECES * (x_pieceBishop - 1) ]); firstSq[BB] = &(bSquares [C_PIECES * (x_pieceBishop - 1) ]); firstSq[WN] = &(wSquares [C_PIECES * (x_pieceKnight - 1) ]); firstSq[BN] = &(bSquares [C_PIECES * (x_pieceKnight - 1) ]); firstSq[WP] = &(wSquares [C_PIECES * (x_piecePawn - 1) ]); firstSq[BP] = &(bSquares [C_PIECES * (x_piecePawn - 1) ]); pieceT * board = pos->GetBoard(); for (squareT sq = A1; sq <= H8; sq++) { pieceT pce = board[sq]; if (pce != EMPTY) { *(firstSq[pce]) = (int) sq; firstSq[pce]++; } } // Set En Passant square it should only be a value other than XX if // there is an EP target square, AND there is a possible EP capture. // Specifying a target EP square (since a pawn has just moved two // squares) when there is no enemy pawn actually able to capture // en passant was able to cause the tablebase to give incorrect // results in testing, so that is why we must check here whether an // EP capture is possible. squareT enPassant = pos->GetEPTarget(); if (enPassant != NULL_SQUARE) { bool possibleEP = false; if (pos->GetToMove() == BLACK) { // White just made a 2-square pawn move: squareT left = square_Move (enPassant, UP_LEFT); if (left != NULL_SQUARE && board[left] == BP) { possibleEP = true; } squareT right = square_Move (enPassant, UP_RIGHT); if (right != NULL_SQUARE && board[right] == BP) { possibleEP = true; } } else { // BLACK just made a 2-square pawn move: squareT left = square_Move (enPassant, DOWN_LEFT); if (left != NULL_SQUARE && board[left] == WP) { possibleEP = true; } squareT right = square_Move (enPassant, DOWN_RIGHT); if (right != NULL_SQUARE && board[right] == WP) { possibleEP = true; } } if (! possibleEP) { enPassant = NULL_SQUARE; } } int epTarget = (int) enPassant; if (enPassant == NULL_SQUARE) { epTarget = XX; } // Now probe the tablebase: INDEX index = PfnIndCalc(iTb,color) (wSqs, bSqs, epTarget, flip); int tbscore = L_TbtProbeTable (iTb, color, index); if (tbscore == bev_broken) { return ERROR_NotFound; } // Convert the tablebase score to the format we want and return it: int distance = tbscore; if (tbscore > 0) { distance = 32767 - tbscore; } else if (tbscore < 0) { distance = -32767 - tbscore; } *score = distance; return OK; }
bool tablebase_t::operator()(const node_t& node, const std::uint_fast8_t height, score_t& score) const { std::uint32_t rgiCounters[10]; unsigned int rgsqWhite[C_PIECES * 5 + 1]; unsigned int rgsqBlack[C_PIECES * 5 + 1]; // initialize counters and piece arrays so the probe code // can compute the modified Godel number. init(rgiCounters, rgsqWhite, 0, node.board_pawns_white()); init(rgiCounters, rgsqWhite, 1, node.board_knights_white()); init(rgiCounters, rgsqWhite, 2, node.board_bishops_white()); init(rgiCounters, rgsqWhite, 3, node.board_rooks_white()); init(rgiCounters, rgsqWhite, 4, node.board_queens_white()); init(rgiCounters + 5, rgsqBlack, 0, node.board_pawns_black()); init(rgiCounters + 5, rgsqBlack, 1, node.board_knights_black()); init(rgiCounters + 5, rgsqBlack, 2, node.board_bishops_black()); init(rgiCounters + 5, rgsqBlack, 3, node.board_rooks_black()); init(rgiCounters + 5, rgsqBlack, 4, node.board_queens_black()); std::lock_guard<std::mutex> lock(const_cast<std::mutex&>(_mutex)); // tablebase registered ? int iTb = IDescFindFromCounters((int*)rgiCounters); if (!iTb) return false; // set up tablebase parameters std::uint32_t invert; int side; unsigned int* psqW; unsigned int* psqB; rgsqWhite[C_PIECES * 5] = node.square_king_white(); rgsqBlack[C_PIECES * 5] = node.square_king_black(); if (iTb > 0) { side = node.color() == black; invert = false; psqW = rgsqWhite; psqB = rgsqBlack; } else { side = node.color() == white; invert = true; psqW = rgsqBlack; psqB = rgsqWhite; iTb = -iTb; } // tablebase registered ? if (!FRegisteredFun(iTb, side)) return false; // get tbValue const unsigned int sqEnP = node.square_en_passant() ? node.square_en_passant() : 127; const std::uint64_t index = PfnIndCalcFun(iTb, side) ((unsigned int *)psqW, (unsigned int *)psqB, sqEnP, invert); const score_t tbValue = L_TbtProbeTable(iTb, side, index); if (tbValue == L_bev_broken) return false; // convert tbValue to score if (tbValue > 0) score = /*Score::max*/ +30000 + 2 * (-L_bev_mi1 + tbValue - 1) - (score_t)height + 1; else if (tbValue < 0) score = /*Score::min*/ -30000 + 2 * (+L_bev_mi1 + tbValue) + (score_t)height; else score = 0;//Score::remis; return true; }
int probe_egtb(void) { #ifdef USE_EGTB int *psqW, *psqB; int rgiCounters[10] = {0,0,0,0,0,0,0,0,0,0}; int side; int fInvert; int sqEnP; int wi = 1, W[8] = {6,0,0,0,0,0}; int bi = 1, B[8] = {6,0,0,0,0,0}; int tbScore; INDEX ind; int j, a, i; int iTb; EGTBProbes++; W[4] = EGTranslate(wking_loc); B[4] = EGTranslate(bking_loc); for (j = 1, a = 1;(a <= piece_count); j++) { i = pieces[j]; if (!i) continue; else a++; switch(board[i]) { case wpawn: rgiCounters[0]++; W[wi] = 1; W[wi+4] = EGTranslate(i); wi++; break; case wknight: rgiCounters[1]++; W[wi] = 2; W[wi+4] = EGTranslate(i); wi++; break; case wbishop: rgiCounters[2]++; W[wi] = 3; W[wi+4] = EGTranslate(i); wi++; break; case wrook: rgiCounters[3]++; W[wi] = 4; W[wi+4] = EGTranslate(i); wi++; break; case wqueen: rgiCounters[4]++; W[wi] = 5; W[wi+4] = EGTranslate(i); wi++; break; case bpawn: rgiCounters[5]++; B[bi] = 1; B[bi+4] = EGTranslate(i); bi++; break; case bknight: rgiCounters[6]++; B[bi] = 2; B[bi+4] = EGTranslate(i); bi++; break; case bbishop: rgiCounters[7]++; B[bi] = 3; B[bi+4] = EGTranslate(i); bi++; break; case brook: rgiCounters[8]++; B[bi] = 4; B[bi+4] = EGTranslate(i); bi++; break; case bqueen: rgiCounters[9]++; B[bi] = 5; B[bi+4] = EGTranslate(i); bi++; break; } } /* more than 4 pieces for one side: not a class we can index */ if (wi >= 4 || bi >= 4) { return KINGCAP; } iTb = IDescFindFromCounters (rgiCounters); if (0 == iTb) { return KINGCAP; } else if (iTb > 0) { /* white = 0*/ side = !white_to_move; fInvert = 0; psqW = W; psqB = B; } else { side = white_to_move; fInvert = 1; psqW = B; psqB = W; iTb = -iTb; } if (!FRegistered(iTb, side)) return KINGCAP; if (ep_square == 0) { sqEnP = XX; } else { if (white_to_move) { if (board[ep_square - 11] == wpawn || board[ep_square - 13] == wpawn) sqEnP = EGTranslate(ep_square); else sqEnP = XX; } else { if (board[ep_square + 11] == bpawn || board[ep_square + 13] == bpawn) sqEnP = EGTranslate(ep_square); else sqEnP = XX; } } ind = PfnIndCalc(iTb, side) (psqW, psqB, sqEnP, fInvert); tbScore = L_TbtProbeTable( iTb, side, ind); if (tbScore == bev_broken) return KINGCAP; EGTBHits++; if (tbScore > 0) { return ((tbScore-bev_mi1)*2+INF-ply-1); } else if (tbScore < 0) { return ((tbScore+bev_mi1)*2-INF+ply); } return 0; #else return KINGCAP; #endif }