X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=4f99eb870e5fc712ea7121fb6d042407a711c793;hp=c2b94b75365885d53d083ce2badfb6eacb6ebdd6;hb=4758fd31b17129530dfeb81119b423c5bdde704a;hpb=cf50e265faf3a331db775db04954c3feb29fd756 diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c2b94b75..4f99eb87 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -17,10 +17,10 @@ along with this program. If not, see . */ +#include #include #include #include -#include #include "bitcount.h" #include "evaluate.h" @@ -76,7 +76,7 @@ namespace { namespace Tracing { enum Terms { // First 8 entries are for PieceType - PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB + MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB }; Score terms[COLOR_NB][TERMS_NB]; @@ -89,23 +89,15 @@ namespace { std::string do_trace(const Position& pos); } - // Evaluation weights, initialized from UCI options - enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem }; - struct Weight { int mg, eg; } Weights[6]; + // Evaluation weights, indexed by evaluation term + enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety }; + const struct Weight { int mg, eg; } Weights[] = { + {289, 344}, {233, 201}, {221, 273}, {46, 0}, {318, 0} + }; typedef Value V; #define S(mg, eg) make_score(mg, eg) - // Internal evaluation weights. These are applied on top of the evaluation - // weights read from UCI parameters. The purpose is to be able to change - // the evaluation weights while keeping the default values of the UCI - // parameters at 100, which looks prettier. - // - // Values modified by Joona Kiiski - const Score WeightsInternal[] = { - S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(271, 0), S(307, 0) - }; - // MobilityBonus[PieceType][attacked] contains bonuses for middle and end // game, indexed by piece type and number of attacked squares not occupied by // friendly pieces. @@ -155,22 +147,20 @@ namespace { // ThreatenedByPawn[PieceType] contains a penalty according to which piece // type is attacked by an enemy pawn. const Score ThreatenedByPawn[] = { - S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118) + S(0, 0), S(0, 0), S(80, 119), S(80, 119), S(117, 199), S(127, 218) }; - // Hanging[side to move] contains a bonus for each enemy hanging piece - const Score Hanging[2] = { S(23, 20) , S(35, 45) }; + // Hanging contains a bonus for each enemy hanging piece + const Score Hanging = S(23, 20); #undef S - const Score Tempo = make_score(24, 11); const Score RookOnPawn = make_score(10, 28); const Score RookOpenFile = make_score(43, 21); const Score RookSemiopenFile = make_score(19, 10); const Score BishopPawns = make_score( 8, 12); const Score MinorBehindPawn = make_score(16, 0); - const Score UndefendedMinor = make_score(25, 10); - const Score TrappedRook = make_score(90, 0); + const Score TrappedRook = make_score(92, 0); const Score Unstoppable = make_score( 0, 20); // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by @@ -203,10 +193,11 @@ namespace { const int BishopCheck = 2; const int KnightCheck = 3; - // KingDanger[Color][attackUnits] contains the actual king danger weighted - // scores, indexed by color and by a calculated integer number. - Score KingDanger[COLOR_NB][128]; + // KingDanger[attackUnits] contains the actual king danger weighted + // scores, indexed by a calculated integer number. + Score KingDanger[128]; + const int ScalePawnSpan[2] = { 38, 56 }; // apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow Score apply_weight(Score v, const Weight& w) { @@ -214,17 +205,6 @@ namespace { } - // weight_option() computes the value of an evaluation weight, by combining - // two UCI-configurable weights (midgame and endgame) with an internal weight. - - Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) { - - Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100, - Options[egOpt] * eg_value(internalWeight) / 100 }; - return w; - } - - // init_eval_info() initializes king bitboards for given color adding // pawn attacks. To be done at the beginning of the evaluation. @@ -252,10 +232,10 @@ namespace { } - // evaluate_outposts() evaluates bishop and knight outpost squares + // evaluate_outpost() evaluates bishop and knight outpost squares template - Score evaluate_outposts(const Position& pos, EvalInfo& ei, Square s) { + Score evaluate_outpost(const Position& pos, const EvalInfo& ei, Square s) { const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -275,7 +255,7 @@ namespace { bonus += bonus / 2; } - return make_score(bonus, bonus); + return make_score(bonus * 2, bonus / 2); } @@ -336,9 +316,9 @@ namespace { if (Pt == BISHOP) score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); - // Bishop and knight outposts squares + // Bishop and knight outpost square if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s))) - score += evaluate_outposts(pos, ei, s); + score += evaluate_outpost(pos, ei, s); // Bishop or knight behind a pawn if ( relative_rank(Us, s) < RANK_5 @@ -370,7 +350,7 @@ namespace { if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq))) && (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1) && !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq))) - score -= (TrappedRook - make_score(mob * 8, 0)) * (1 + !pos.can_castle(Us)); + score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us)); } // An important Chess960 pattern: A cornered bishop blocked by a friendly @@ -446,9 +426,7 @@ namespace { | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); if (b) - attackUnits += QueenContactCheck - * popcount(b) - * (Them == pos.side_to_move() ? 2 : 1); + attackUnits += QueenContactCheck * popcount(b); } // Analyse the enemy's safe rook contact checks. Firstly, find the @@ -466,9 +444,7 @@ namespace { | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); if (b) - attackUnits += RookContactCheck - * popcount(b) - * (Them == pos.side_to_move() ? 2 : 1); + attackUnits += RookContactCheck * popcount(b); } // Analyse the enemy's safe distance checks for sliders and knights @@ -502,7 +478,7 @@ namespace { // Finally, extract the king danger score from the KingDanger[] // array and subtract the score from evaluation. - score -= KingDanger[Us == Search::RootColor][attackUnits]; + score -= KingDanger[attackUnits]; } if (Trace) @@ -520,15 +496,16 @@ namespace { const Color Them = (Us == WHITE ? BLACK : WHITE); - Bitboard b, undefendedMinors, weakEnemies; + Bitboard b, weakEnemies, protectedEnemies; Score score = SCORE_ZERO; - // Undefended minors get penalized even if they are not under attack - undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT) - & ~ei.attackedBy[Them][ALL_PIECES]; + // Protected enemies + protectedEnemies = (pos.pieces(Them) ^ pos.pieces(Them,PAWN)) + & ei.attackedBy[Them][PAWN] + & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); - if (undefendedMinors) - score += UndefendedMinor; + if(protectedEnemies) + score += Threat[0][type_of(pos.piece_on(lsb(protectedEnemies)))]; // Enemies not defended by a pawn and under our attack weakEnemies = pos.pieces(Them) @@ -538,7 +515,7 @@ namespace { // Add a bonus according if the attacking pieces are minor or major if (weakEnemies) { - b = weakEnemies & (ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); + b = weakEnemies & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); if (b) score += Threat[0][type_of(pos.piece_on(lsb(b)))]; @@ -548,8 +525,7 @@ namespace { b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES]; if (b) - score += more_than_one(b) ? Hanging[Us != pos.side_to_move()] * popcount(b) - : Hanging[Us == pos.side_to_move()]; + score += more_than_one(b) ? Hanging * popcount(b) : Hanging; } if (Trace) @@ -598,22 +574,18 @@ namespace { // If the pawn is free to advance, then increase the bonus if (pos.empty(blockSq)) { - squaresToQueen = forward_bb(Us, s); - - // If there is an enemy rook or queen attacking the pawn from behind, - // add all X-ray attacks by the rook or queen. Otherwise consider only - // the squares in the pawn's path attacked or occupied by the enemy. - if ( unlikely(forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN)) - && (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from(s))) - unsafeSquares = squaresToQueen; - else - unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them)); - - if ( unlikely(forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN)) - && (forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN) & pos.attacks_from(s))) - defendedSquares = squaresToQueen; - else - defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES]; + // If there is a rook or queen attacking/defending the pawn from behind, + // consider all the squaresToQueen. Otherwise consider only the squares + // in the pawn's path attacked or occupied by the enemy. + defendedSquares = unsafeSquares = squaresToQueen = forward_bb(Us, s); + + Bitboard bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from(s); + + if (!(pos.pieces(Us) & bb)) + defendedSquares &= ei.attackedBy[Us][ALL_PIECES]; + + if (!(pos.pieces(Them) & bb)) + unsafeSquares &= ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them); // If there aren't any enemy attacks, assign a big bonus. Otherwise // assign a smaller bonus if the block square isn't attacked. @@ -629,6 +601,8 @@ namespace { mbonus += k * rr, ebonus += k * rr; } + else if(pos.pieces(Us) & blockSq) + mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2; } // rr != 0 if (pos.count(Us) < pos.count(Them)) @@ -646,17 +620,14 @@ namespace { // evaluate_unstoppable_pawns() scores the most advanced among the passed and - // candidate pawns. In case opponent has no pieces but pawns, this is somewhat - // related to the possibility that pawns are unstoppable. + // candidate pawns. In case both players have no pieces but pawns, this is + // somewhat related to the possibility that pawns are unstoppable. - Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) { + Score evaluate_unstoppable_pawns(Color us, const EvalInfo& ei) { Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us); - if (!b || pos.non_pawn_material(~us)) - return SCORE_ZERO; - - return Unstoppable * int(relative_rank(us, frontmost_sq(us, b))); + return b ? Unstoppable * int(relative_rank(us, frontmost_sq(us, b))) : SCORE_ZERO; } @@ -704,9 +675,9 @@ namespace { Thread* thisThread = pos.this_thread(); // Initialize score by reading the incrementally updated scores included - // in the position object (material + piece square tables) and adding a - // Tempo bonus. Score is computed from the point of view of white. - score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo); + // in the position object (material + piece square tables). + // Score is computed from the point of view of white. + score = pos.psq_score(); // Probe the material hash table ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames); @@ -749,10 +720,10 @@ namespace { score += evaluate_passed_pawns(pos, ei) - evaluate_passed_pawns(pos, ei); - // If one side has only a king, score for potential unstoppable pawns - if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) - score += evaluate_unstoppable_pawns(pos, WHITE, ei) - - evaluate_unstoppable_pawns(pos, BLACK, ei); + // If both sides have only pawns, score for potential unstoppable pawns + if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK)) + score += evaluate_unstoppable_pawns(WHITE, ei) + - evaluate_unstoppable_pawns(BLACK, ei); // Evaluate space for both sides, only in middlegame if (ei.mi->space_weight()) @@ -762,29 +733,35 @@ namespace { } // Scale winning side if position is more drawish than it appears - ScaleFactor sf = eg_value(score) > VALUE_DRAW ? ei.mi->scale_factor(pos, WHITE) - : ei.mi->scale_factor(pos, BLACK); + Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK; + ScaleFactor sf = ei.mi->scale_factor(pos, strongSide); - // If we don't already have an unusual scale factor, check for opposite - // colored bishop endgames, and use a lower scale for those. + // If we don't already have an unusual scale factor, check for certain + // types of endgames, and use a lower scale for those. if ( ei.mi->game_phase() < PHASE_MIDGAME - && pos.opposite_bishops() && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) { - // Ignoring any pawns, do both sides only have a single bishop and no - // other pieces? - if ( pos.non_pawn_material(WHITE) == BishopValueMg - && pos.non_pawn_material(BLACK) == BishopValueMg) - { - // Check for KBP vs KB with only a single pawn that is almost - // certainly a draw or at least two pawns. - bool one_pawn = (pos.count(WHITE) + pos.count(BLACK) == 1); - sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); + if (pos.opposite_bishops()) { + // Ignoring any pawns, do both sides only have a single bishop and no + // other pieces? + if ( pos.non_pawn_material(WHITE) == BishopValueMg + && pos.non_pawn_material(BLACK) == BishopValueMg) + { + // Check for KBP vs KB with only a single pawn that is almost + // certainly a draw or at least two pawns. + bool one_pawn = (pos.count(WHITE) + pos.count(BLACK) == 1); + sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); + } + else + // Endgame with opposite-colored bishops, but also other pieces. Still + // a bit drawish, but not as drawish as with only the two bishops. + sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL); + } else if ( abs(eg_value(score)) <= BishopValueEg + && ei.pi->pawn_span(strongSide) <= 1 + && !pos.pawn_passed(~strongSide, pos.king_square(~strongSide))) { + // Endings where weaker side can be place his king in front of the opponent's pawns are drawish. + sf = ScaleFactor(ScalePawnSpan[ei.pi->pawn_span(strongSide)]); } - else - // Endgame with opposite-colored bishops, but also other pieces. Still - // a bit drawish, but not as drawish as with only the two bishops. - sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL); } // Interpolate between a middlegame and a (scaled by 'sf') endgame score @@ -796,7 +773,7 @@ namespace { // In case of tracing add all single evaluation contributions for both white and black if (Trace) { - Tracing::add_term(Tracing::PST, pos.psq_score()); + Tracing::add_term(Tracing::MATERIAL, pos.psq_score()); Tracing::add_term(Tracing::IMBALANCE, ei.mi->material_value()); Tracing::add_term(PAWN, ei.pi->pawns_value()); Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility]) @@ -829,13 +806,13 @@ namespace { Score bScore = terms[BLACK][idx]; switch (idx) { - case PST: case IMBALANCE: case PAWN: case TOTAL: - ss << std::setw(20) << name << " | --- --- | --- --- | " + case MATERIAL: case IMBALANCE: case PAWN: case TOTAL: + ss << std::setw(15) << name << " | --- --- | --- --- | " << std::setw(5) << to_cp(mg_value(wScore - bScore)) << " " << std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n"; break; default: - ss << std::setw(20) << name << " | " << std::noshowpos + ss << std::setw(15) << name << " | " << std::noshowpos << std::setw(5) << to_cp(mg_value(wScore)) << " " << std::setw(5) << to_cp(eg_value(wScore)) << " | " << std::setw(5) << to_cp(mg_value(bScore)) << " " @@ -854,12 +831,12 @@ namespace { std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) - << " Eval term | White | Black | Total \n" - << " | MG EG | MG EG | MG EG \n" - << "---------------------+-------------+-------------+-------------\n"; + << " Eval term | White | Black | Total \n" + << " | MG EG | MG EG | MG EG \n" + << "----------------+-------------+-------------+-------------\n"; - format_row(ss, "Material, PST, Tempo", PST); - format_row(ss, "Material imbalance", IMBALANCE); + format_row(ss, "Material", MATERIAL); + format_row(ss, "Imbalance", IMBALANCE); format_row(ss, "Pawns", PAWN); format_row(ss, "Knights", KNIGHT); format_row(ss, "Bishops", BISHOP); @@ -871,7 +848,7 @@ namespace { format_row(ss, "Passed pawns", PASSED); format_row(ss, "Space", SPACE); - ss << "---------------------+-------------+-------------+-------------\n"; + ss << "----------------+-------------+-------------+-------------\n"; format_row(ss, "Total", TOTAL); ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; @@ -888,7 +865,7 @@ namespace Eval { /// of the position always from the point of view of the side to move. Value evaluate(const Position& pos) { - return do_evaluate(pos); + return do_evaluate(pos) + Tempo; } @@ -906,22 +883,13 @@ namespace Eval { 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]); + KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]); } }