X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=0a500c92ebbf4c8ef69c4288ab81638fa6f94e30;hp=540f402e7187bdbe6fbca0a1e9fe3bb81b62d8c5;hb=10e64e05094e75d89baa4495fa867a8a64195bb7;hpb=843a5961e1d9c938e692a115b6bc8774f20d5769 diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 540f402e..0a500c92 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -29,8 +29,6 @@ #include "thread.h" #include "ucioption.h" -Color EvalRootColor; - namespace { // Struct EvalInfo contains various information computed and collected @@ -169,6 +167,9 @@ namespace { // happen in Chess960 games. const Score TrappedBishopA1H1Penalty = make_score(100, 100); + // Penalty for BNR that is not defended by anything + const Score UndefendedPiecePenalty = make_score(25, 10); + // The SpaceMask[Color] contains the area of the board which is considered // by the space evaluation. In the middle game, each side is given a bonus // based on how many squares inside this area are safe and available for @@ -221,8 +222,8 @@ namespace { std::stringstream TraceStream; enum TracedType { - PST = 8, IMBALANCE = 9, MOBILITY = 10, THREAT = 11, - PASSED = 12, UNSTOPPABLE = 13, SPACE = 14, TOTAL = 15 + PST = 8, IMBALANCE = 9, MOBILITY = 10, THREAT = 11, + PASSED = 12, UNSTOPPABLE = 13, SPACE = 14, TOTAL = 15 }; // Function prototypes @@ -254,26 +255,114 @@ namespace { Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight); double to_cp(Value v); void trace_add(int idx, Score term_w, Score term_b = SCORE_ZERO); + void trace_row(const char* name, int idx); } -/// evaluate() is the main evaluation function. It always computes two -/// values, an endgame score and a middle game score, and interpolates -/// between them based on the remaining material. -Value evaluate(const Position& pos, Value& margin) { return do_evaluate(pos, margin); } +namespace Eval { + + Color RootColor; + + /// evaluate() is the main evaluation function. It always computes two + /// values, an endgame score and a middle game score, and interpolates + /// between them based on the remaining material. + + Value evaluate(const Position& pos, Value& margin) { + return do_evaluate(pos, margin); + } + + + /// init() computes evaluation weights from the corresponding UCI parameters + /// and setup king tables. + + 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]); + } + } + + + /// trace() is like evaluate() but instead of a value returns a string suitable + /// to be print on stdout with the detailed descriptions and values of each + /// evaluation term. Used mainly for debugging. + + std::string trace(const Position& pos) { + + Value margin; + std::string totals; + + RootColor = pos.side_to_move(); + + TraceStream.str(""); + TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); + memset(TracedScores, 0, 2 * 16 * sizeof(Score)); + + do_evaluate(pos, margin); + + totals = TraceStream.str(); + TraceStream.str(""); + + TraceStream << std::setw(21) << "Eval term " << "| White | Black | Total \n" + << " | MG EG | MG EG | MG EG \n" + << "---------------------+-------------+-------------+---------------\n"; + + trace_row("Material, PST, Tempo", PST); + trace_row("Material imbalance", IMBALANCE); + trace_row("Pawns", PAWN); + trace_row("Knights", KNIGHT); + trace_row("Bishops", BISHOP); + trace_row("Rooks", ROOK); + trace_row("Queens", QUEEN); + trace_row("Mobility", MOBILITY); + trace_row("King safety", KING); + trace_row("Threats", THREAT); + trace_row("Passed pawns", PASSED); + trace_row("Unstoppable pawns", UNSTOPPABLE); + trace_row("Space", SPACE); + + TraceStream << "---------------------+-------------+-------------+---------------\n"; + trace_row("Total", TOTAL); + TraceStream << totals; + + return TraceStream.str(); + } + +} // namespace Eval + namespace { template Value do_evaluate(const Position& pos, Value& margin) { + assert(!pos.in_check()); + EvalInfo ei; Value margins[2]; Score score, mobilityWhite, mobilityBlack; - assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS); - assert(!pos.in_check()); - // Initialize score by reading the incrementally updated scores included // in the position object (material + piece square tables). score = pos.value(); @@ -387,49 +476,6 @@ Value do_evaluate(const Position& pos, Value& margin) { return pos.side_to_move() == WHITE ? v : -v; } -} // namespace - - -/// eval_init() reads evaluation weights from the corresponding UCI parameters -/// and setup weights and tables. -void eval_init() { - - const Value MaxSlope = Value(30); - const Value Peak = Value(1280); - Value t[100]; - - // King safety is asymmetrical. Our king danger level is weighted by - // "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness". - 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]); - - // 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; - - // First setup the base table - for (int i = 0; i < 100; i++) - { - t[i] = Value(int(0.4 * i * i)); - - if (i > 0) - t[i] = std::min(t[i], t[i - 1] + MaxSlope); - - t[i] = std::min(t[i], Peak); - } - - // Then apply the weights and get the final KingDangerTable[] array - for (Color c = WHITE; c <= BLACK; c++) - for (int i = 0; i < 100; i++) - KingDangerTable[c == WHITE][i] = apply_weight(make_score(t[i], 0), Weights[KingDangerUs + c]); -} - - -namespace { // init_eval_info() initializes king bitboards for given color adding // pawn attacks. To be done at the beginning of the evaluation. @@ -503,9 +549,9 @@ namespace { if (Piece == KNIGHT || Piece == QUEEN) b = pos.attacks_from(s); else if (Piece == BISHOP) - b = attacks_bb(s, pos.occupied_squares() & ~pos.pieces(QUEEN, Us)); + b = attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN, Us)); else if (Piece == ROOK) - b = attacks_bb(s, pos.occupied_squares() & ~pos.pieces(ROOK, QUEEN, Us)); + b = attacks_bb(s, pos.pieces() ^ pos.pieces(ROOK, QUEEN, Us)); else assert(false); @@ -529,7 +575,7 @@ namespace { if ( (Piece == BISHOP || Piece == ROOK || Piece == QUEEN) && (PseudoAttacks[Piece][pos.king_square(Them)] & s)) { - b = BetweenBB[s][pos.king_square(Them)] & pos.occupied_squares(); + b = BetweenBB[s][pos.king_square(Them)] & pos.pieces(); assert(b); @@ -632,15 +678,26 @@ namespace { const Color Them = (Us == WHITE ? BLACK : WHITE); - Bitboard b; + Bitboard b, undefended, undefendedMinors, weakEnemies; Score score = SCORE_ZERO; + // Undefended pieces get penalized even if not under attack + undefended = pos.pieces(Them) & ~ei.attackedBy[Them][0]; + undefendedMinors = undefended & (pos.pieces(BISHOP) | pos.pieces(KNIGHT)); + + if (undefendedMinors) + score += single_bit(undefendedMinors) ? UndefendedPiecePenalty + : UndefendedPiecePenalty * 2; + if (undefended & pos.pieces(ROOK)) + score += UndefendedPiecePenalty; + // Enemy pieces not defended by a pawn and under our attack - Bitboard weakEnemies = pos.pieces(Them) - & ~ei.attackedBy[Them][PAWN] - & ei.attackedBy[Us][0]; + weakEnemies = pos.pieces(Them) + & ~ei.attackedBy[Them][PAWN] + & ei.attackedBy[Us][0]; + if (!weakEnemies) - return SCORE_ZERO; + return score; // Add bonus according to type of attacked enemy piece and to the // type of attacking piece, from knights to queens. Kings are not @@ -694,8 +751,8 @@ namespace { int attackUnits; const Square ksq = pos.king_square(Us); - // King shelter - Score score = ei.pi->king_shelter(pos, ksq); + // King shelter and enemy pawns storm + Score score = ei.pi->king_safety(pos, ksq); // King safety. This is quite complicated, and is almost certainly far // from optimally tuned. @@ -717,7 +774,7 @@ namespace { attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount(undefended)) + InitKingDanger[relative_square(Us, ksq)] - - mg_value(ei.pi->king_shelter(pos, ksq)) / 32; + - mg_value(ei.pi->king_safety(pos, ksq)) / 32; // Analyse enemy's safe queen contact checks. First find undefended // squares around the king attacked by enemy queen... @@ -785,8 +842,8 @@ namespace { // value that will be used for pruning because this value can sometimes // be very big, and so capturing a single attacking piece can therefore // result in a score change far bigger than the value of the captured piece. - score -= KingDangerTable[Us == EvalRootColor][attackUnits]; - margins[Us] += mg_value(KingDangerTable[Us == EvalRootColor][attackUnits]); + score -= KingDangerTable[Us == Eval::RootColor][attackUnits]; + margins[Us] += mg_value(KingDangerTable[Us == Eval::RootColor][attackUnits]); } if (Trace) @@ -933,7 +990,7 @@ namespace { // Opponent king cannot block because path is defended and position // is not in check. So only friendly pieces can be blockers. assert(!pos.in_check()); - assert((queeningPath & pos.occupied_squares()) == (queeningPath & pos.pieces(c))); + assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c))); // Add moves needed to free the path from friendly pieces and retest condition movesToGo += popcount(queeningPath & pos.pieces(c)); @@ -1126,14 +1183,15 @@ namespace { void trace_add(int idx, Score wScore, Score bScore) { - TracedScores[WHITE][idx] = wScore; - TracedScores[BLACK][idx] = bScore; + TracedScores[WHITE][idx] = wScore; + TracedScores[BLACK][idx] = bScore; } + // trace_row() is an helper function used by tracing code to register the // values of a single evaluation term. - void trace_row(const char *name, int idx) { + void trace_row(const char* name, int idx) { Score wScore = TracedScores[WHITE][idx]; Score bScore = TracedScores[BLACK][idx]; @@ -1156,47 +1214,3 @@ namespace { } } } - - -/// trace_evaluate() is like evaluate() but instead of a value returns a string -/// suitable to be print on stdout with the detailed descriptions and values of -/// each evaluation term. Used mainly for debugging. - -std::string trace_evaluate(const Position& pos) { - - Value margin; - std::string totals; - - TraceStream.str(""); - TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); - memset(TracedScores, 0, 2 * 16 * sizeof(Score)); - - do_evaluate(pos, margin); - - totals = TraceStream.str(); - TraceStream.str(""); - - TraceStream << std::setw(21) << "Eval term " << "| White | Black | Total \n" - << " | MG EG | MG EG | MG EG \n" - << "---------------------+-------------+-------------+---------------\n"; - - trace_row("Material, PST, Tempo", PST); - trace_row("Material imbalance", IMBALANCE); - trace_row("Pawns", PAWN); - trace_row("Knights", KNIGHT); - trace_row("Bishops", BISHOP); - trace_row("Rooks", ROOK); - trace_row("Queens", QUEEN); - trace_row("Mobility", MOBILITY); - trace_row("King safety", KING); - trace_row("Threats", THREAT); - trace_row("Passed pawns", PASSED); - trace_row("Unstoppable pawns", UNSTOPPABLE); - trace_row("Space", SPACE); - - TraceStream << "---------------------+-------------+-------------+---------------\n"; - trace_row("Total", TOTAL); - TraceStream << totals; - - return TraceStream.str(); -}