X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=2161bf94c07ddf1ee53af1d0c9116a421698a4bc;hp=9a55fbd88789a5096731061fb66d4109591cf8b6;hb=42a20920e5259dbe3efd9002fbc7176a9f071636;hpb=5adc678628ceaec3695f12873db633626753ea5a diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9a55fbd8..2161bf94 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -27,7 +27,6 @@ #include "material.h" #include "pawns.h" #include "thread.h" -#include "ucioption.h" namespace { @@ -79,13 +78,14 @@ namespace { MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB }; - Score terms[COLOR_NB][TERMS_NB]; + Score scores[COLOR_NB][TERMS_NB]; EvalInfo ei; ScaleFactor sf; double to_cp(Value v); - void add_term(int idx, Score term_w, Score term_b = SCORE_ZERO); - void format_row(std::stringstream& ss, const char* name, int idx); + void write(int idx, Color c, Score s); + void write(int idx, Score w, Score b = SCORE_ZERO); + void print(std::stringstream& ss, const char* name, int idx); std::string do_trace(const Position& pos); } @@ -140,8 +140,8 @@ namespace { // Threat[attacking][attacked] contains bonuses according to which piece // type attacks which one. const Score Threat[][PIECE_TYPE_NB] = { - { S(0, 0), S( 7, 39), S(24, 49), S(24, 49), S(41,100), S(41,100) }, // Minor - { S(0, 0), S(15, 39), S(15, 45), S(15, 45), S(15, 45), S(24, 49) } // Major + { S(0, 0), S(0, 38), S(32, 45), S(32, 45), S(41,100), S(35,104) }, // Minor + { S(0, 0), S(7, 28), S(20, 49), S(20, 49), S(8 , 42), S(23, 44) } // Major }; // ThreatenedByPawn[PieceType] contains a penalty according to which piece @@ -150,23 +150,24 @@ namespace { S(0, 0), S(0, 0), S(80, 119), S(80, 119), S(117, 199), S(127, 218) }; - // Hanging contains a bonus for each enemy hanging piece - const Score Hanging = S(23, 20); - - #undef S - - 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 TrappedRook = make_score(92, 0); - const Score Unstoppable = make_score( 0, 20); + // Assorted bonuses and penalties used by evaluation + const Score KingOnOne = S(2 , 58); + const Score KingOnMany = S(6 ,125); + const Score RookOnPawn = S(10, 28); + const Score RookOpenFile = S(43, 21); + const Score RookSemiOpenFile = S(19, 10); + const Score BishopPawns = S( 8, 12); + const Score MinorBehindPawn = S(16, 0); + const Score TrappedRook = S(92, 0); + const Score Unstoppable = S( 0, 20); + const Score Hanging = S(23, 20); // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only // happen in Chess960 games. - const Score TrappedBishopA1H1 = make_score(50, 50); + const Score TrappedBishopA1H1 = S(50, 50); + + #undef S // SpaceMask[Color] contains the area of the board which is considered // by the space evaluation. In the middlegame, each side is given a bonus @@ -197,8 +198,6 @@ namespace { // 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) { return make_score(mg_value(v) * w.mg / 256, eg_value(v) * w.eg / 256); @@ -220,7 +219,7 @@ namespace { ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); // Init king safety tables only if we are going to use them - if (pos.count(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg) + if (pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg) { ei.kingRing[Them] = b | shift_bb(b); b &= ei.attackedBy[Us][PAWN]; @@ -338,7 +337,7 @@ namespace { // Give a bonus for a rook on a open or semi-open file if (ei.pi->semiopen_file(Us, file_of(s))) - score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOpenFile : RookSemiopenFile; + score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOpenFile : RookSemiOpenFile; if (mob > 3 || ei.pi->semiopen_file(Us, file_of(s))) continue; @@ -369,7 +368,7 @@ namespace { } if (Trace) - Tracing::terms[Us][Pt] = score; + Tracing::write(Pt, Us, score); return score - evaluate_pieces(pos, ei, mobility, mobilityArea); } @@ -413,7 +412,8 @@ namespace { attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount(undefended)) + 2 * (ei.pinnedPieces[Us] != 0) - - mg_value(score) / 32; + - mg_value(score) / 32 + - !pos.count(Them) * 15; // Analyse the enemy's safe queen contact checks. Firstly, find the // undefended squares around the king that are attacked by the enemy's @@ -482,12 +482,28 @@ namespace { } if (Trace) - Tracing::terms[Us][KING] = score; + Tracing::write(KING, Us, score); return score; } + // max_piece_type() is a helper function used by evaluate_threats() to get + // the value of the biggest PieceType of color C in 'target' bitboard. + + template + inline PieceType max_piece_type(const Position& pos, const Bitboard target) { + + assert(target & (pos.pieces(C) ^ pos.pieces(C, KING))); + + for (PieceType pt = QUEEN; pt > PAWN; --pt) + if (target & pos.pieces(C, pt)) + return pt; + + return PAWN; + } + + // evaluate_threats() assigns bonuses according to the type of attacking piece // and the type of attacked one. @@ -496,41 +512,46 @@ namespace { const Color Them = (Us == WHITE ? BLACK : WHITE); + enum { Minor, Major }; + Bitboard b, weakEnemies, protectedEnemies; Score score = SCORE_ZERO; - enum { Minor, Major }; - // Protected enemies - protectedEnemies = (pos.pieces(Them) ^ pos.pieces(Them,PAWN)) - & ei.attackedBy[Them][PAWN] + // Enemies defended by a pawn and under our attack by a minor piece + protectedEnemies = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) + & ei.attackedBy[Them][PAWN] & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); - if(protectedEnemies) - score += Threat[Minor][type_of(pos.piece_on(lsb(protectedEnemies)))]; + if (protectedEnemies) + score += Threat[Minor][max_piece_type(pos, protectedEnemies)]; // Enemies not defended by a pawn and under our attack - weakEnemies = pos.pieces(Them) + weakEnemies = pos.pieces(Them) & ~ei.attackedBy[Them][PAWN] - & ei.attackedBy[Us][ALL_PIECES]; + & ei.attackedBy[Us][ALL_PIECES]; // Add a bonus according if the attacking pieces are minor or major if (weakEnemies) { b = weakEnemies & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); if (b) - score += Threat[Minor][type_of(pos.piece_on(lsb(b)))]; + score += Threat[Minor][max_piece_type(pos, b)]; b = weakEnemies & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); if (b) - score += Threat[Major][type_of(pos.piece_on(lsb(b)))]; + score += Threat[Major][max_piece_type(pos, b)]; b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES]; if (b) score += more_than_one(b) ? Hanging * popcount(b) : Hanging; + + b = weakEnemies & ei.attackedBy[Us][KING]; + if (b) + score += more_than_one(b) ? KingOnMany : KingOnOne; } if (Trace) - Tracing::terms[Us][Tracing::THREAT] = score; + Tracing::write(Tracing::THREAT, Us, score); return score; } @@ -602,7 +623,7 @@ namespace { mbonus += k * rr, ebonus += k * rr; } - else if(pos.pieces(Us) & blockSq) + else if (pos.pieces(Us) & blockSq) mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2; } // rr != 0 @@ -613,20 +634,20 @@ namespace { } if (Trace) - Tracing::terms[Us][Tracing::PASSED] = apply_weight(score, Weights[PassedPawns]); + Tracing::write(Tracing::PASSED, Us, apply_weight(score, Weights[PassedPawns])); // Add the scores to the middlegame and endgame eval return apply_weight(score, Weights[PassedPawns]); } - // evaluate_unstoppable_pawns() scores the most advanced among the passed and - // candidate pawns. In case both players have no pieces but pawns, this is - // somewhat related to the possibility that pawns are unstoppable. + // evaluate_unstoppable_pawns() scores the most advanced passed pawn. In case + // both players have no pieces but pawns, this is somewhat related to the + // possibility that pawns are unstoppable. Score evaluate_unstoppable_pawns(Color us, const EvalInfo& ei) { - Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us); + Bitboard b = ei.pi->passed_pawns(us); return b ? Unstoppable * int(relative_rank(us, frontmost_sq(us, b))) : SCORE_ZERO; } @@ -687,7 +708,7 @@ namespace { // If we have a specialized evaluation function for the current material // configuration, call it and return. if (ei.mi->specialized_eval_exists()) - return ei.mi->evaluate(pos); + return ei.mi->evaluate(pos) + Eval::Tempo; // Probe the pawn hash table ei.pi = Pawns::probe(pos, thisThread->pawnsTable); @@ -742,27 +763,25 @@ namespace { if ( ei.mi->game_phase() < PHASE_MIDGAME && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) { - if (pos.opposite_bishops()) { - // Ignoring any pawns, do both sides only have a single bishop and no - // other pieces? + if (pos.opposite_bishops()) + { + // Endgame with opposite-colored bishops and no other pieces (ignoring pawns) + // is almost a draw, in case of KBP vs KB is even more a draw. 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); - } + sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(32) : ScaleFactor(8); + + // Endgame with opposite-colored bishops, but also other pieces. Still + // a bit drawish, but not as drawish as with only the two bishops. 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)]); } + // Endings where weaker side can place his king in front of the opponent's + // pawns are drawish. + else if ( abs(eg_value(score)) <= BishopValueEg + && ei.pi->pawn_span(strongSide) <= 1 + && !pos.pawn_passed(~strongSide, pos.king_square(~strongSide))) + sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(56) : ScaleFactor(38); } // Interpolate between a middlegame and a (scaled by 'sf') endgame score @@ -774,20 +793,20 @@ namespace { // In case of tracing add all single evaluation contributions for both white and black if (Trace) { - 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]) - , apply_weight(mobility[BLACK], Weights[Mobility])); + Tracing::write(Tracing::MATERIAL, pos.psq_score()); + Tracing::write(Tracing::IMBALANCE, ei.mi->material_value()); + Tracing::write(PAWN, ei.pi->pawns_value()); + Tracing::write(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility]) + , apply_weight(mobility[BLACK], Weights[Mobility])); Score w = ei.mi->space_weight() * evaluate_space(pos, ei); Score b = ei.mi->space_weight() * evaluate_space(pos, ei); - Tracing::add_term(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); - Tracing::add_term(Tracing::TOTAL, score); + Tracing::write(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); + Tracing::write(Tracing::TOTAL, score); Tracing::ei = ei; Tracing::sf = sf; } - return pos.side_to_move() == WHITE ? v : -v; + return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; } @@ -795,16 +814,18 @@ namespace { double Tracing::to_cp(Value v) { return double(v) / PawnValueEg; } - void Tracing::add_term(int idx, Score wScore, Score bScore) { + void Tracing::write(int idx, Color c, Score s) { scores[c][idx] = s; } + + void Tracing::write(int idx, Score w, Score b) { - terms[WHITE][idx] = wScore; - terms[BLACK][idx] = bScore; + write(idx, WHITE, w); + write(idx, BLACK, b); } - void Tracing::format_row(std::stringstream& ss, const char* name, int idx) { + void Tracing::print(std::stringstream& ss, const char* name, int idx) { - Score wScore = terms[WHITE][idx]; - Score bScore = terms[BLACK][idx]; + Score wScore = scores[WHITE][idx]; + Score bScore = scores[BLACK][idx]; switch (idx) { case MATERIAL: case IMBALANCE: case PAWN: case TOTAL: @@ -825,7 +846,7 @@ namespace { std::string Tracing::do_trace(const Position& pos) { - std::memset(terms, 0, sizeof(terms)); + std::memset(scores, 0, sizeof(scores)); Value v = do_evaluate(pos); v = pos.side_to_move() == WHITE ? v : -v; // White's point of view @@ -836,21 +857,21 @@ namespace { << " | MG EG | MG EG | MG EG \n" << "----------------+-------------+-------------+-------------\n"; - 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); - format_row(ss, "Rooks", ROOK); - format_row(ss, "Queens", QUEEN); - format_row(ss, "Mobility", MOBILITY); - format_row(ss, "King safety", KING); - format_row(ss, "Threats", THREAT); - format_row(ss, "Passed pawns", PASSED); - format_row(ss, "Space", SPACE); + print(ss, "Material", MATERIAL); + print(ss, "Imbalance", IMBALANCE); + print(ss, "Pawns", PAWN); + print(ss, "Knights", KNIGHT); + print(ss, "Bishops", BISHOP); + print(ss, "Rooks", ROOK); + print(ss, "Queens", QUEEN); + print(ss, "Mobility", MOBILITY); + print(ss, "King safety", KING); + print(ss, "Threats", THREAT); + print(ss, "Passed pawns", PASSED); + print(ss, "Space", SPACE); ss << "----------------+-------------+-------------+-------------\n"; - format_row(ss, "Total", TOTAL); + print(ss, "Total", TOTAL); ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; @@ -866,7 +887,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) + Tempo; + return do_evaluate(pos); } @@ -879,8 +900,7 @@ namespace Eval { } - /// init() computes evaluation weights from the corresponding UCI parameters - /// and setup king tables. + /// init() computes evaluation weights. void init() {