void init() { Weights[Mobility] = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightsInternal[Mobility]); Weights[PassedPawns] = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]); Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]); Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]); Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]); // King safety is asymmetrical. Our king danger level is weighted by // "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness". // If running in analysis mode, make sure we use symmetrical king safety. We // do this by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] // by their average. if (Options["UCI_AnalyseMode"]) Weights[KingDangerUs] = Weights[KingDangerThem] = (Weights[KingDangerUs] + Weights[KingDangerThem]) / 2; const int MaxSlope = 30; const int Peak = 1280; for (int t = 0, i = 1; i < 100; i++) { t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope)); KingDangerTable[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]); KingDangerTable[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]); } if (Options["UCI_AnalyseMode"]) ContemptFactor = VALUE_ZERO; else ContemptFactor = Options["Contempt Factor"] * PawnValueMg / 100; }
Score Entry::do_king_safety(const Position& pos, Square ksq) { kingSquares[Us] = ksq; castlingRights[Us] = pos.can_castle(Us); int minKingPawnDistance = 0; #ifdef THREECHECK CheckCount checks = pos.is_three_check() ? pos.checks_given(~Us) : CHECKS_0; #endif Bitboard pawns = pos.pieces(Us, PAWN); if (pawns) while (!(DistanceRingBB[ksq][minKingPawnDistance++] & pawns)) {} Value bonus = shelter_storm<Us>(pos, ksq); // If we can castle use the bonus after the castling if it is bigger if (pos.can_castle(MakeCastling<Us, KING_SIDE>::right)) bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1))); if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right)) bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1))); #ifdef THREECHECK // Decrease score when checks have been taken return make_score(bonus, (-16 * minKingPawnDistance) + (-2 * checks)); #else return make_score(bonus, -16 * minKingPawnDistance); #endif }
void init() { Weights[Mobility] = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightsInternal[Mobility]); Weights[PassedPawns] = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]); Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]); Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]); Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]); const int MaxSlope = 30; const int Peak = 1280; for (int t = 0, i = 1; i < 100; i++) { t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope)); KingDangerTable[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]); KingDangerTable[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]); } }
void init() { Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]); Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]); Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]); Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]); Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]); Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]); const double MaxSlope = 30; const double Peak = 1280; for (int t = 0, i = 1; i < 100; ++i) { t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope))); KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]); KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]); } }
void init() { const int bonusByFile[] = { 1, 3, 3, 4, 4, 3, 3, 1 }; for (Rank r = RANK_1; r < RANK_8; ++r) for (File f = FILE_A; f <= FILE_H; ++f) { int bonus = r * (r - 1) * (r - 2) + bonusByFile[f] * (r / 2 + 1); Connected[f][r] = make_score(bonus, bonus); } }
void init() { const double MaxSlope = 30; const double Peak = 1280; for (int t = 0, i = 1; i < 100; ++i) { t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope))); KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]); } }
void init() { const int MaxSlope = 8700; const int Peak = 1280000; int t = 0; for (int i = 0; i < 400; ++i) { t = std::min(Peak, std::min(i * i * 27, t + MaxSlope)); KingDanger[i] = make_score(t / 1000, 0) * Weights[KingSafety]; } }
void init() { const int chainByFile[8] = { 1, 3, 3, 4, 4, 3, 3, 1 }; int bonus; for (Rank r = RANK_1; r < RANK_8; ++r) for (File f = FILE_A; f <= FILE_H; ++f) { bonus = r * (r-1) * (r-2) + chainByFile[f] * (r/2 + 1); ChainMember[f][r] = make_score(bonus, bonus); } }
void init() { const double MaxSlope = 7.5; const double Peak = 1280; double t = 0.0; for (int i = 1; i < 400; ++i) { t = std::min(Peak, std::min(0.025 * i * i, t + MaxSlope)); KingDanger[i] = apply_weight(make_score(int(t), 0), Weights[KingSafety]); } }
void Eval::init() { const int MaxSlope = 322; const int Peak = 47410; int t = 0; for (int i = 0; i < 400; ++i) { t = std::min(Peak, std::min(i * i - 16, t + MaxSlope)); KingDanger[i] = make_score(t * 268 / 7700, 0); } }
Score Entry::do_king_safety(const Position& pos, Square ksq) { kingSquares[Us] = ksq; castlingRights[Us] = pos.can_castle(Us); minKPdistance[Us] = 0; Bitboard pawns = pos.pieces(Us, PAWN); if (pawns) while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {} if (relative_rank(Us, ksq) > RANK_4) return make_score(0, -16 * minKPdistance[Us]); Value bonus = shelter_storm<Us>(pos, ksq); // If we can castle use the bonus after the castling if it is bigger if (pos.can_castle(MakeCastling<Us, KING_SIDE>::right)) bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1))); if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right)) bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1))); return make_score(bonus, -16 * minKPdistance[Us]); }
// init() initializes piece square tables: the white halves of the tables are // copied from Bonus[] adding the piece value, then the black halves of the // tables are initialized by flipping and changing the sign of the white scores. void init() { for (PieceType pt = PAWN; pt <= KING; ++pt) { PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt]; Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]); for (Square s = SQ_A1; s <= SQ_H8; ++s) { int edgeDistance = file_of(s) < FILE_E ? file_of(s) : FILE_H - file_of(s); psq[BLACK][pt][~s] = -(psq[WHITE][pt][s] = v + Bonus[pt][rank_of(s)][edgeDistance]); } } }
// init() initializes piece-square tables: the white halves of the tables are // copied from Bonus[] adding the piece value, then the black halves of the // tables are initialized by flipping and changing the sign of the white scores. void init() { for (Piece pc = W_PAWN; pc <= W_KING; ++pc) { PieceValue[MG][~pc] = PieceValue[MG][pc]; PieceValue[EG][~pc] = PieceValue[EG][pc]; Score v = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); for (Square s = SQ_A1; s <= SQ_H8; ++s) { File f = std::min(file_of(s), FILE_H - file_of(s)); psq[ pc][ s] = v + Bonus[pc][rank_of(s)][f]; psq[~pc][~s] = -psq[pc][s]; } } }
// init() initializes piece-square tables: the white halves of the tables are // copied from Bonus[] adding the piece value, then the black halves of the // tables are initialized by flipping and changing the sign of the white scores. void init() { for (PieceType pt = PAWN; pt <= KING; ++pt) { PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt]; Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]); for (Square s = SQ_A1; s <= SQ_H8; ++s) { File f = std::min(file_of(s), FILE_H - file_of(s)); psq[WHITE][pt][ s] = v + Bonus[pt][rank_of(s)][f]; psq[BLACK][pt][~s] = -psq[WHITE][pt][s]; } } }
// init() initializes piece-square tables: the white halves of the tables are // copied from Bonus[] adding the piece value, then the black halves of the // tables are initialized by flipping and changing the sign of the white scores. void init() { for (Piece pc = W_PAWN; pc <= W_KING; ++pc) { PieceValue[MG][~pc] = PieceValue[MG][pc]; PieceValue[EG][~pc] = PieceValue[EG][pc]; Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); for (Square s = SQ_A1; s <= SQ_H8; ++s) { File f = std::min(file_of(s), ~file_of(s)); psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] : Bonus[pc][rank_of(s)][f]); psq[~pc][~s] = -psq[pc][s]; } } }
Score Entry::do_king_safety(const Position& pos) { Square ksq = pos.square<KING>(Us); kingSquares[Us] = ksq; castlingRights[Us] = pos.can_castle(Us); int minKingPawnDistance = 0; Bitboard pawns = pos.pieces(Us, PAWN); if (pawns) while (!(DistanceRingBB[ksq][++minKingPawnDistance] & pawns)) {} Value bonus = evaluate_shelter<Us>(pos, ksq); // If we can castle use the bonus after the castling if it is bigger if (pos.can_castle(Us | KING_SIDE)) bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1))); if (pos.can_castle(Us | QUEEN_SIDE)) bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1))); return make_score(bonus, -16 * minKingPawnDistance); }
void init(OptionsMap& o) { o["Write Debug Log"] = Option(false, on_logger); o["Write Search Log"] = Option(false); o["Search Log Filename"] = Option("SearchLog.txt"); o["Book File"] = Option("book.bin"); o["Best Book Move"] = Option(false); o["Contempt Factor"] = Option(0, -50, 50); o["Mobility (Midgame)"] = Option(100, 0, 200, on_eval); o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); o["Pawn Structure (Midgame)"] = Option(100, 0, 200, on_eval); o["Pawn Structure (Endgame)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Midgame)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval); o["Space"] = Option(100, 0, 200, on_eval); o["Aggressiveness"] = Option(100, 0, 200, on_eval); o["Cowardice"] = Option(100, 0, 200, on_eval); o["Min Split Depth"] = Option(0, 0, 12, on_threads); o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); o["Threads"] = Option(1, 1, MAX_THREADS, on_threads); o["Idle Threads Sleep"] = Option(true); o["Hash"] = Option(128, 1, 8192, on_hash_size); o["Clear Hash"] = Option(on_clear_hash); o["Ponder"] = Option(true); o["OwnBook"] = Option(false); o["MultiPV"] = Option(1, 1, 500); o["Skill Level"] = Option(20, 0, 20); o["Emergency Move Horizon"] = Option(40, 0, 50); o["Emergency Base Time"] = Option(50, 0, 30000); o["Emergency Move Time"] = Option(20, 0, 5000); o["Minimum Thinking Time"] = Option(20, 0, 5000); o["Slow Mover"] = Option(50, 10, 1000); o["UCI_Chess960"] = Option(false); o["UCI_AnalyseMode"] = Option(false, on_eval); o["Piece Structure"] = Option(100, 0, 200, on_eval); typedef Value V; #define S(mg, eg) make_score(mg, eg) //用于产生tuner文件 //Log log; //log<<"name, init, max, min, c_end, r_end, elod"<<std::endl; //PAWN, BISHOP, ADVISOR, KNIGHT, CANNON, ROOK, KING const Score MobilityBonus[][32] = { {}, {},//Pawn { S( 0, 0), S( 0, 0 ), S( 0, 0), S(0, 0), S(0, 0)},// Bishops { S( 0, 0), S( 0, 0 ), S( 0, 0), S(0, 0), S(0, 0)},// Advisor { S(-35,-30), S(-20,-20), S(-20,-20), S( 0, 0), S(0, 0), S(15, 10),S( 15, 10), S( 25, 12), S(25, 12) },//knight { S( -10, -10), S( 2, 4), S( 4, 4), S(6, 6), S(8, 8),S(10, 10),S(12, 12),S(12, 12),S(12, 12),S(12, 12),S(12, 12),S(12, 12),S(12, 12),S(12, 12),S(12, 12),S(12, 12),S(12, 12),S(12, 12)},// Cannon { S(-20,-20), S(-18,-18), S(-16,-16), S( -10,-10), S( -8,-8), S(-4,-4),S( 0, 0), S( 4, 2), S(8, 4), S(12,6), S(16,8), S(20,10),S( 24,12), S( 24,12), S(24,12), S(24,12), S(24,12), S(24,12)}, // Rooks }; for (int pt1 = KNIGHT; pt1 <= ROOK; ++pt1) { for (int c = 0; c <= 17; ++c) { int min = -40; int max = 40; if (pt1 == KNIGHT) { min = -50; max = 50; } if (pt1 == CANNON) { min = 0; max = 20; } if (pt1 == ROOK) { min = -30; max = 30; } int m = (int)mg_value(MobilityBonus[pt1][c]); int e = (int)eg_value(MobilityBonus[pt1][c]); char buf[256] = {0}; char text[1024]={0}; sprintf(buf, "MobilityBonusM[%d][%d]",pt1,c); o[buf] = Option(m, min, max, on_eval_variables); //sprintf(text, "%s,%d,%d,%d,%d,%d,%d",buf, m, max,min,8, 1, 0); //log<<text<<std::endl; //------ sprintf(buf, "MobilityBonusE[%d][%d]",pt1,c); o[buf] = Option(e, min, max, on_eval_variables); //sprintf(text, "%s,%d,%d,%d,%d,%d,%d",buf, e, max,min,8, 1, 0); //log<<text<<std::endl; } } const Score RookPin = make_score(26, 31); const Score CannonPin = make_score(16, 11); const Score RookOnPawn = make_score(10, 28); const Score RookOpenFile = make_score(53, 21); const Score RookPinRook = make_score(20, 20); const Score CannonPinRook = make_score(10, 10); const Score CannonPinKnight = make_score(10, 10); const Score CannonPinBishop = make_score(5, 3); const Score KnightLegPawn = make_score(16, 0); { char buf[256] = {0}; char text[1024]={0}; int min = -50; int max = 50; int m = 0; int e = 0; #define GEN_CODE(namem,namee, v, minv, maxv) {\ m = (int)mg_value((v));\ e = (int)eg_value((v));\ min = minv;\ max = maxv;\ o[namem]= Option(m, min, max, on_eval_variables);\ o[namee]= Option(e, min, max, on_eval_variables);\ } //sprintf(text, "%s,%d,%d,%d,%d,%d,%d",(namem), m, max,min,8, 1, 0);\ //log<<text<<std::endl;\ //sprintf(text, "%s,%d,%d,%d,%d,%d,%d",(namee), e, max,min,8, 1, 0);\ //log<<text<<std::endl;\ } GEN_CODE("RookPinM","RookPinE", RookPin, 0, 50); GEN_CODE("CannonPinM","CannonPinE", CannonPin, 0, 50); GEN_CODE("RookOnPawnM","RookOnPawnE", RookOnPawn, 0, 50); GEN_CODE("RookOpenFileM","RookOpenFileE", RookOpenFile, 0, 50); GEN_CODE("RookPinRookM","RookPinRookE", RookPinRook, 0, 50); GEN_CODE("CannonPinRookM","CannonPinRookE", CannonPinRook, 0, 50); GEN_CODE("CannonPinKnightM","CannonPinKnightE", CannonPinKnight, 0, 50); GEN_CODE("CannonPinBishopM","CannonPinBishopE", CannonPinBishop, 0, 50); GEN_CODE("KnightLegPawnM","KnightLegPawnE", KnightLegPawn, 0, 50); }
void thread_search(Pos *pos) { Value bestValue, alpha, beta, delta; Move pv[MAX_PLY + 1]; Move lastBestMove = 0; Depth lastBestMoveDepth = DEPTH_ZERO; double timeReduction = 1.0; Stack *ss = pos->st; // At least the seventh element of the allocated array. for (int i = -7; i < 3; i++) memset(SStackBegin(ss[i]), 0, SStackSize); (ss-1)->endMoves = pos->moveList; for (int i = -7; i < 0; i++) ss[i].history = &(*pos->counterMoveHistory)[0][0]; // Use as sentinel for (int i = 0; i <= MAX_PLY; i++) ss[i].ply = i; ss->pv = pv; bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; pos->completedDepth = DEPTH_ZERO; if (pos->threadIdx == 0) mainThread.bestMoveChanges = 0; int multiPV = option_value(OPT_MULTI_PV); #if 0 Skill skill(option_value(OPT_SKILL_LEVEL)); // When playing with strength handicap enable MultiPV search that we will // use behind the scenes to retrieve a set of possible moves. if (skill.enabled()) multiPV = std::max(multiPV, (size_t)4); #endif RootMoves *rm = pos->rootMoves; multiPV = min(multiPV, rm->size); // Iterative deepening loop until requested to stop or the target depth // is reached. while ( (pos->rootDepth += ONE_PLY) < DEPTH_MAX && !Signals.stop && !( Limits.depth && pos->threadIdx == 0 && pos->rootDepth / ONE_PLY > Limits.depth)) { // Age out PV variability metric if (pos->threadIdx == 0) mainThread.bestMoveChanges *= 0.517; // Save the last iteration's scores before first PV line is searched and // all the move scores except the (new) PV are set to -VALUE_INFINITE. for (int idx = 0; idx < rm->size; idx++) rm->move[idx].previousScore = rm->move[idx].score; pos->contempt = pos_stm() == WHITE ? make_score(base_ct, base_ct / 2) : -make_score(base_ct, base_ct / 2); int pvFirst = 0, pvLast = 0; // MultiPV loop. We perform a full root search for each PV line for (int pvIdx = 0; pvIdx < multiPV && !Signals.stop; pvIdx++) { pos->pvIdx = pvIdx; if (pvIdx == pvLast) { pvFirst = pvLast; for (pvLast++; pvLast < rm->size; pvLast++) if (rm->move[pvLast].tbRank != rm->move[pvFirst].tbRank) break; pos->pvLast = pvLast; } pos->selDepth = 0; // Skip the search if we have a mate value from DTM tables. if (abs(rm->move[pvIdx].tbRank) > 1000) { bestValue = rm->move[pvIdx].score = rm->move[pvIdx].tbScore; alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; goto skip_search; } // Reset aspiration window starting size if (pos->rootDepth >= 5 * ONE_PLY) { Value previousScore = rm->move[pvIdx].previousScore; delta = 20; alpha = max(previousScore - delta, -VALUE_INFINITE); beta = min(previousScore + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore int ct = base_ct + 88 * previousScore / (abs(previousScore) + 200); pos->contempt = pos_stm() == WHITE ? make_score(ct, ct / 2) : -make_score(ct, ct / 2); } // Start with a small aspiration window and, in the case of a fail // high/low, re-search with a bigger window until we're not failing // high/low anymore. int failedHighCnt = 0; while (true) { Depth adjustedDepth = max(ONE_PLY, pos->rootDepth - failedHighCnt * ONE_PLY); bestValue = search_PV(pos, ss, alpha, beta, adjustedDepth); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the // first and eventually the new best one are set to -VALUE_INFINITE // and we want to keep the same order for all the moves except the // new PV that goes to the front. Note that in case of MultiPV // search the already searched PV lines are preserved. stable_sort(&rm->move[pvIdx], pvLast - pvIdx); // If search has been stopped, we break immediately. Sorting and // writing PV back to TT is safe because RootMoves is still // valid, although it refers to the previous iteration. if (Signals.stop) break; // When failing high/low give some update (without cluttering // the UI) before a re-search. if ( pos->threadIdx == 0 && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) && time_elapsed() > 3000) uci_print_pv(pos, pos->rootDepth, alpha, beta); // In case of failing low/high increase aspiration window and // re-search, otherwise exit the loop. if (bestValue <= alpha) { beta = (alpha + beta) / 2; alpha = max(bestValue - delta, -VALUE_INFINITE); if (pos->threadIdx == 0) { failedHighCnt = 0; Signals.stopOnPonderhit = 0; } } else if (bestValue >= beta) { beta = min(bestValue + delta, VALUE_INFINITE); if (pos->threadIdx == 0) failedHighCnt++; } else break; delta += delta / 4 + 5; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } // Sort the PV lines searched so far and update the GUI stable_sort(&rm->move[pvFirst], pvIdx - pvFirst + 1); skip_search: if ( pos->threadIdx == 0 && (Signals.stop || pvIdx + 1 == multiPV || time_elapsed() > 3000)) uci_print_pv(pos, pos->rootDepth, alpha, beta); } if (!Signals.stop) pos->completedDepth = pos->rootDepth; if (rm->move[0].pv[0] != lastBestMove) { lastBestMove = rm->move[0].pv[0]; lastBestMoveDepth = pos->rootDepth; } // Have we found a "mate in x"? if ( Limits.mate && bestValue >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - bestValue <= 2 * Limits.mate) Signals.stop = 1; if (pos->threadIdx != 0) continue; #if 0 // If skill level is enabled and time is up, pick a sub-optimal best move if (skill.enabled() && skill.time_to_pick(thread->rootDepth)) skill.pick_best(multiPV); #endif // Do we have time for the next iteration? Can we stop searching now? if ( use_time_management() && !Signals.stop && !Signals.stopOnPonderhit) { // Stop the search if only one legal move is available, or if all // of the available time has been used. double fallingEval = (306 + 9 * (mainThread.previousScore - bestValue)) / 581.0; fallingEval = max(0.5, min(1.5, fallingEval)); // If the best move is stable over several iterations, reduce time // accordingly timeReduction = lastBestMoveDepth + 10 * ONE_PLY < pos->completedDepth ? 1.95 : 1.0; double reduction = pow(mainThread.previousTimeReduction, 0.528) / timeReduction; // Use part of the gained time from a previous stable move for this move double bestMoveInstability = 1.0 + mainThread.bestMoveChanges; if ( rm->size == 1 || time_elapsed() > time_optimum() * fallingEval * reduction * bestMoveInstability) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". if (Limits.ponder) Signals.stopOnPonderhit = 1; else Signals.stop = 1; } } } if (pos->threadIdx != 0) return; mainThread.previousTimeReduction = timeReduction; #if 0 // If skill level is enabled, swap best PV line with the sub-optimal one if (skill.enabled()) std::swap(rm[0], *std::find(rm.begin(), rm.end(), skill.best_move(multiPV))); #endif }
Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { Key key = pos.material_key(); Entry* e = entries[key]; // If e->key matches the position's material hash key, it means that we // have analysed this material configuration before, and we can simply // return the information we found the last time instead of recomputing it. if (e->key == key) return e; std::memset(e, 0, sizeof(Entry)); e->key = key; e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; e->gamePhase = game_phase(pos); // Let's look if we have a specialized evaluation function for this particular // material configuration. Firstly we look for a fixed configuration one, then // for a generic one if the previous search failed. if (endgames.probe(key, e->evaluationFunction)) return e; if (is_KXK<WHITE>(pos)) { e->evaluationFunction = &EvaluateKXK[WHITE]; return e; } if (is_KXK<BLACK>(pos)) { e->evaluationFunction = &EvaluateKXK[BLACK]; return e; } if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN)) { // Minor piece endgame with at least one minor piece per side and // no pawns. Note that the case KmmK is already handled by KXK. assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP))); assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP))); if ( pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE) <= 2 && pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK) <= 2) { e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()]; return e; } } // OK, we didn't find any special evaluation function for the current // material configuration. Is there a suitable scaling function? // // We face problems when there are several conflicting applicable // scaling functions and we need to decide which one to use. EndgameBase<ScaleFactor>* sf; if (endgames.probe(key, sf)) { e->scalingFunction[sf->color()] = sf; return e; } // Generic scaling functions that refer to more then one material // distribution. They should be probed after the specialized ones. // Note that these ones don't return after setting the function. if (is_KBPsKs<WHITE>(pos)) e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; if (is_KBPsKs<BLACK>(pos)) e->scalingFunction[BLACK] = &ScaleKBPsK[BLACK]; if (is_KQKRPs<WHITE>(pos)) e->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE]; else if (is_KQKRPs<BLACK>(pos)) e->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK]; Value npm_w = pos.non_pawn_material(WHITE); Value npm_b = pos.non_pawn_material(BLACK); if (npm_w + npm_b == VALUE_ZERO) { if (!pos.count<PAWN>(BLACK)) { assert(pos.count<PAWN>(WHITE) >= 2); e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; } else if (!pos.count<PAWN>(WHITE)) { assert(pos.count<PAWN>(BLACK) >= 2); e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; } else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1) { // This is a special case because we set scaling functions // for both colors instead of only one. e->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; e->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; } } // No pawns makes it difficult to win, even with a material advantage. This // catches some trivial draws like KK, KBK and KNK if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg) { e->factor[WHITE] = (uint8_t) (npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(WHITE), 2)]); } if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg) { e->factor[BLACK] = (uint8_t) (npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(BLACK), 2)]); } // Compute the space weight if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) { int minorPieceCount = pos.count<KNIGHT>(WHITE) + pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(BLACK) + pos.count<BISHOP>(BLACK); e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0); } // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // for the bishop pair "extended piece", which allows us to be more flexible // in defining bishop pair bonuses. const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = { { pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE), pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) }, { pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK), pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } }; e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16); // Having pawn(s) and ahead at least a piece (npm) ==> Exchange Pieces not Pawns ! if (npm_w >= (npm_b + 3*PawnValueMg) && pos.count<PAWN>(WHITE) && pos.count<PAWN>(WHITE) > pos.count<PAWN>(BLACK) - 3) e->value += (int16_t)((imbalanceWinning<WHITE>(pieceCount) - imbalanceLoosing<BLACK>(pieceCount)) / 16); if (npm_b >= (npm_w + 3*PawnValueMg) && pos.count<PAWN>(BLACK) && pos.count<PAWN>(BLACK) > pos.count<PAWN>(WHITE) - 3) e->value += (int16_t)((imbalanceWinning<BLACK>(pieceCount) - imbalanceLoosing<WHITE>(pieceCount)) / 16); return e; }