X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=51d9b4290e6d48b3b324de5e6c459d5fd9d824a2;hp=912c05fc8b394942e46a864235dd9c29dda5d8e0;hb=c9dcda6ac488c0058ebd567e1f52e30b8cd0db20;hpb=7a1ff6d8ff39bb9e6844d24467899d47e942486f diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 912c05fc..51d9b429 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,7 +32,7 @@ namespace { enum ExtendedPieceType { // Used for tracing - PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, UNSTOPPABLE, SPACE, TOTAL + PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL }; namespace Tracing { @@ -83,6 +83,8 @@ namespace { // king is on g8 and there's a white knight on g5, this knight adds // 2 to kingAdjacentZoneAttacksCount[BLACK]. int kingAdjacentZoneAttacksCount[COLOR_NB]; + + Bitboard pinnedPieces[COLOR_NB]; }; // Evaluation grain size, must be a power of 2 @@ -125,8 +127,8 @@ namespace { S( 25, 41), S( 25, 41), S(25, 41), S(25, 41) } }; - // Outpost[PieceType][Square] contains bonuses of knights and bishops, indexed - // by piece type and square (from white's point of view). + // Outpost[PieceType][Square] contains bonuses for knights and bishops outposts, + // indexed by piece type and square (from white's point of view). const Value Outpost[][SQUARE_NB] = { { // A B C D E F G H @@ -148,11 +150,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( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT - { S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP - { S(0, 0), S( 0, 22), S(15, 49), S(15, 49), S( 0, 0), S(24, 49) }, // ROOK - { S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN + { 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 }; // ThreatenedByPawn[PieceType] contains a penalty according to which piece @@ -164,7 +163,6 @@ namespace { #undef S const Score Tempo = make_score(24, 11); - const Score BishopPin = make_score(66, 11); const Score RookOn7th = make_score(11, 20); const Score QueenOn7th = make_score( 3, 8); const Score RookOnPawn = make_score(10, 28); @@ -172,17 +170,19 @@ namespace { const Score RookOpenFile = make_score(43, 21); const Score RookSemiopenFile = make_score(19, 10); const Score BishopPawns = make_score( 8, 12); + const Score KnightPawns = make_score( 8, 4); const Score MinorBehindPawn = make_score(16, 0); const Score UndefendedMinor = make_score(25, 10); const Score TrappedRook = make_score(90, 0); + const Score Unstoppable = make_score( 0, 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); - // 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 + // SpaceMask[Color] contains the area of the board which is considered + // by the space evaluation. In the middlegame, each side is given a bonus // based on how many squares inside this area are safe and available for // friendly minor pieces. const Bitboard SpaceMask[] = { @@ -191,33 +191,20 @@ namespace { }; // King danger constants and variables. The king danger scores are taken - // from the KingDanger[]. Various little "meta-bonuses" measuring - // the strength of the enemy attack are added up into an integer, which - // is used as an index to KingDanger[]. + // from KingDanger[]. Various little "meta-bonuses" measuring the strength + // of the enemy attack are added up into an integer, which is used as an + // index to KingDanger[]. // // KingAttackWeights[PieceType] contains king attack weights by piece type const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 }; // Bonuses for enemy's safe checks - const int QueenContactCheck = 6; - const int RookContactCheck = 4; - const int QueenCheck = 3; - const int RookCheck = 2; - const int BishopCheck = 1; - const int KnightCheck = 1; - - // KingExposed[Square] contains penalties based on the position of the - // defending king, indexed by king's square (from white's point of view). - const int KingExposed[] = { - 2, 0, 2, 5, 5, 2, 0, 2, - 2, 2, 4, 8, 8, 4, 2, 2, - 7, 10, 12, 12, 12, 12, 10, 7, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15 - }; + const int QueenContactCheck = 24; + const int RookContactCheck = 16; + const int QueenCheck = 12; + const int RookCheck = 8; + 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. @@ -225,16 +212,16 @@ namespace { // Function prototypes template - Value do_evaluate(const Position& pos, Value& margin); + Value do_evaluate(const Position& pos); template void init_eval_info(const Position& pos, EvalInfo& ei); template - Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility); + Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score* mobility); template - Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]); + Score evaluate_king(const Position& pos, const EvalInfo& ei); template Score evaluate_threats(const Position& pos, const EvalInfo& ei); @@ -245,7 +232,7 @@ namespace { template int evaluate_space(const Position& pos, const EvalInfo& ei); - Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei); + Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei); Value interpolate(const Score& v, Phase ph, ScaleFactor sf); Score apply_weight(Score v, Score w); @@ -257,17 +244,18 @@ namespace { namespace Eval { /// evaluate() is the main evaluation function. It always computes two - /// values, an endgame score and a middle game score, and interpolates + /// values, an endgame score and a middlegame score, and interpolates /// between them based on the remaining material. - Value evaluate(const Position& pos, Value& margin) { - return do_evaluate(pos, margin); + Value evaluate(const Position& pos) { + return do_evaluate(pos); } - /// 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. + /// trace() is like evaluate(), but instead of returning a value, it returns + /// a string (suitable for outputting to stdout) that contains the detailed + /// descriptions and values of each evaluation term. It's mainly used for + /// debugging. std::string trace(const Position& pos) { return Tracing::do_trace(pos); } @@ -288,7 +276,7 @@ namespace Eval { const int MaxSlope = 30; const int Peak = 1280; - for (int t = 0, i = 1; i < 100; i++) + for (int t = 0, i = 1; i < 100; ++i) { t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope)); @@ -303,21 +291,16 @@ namespace Eval { namespace { template -Value do_evaluate(const Position& pos, Value& margin) { +Value do_evaluate(const Position& pos) { assert(!pos.checkers()); EvalInfo ei; - Value margins[COLOR_NB]; - Score score, mobilityWhite, mobilityBlack; + Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO }; Thread* th = pos.this_thread(); - // margins[] store the uncertainty estimation of position's evaluation - // that typically is used by the search for pruning decisions. - margins[WHITE] = margins[BLACK] = VALUE_ZERO; - // Initialize score by reading the incrementally updated scores included - // in the position object (material + piece square tables) and adding + // 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); @@ -328,10 +311,7 @@ Value do_evaluate(const Position& pos, Value& margin) { // If we have a specialized evaluation function for the current material // configuration, call it and return. if (ei.mi->specialized_eval_exists()) - { - margin = VALUE_ZERO; return ei.mi->evaluate(pos); - } // Probe the pawn hash table ei.pi = Pawns::probe(pos, th->pawnsTable); @@ -342,15 +322,15 @@ Value do_evaluate(const Position& pos, Value& margin) { init_eval_info(pos, ei); // Evaluate pieces and mobility - score += evaluate_pieces_of_color(pos, ei, mobilityWhite) - - evaluate_pieces_of_color(pos, ei, mobilityBlack); + score += evaluate_pieces_of_color(pos, ei, mobility) + - evaluate_pieces_of_color(pos, ei, mobility); - score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]); + score += apply_weight(mobility[WHITE] - mobility[BLACK], Weights[Mobility]); // Evaluate kings after all other pieces because we need complete attack // information when computing the king safety evaluation. - score += evaluate_king(pos, ei, margins) - - evaluate_king(pos, ei, margins); + score += evaluate_king(pos, ei) + - evaluate_king(pos, ei); // Evaluate tactical threats, we need full attack information including king score += evaluate_threats(pos, ei) @@ -360,18 +340,19 @@ Value do_evaluate(const Position& pos, Value& margin) { score += evaluate_passed_pawns(pos, ei) - evaluate_passed_pawns(pos, ei); - // If one side has only a king, check whether exists any unstoppable passed pawn + // 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, ei); + score += evaluate_unstoppable_pawns(pos, WHITE, ei) + - evaluate_unstoppable_pawns(pos, BLACK, ei); - // Evaluate space for both sides, only in middle-game. + // Evaluate space for both sides, only in middlegame if (ei.mi->space_weight()) { int s = evaluate_space(pos, ei) - evaluate_space(pos, ei); score += apply_weight(s * ei.mi->space_weight(), Weights[Space]); } - // Scale winning side if position is more drawish that what it appears + // 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); @@ -381,7 +362,8 @@ Value do_evaluate(const Position& pos, Value& margin) { && pos.opposite_bishops() && sf == SCALE_FACTOR_NORMAL) { - // Only the two 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) { @@ -396,7 +378,6 @@ Value do_evaluate(const Position& pos, Value& margin) { sf = ScaleFactor(50); } - margin = margins[pos.side_to_move()]; Value v = interpolate(score, ei.mi->game_phase(), sf); // In case of tracing add all single evaluation contributions for both white and black @@ -405,14 +386,11 @@ Value do_evaluate(const Position& pos, Value& margin) { Tracing::add(PST, pos.psq_score()); Tracing::add(IMBALANCE, ei.mi->material_value()); Tracing::add(PAWN, ei.pi->pawns_value()); - Tracing::add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei)); Score w = ei.mi->space_weight() * evaluate_space(pos, ei); Score b = ei.mi->space_weight() * evaluate_space(pos, ei); Tracing::add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); Tracing::add(TOTAL, score); - Tracing::stream << "\nUncertainty margin: White: " << to_cp(margins[WHITE]) - << ", Black: " << to_cp(margins[BLACK]) - << "\nScaling: " << std::noshowpos + Tracing::stream << "\nScaling: " << std::noshowpos << std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, " << std::setw(6) << 100.0 * (1.0 - ei.mi->game_phase() / 128.0) << "% * " << std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n" @@ -432,6 +410,8 @@ Value do_evaluate(const Position& pos, Value& margin) { const Color Them = (Us == WHITE ? BLACK : WHITE); const Square Down = (Us == WHITE ? DELTA_S : DELTA_N); + ei.pinnedPieces[Us] = pos.pinned_pieces(Us); + Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from(pos.king_square(Them)); ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); @@ -440,14 +420,15 @@ Value do_evaluate(const Position& pos, Value& margin) { { ei.kingRing[Them] = b | shift_bb(b); b &= ei.attackedBy[Us][PAWN]; - ei.kingAttackersCount[Us] = b ? popcount(b) / 2 : 0; + ei.kingAttackersCount[Us] = b ? popcount(b) : 0; ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; - } else + } + else ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0; } - // evaluate_outposts() evaluates bishop and knight outposts squares + // evaluate_outposts() evaluates bishop and knight outpost squares template Score evaluate_outposts(const Position& pos, EvalInfo& ei, Square s) { @@ -460,7 +441,7 @@ Value do_evaluate(const Position& pos, Value& margin) { Value bonus = Outpost[Piece == BISHOP][relative_square(Us, s)]; // Increase bonus if supported by pawn, especially if the opponent has - // no minor piece which can exchange the outpost piece. + // no minor piece which can trade with the outpost piece. if (bonus && (ei.attackedBy[Us][PAWN] & s)) { if ( !pos.pieces(Them, KNIGHT) @@ -469,14 +450,15 @@ Value do_evaluate(const Position& pos, Value& margin) { else bonus += bonus / 2; } + return make_score(bonus, bonus); } - // evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color + // evaluate_pieces() assigns bonuses and penalties to the pieces of a given color template - Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) { + Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, Bitboard mobilityArea) { Bitboard b; Square s; @@ -494,13 +476,16 @@ Value do_evaluate(const Position& pos, Value& margin) { : Piece == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN)) : pos.attacks_from(s); + if (ei.pinnedPieces[Us] & s) + b &= LineBB[pos.king_square(Us)][s]; + ei.attackedBy[Us][Piece] |= b; if (b & ei.kingRing[Them]) { ei.kingAttackersCount[Us]++; ei.kingAttackersWeight[Us] += KingAttackWeights[Piece]; - Bitboard bb = (b & ei.attackedBy[Them][KING]); + Bitboard bb = b & ei.attackedBy[Them][KING]; if (bb) ei.kingAdjacentZoneAttacksCount[Us] += popcount(bb); } @@ -508,24 +493,21 @@ Value do_evaluate(const Position& pos, Value& margin) { int mob = Piece != QUEEN ? popcount(b & mobilityArea) : popcount(b & mobilityArea); - mobility += MobilityBonus[Piece][mob]; + mobility[Us] += MobilityBonus[Piece][mob]; - // Decrease score if we are attacked by an enemy pawn. Remaining part + // Decrease score if we are attacked by an enemy pawn. The remaining part // of threat evaluation must be done later when we have full attack info. if (ei.attackedBy[Them][PAWN] & s) score -= ThreatenedByPawn[Piece]; - // Otherwise give a bonus if we are a bishop and can pin a piece or can - // give a discovered check through an x-ray attack. - else if ( Piece == BISHOP - && (PseudoAttacks[Piece][pos.king_square(Them)] & s) - && !more_than_one(BetweenBB[s][pos.king_square(Them)] & pos.pieces())) - score += BishopPin; - // Penalty for bishop with same coloured pawns if (Piece == BISHOP) score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); + // Penalty for knight when there are few enemy pawns + if (Piece == KNIGHT) + score -= KnightPawns * std::max(5 - pos.count(Them), 0); + if (Piece == BISHOP || Piece == KNIGHT) { // Bishop and knight outposts squares @@ -564,8 +546,8 @@ Value do_evaluate(const Position& pos, Value& margin) { Square ksq = pos.king_square(Us); - // Penalize rooks which are trapped inside a king. Penalize more if - // king has lost right to castle. + // Penalize rooks which are trapped by a king. Penalize more if the + // king has lost its castling capability. 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_on_side(Us, file_of(ksq), file_of(ksq) < FILE_E)) @@ -582,9 +564,9 @@ Value do_evaluate(const Position& pos, Value& margin) { const enum Piece P = make_piece(Us, PAWN); Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); if (pos.piece_on(s + d) == P) - score -= !pos.is_empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 - : pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2 - : TrappedBishopA1H1; + score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 + : pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2 + : TrappedBishopA1H1; } } @@ -595,82 +577,37 @@ Value do_evaluate(const Position& pos, Value& margin) { } - // evaluate_threats<>() assigns bonuses according to the type of attacking piece - // and the type of attacked one. - - template - Score evaluate_threats(const Position& pos, const EvalInfo& ei) { - - const Color Them = (Us == WHITE ? BLACK : WHITE); - - Bitboard b, undefendedMinors, weakEnemies; - Score score = SCORE_ZERO; - - // Undefended minors get penalized even if not under attack - undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT) - & ~ei.attackedBy[Them][ALL_PIECES]; - - if (undefendedMinors) - score += UndefendedMinor; - - // Enemy pieces not defended by a pawn and under our attack - weakEnemies = pos.pieces(Them) - & ~ei.attackedBy[Them][PAWN] - & ei.attackedBy[Us][ALL_PIECES]; - - // Add bonus according to type of attacked enemy piece and to the - // type of attacking piece, from knights to queens. Kings are not - // considered because are already handled in king evaluation. - if (weakEnemies) - for (PieceType pt1 = KNIGHT; pt1 < KING; ++pt1) - { - b = ei.attackedBy[Us][pt1] & weakEnemies; - if (b) - for (PieceType pt2 = PAWN; pt2 < KING; ++pt2) - if (b & pos.pieces(pt2)) - score += Threat[pt1][pt2]; - } - - if (Trace) - Tracing::scores[Us][THREAT] = score; - - return score; - } - - - // evaluate_pieces_of_color<>() assigns bonuses and penalties to all the + // evaluate_pieces_of_color() assigns bonuses and penalties to all the // pieces of a given color. template - Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) { + Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score* mobility) { const Color Them = (Us == WHITE ? BLACK : WHITE); - Score score = mobility = SCORE_ZERO; - // Do not include in mobility squares protected by enemy pawns or occupied by our pieces const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us, PAWN, KING)); - score += evaluate_pieces(pos, ei, mobility, mobilityArea); - score += evaluate_pieces(pos, ei, mobility, mobilityArea); - score += evaluate_pieces(pos, ei, mobility, mobilityArea); - score += evaluate_pieces(pos, ei, mobility, mobilityArea); + Score score = evaluate_pieces(pos, ei, mobility, mobilityArea) + + evaluate_pieces(pos, ei, mobility, mobilityArea) + + evaluate_pieces(pos, ei, mobility, mobilityArea) + + evaluate_pieces(pos, ei, mobility, mobilityArea); - // Sum up all attacked squares - ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] - | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] - | ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING]; + // Sum up all attacked squares (updated in evaluate_pieces) + ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] + | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] + | ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING]; if (Trace) - Tracing::scores[Us][MOBILITY] = apply_weight(mobility, Weights[Mobility]); + Tracing::scores[Us][MOBILITY] = apply_weight(mobility[Us], Weights[Mobility]); return score; } - // evaluate_king<>() assigns bonuses and penalties to a king of a given color + // evaluate_king() assigns bonuses and penalties to a king of a given color template - Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]) { + Score evaluate_king(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -681,34 +618,33 @@ Value do_evaluate(const Position& pos, Value& margin) { // 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. - if ( ei.kingAttackersCount[Them] >= 2 - && ei.kingAdjacentZoneAttacksCount[Them]) + // Main king safety evaluation + if (ei.kingAttackersCount[Them]) { - // Find the attacked squares around the king which has no defenders + // Find the attacked squares around the king which have no defenders // apart from the king itself - undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING]; - undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] + undefended = ei.attackedBy[Them][ALL_PIECES] + & ei.attackedBy[Us][KING] + & ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); // Initialize the 'attackUnits' variable, which is used later on as an // index to the KingDanger[] array. The initial value is based on the // number and types of the enemy's attacking pieces, the number of - // attacked and undefended squares around our king, the square of the - // king, and the quality of the pawn shelter. - attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + // attacked and undefended squares around our king and the quality of + // the pawn shelter (current 'score' value). + attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount(undefended)) - + KingExposed[relative_square(Us, ksq)] - mg_value(score) / 32; - // Analyse enemy's safe queen contact checks. First find undefended - // squares around the king attacked by enemy queen... + // Analyse the enemy's safe queen contact checks. Firstly, find the + // undefended squares around the king that are attacked by the enemy's + // queen... b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them); if (b) { - // ...then remove squares not supported by another enemy piece + // ...and then remove squares not supported by another enemy piece b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); if (b) @@ -717,16 +653,17 @@ Value do_evaluate(const Position& pos, Value& margin) { * (Them == pos.side_to_move() ? 2 : 1); } - // Analyse enemy's safe rook contact checks. First find undefended - // squares around the king attacked by enemy rooks... + // Analyse the enemy's safe rook contact checks. Firstly, find the + // undefended squares around the king that are attacked by the enemy's + // rooks... b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them); - // Consider only squares where the enemy rook gives check + // Consider only squares where the enemy's rook gives check b &= PseudoAttacks[ROOK][ksq]; if (b) { - // ...then remove squares not supported by another enemy piece + // ...and then remove squares not supported by another enemy piece b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); if (b) @@ -735,7 +672,7 @@ Value do_evaluate(const Position& pos, Value& margin) { * (Them == pos.side_to_move() ? 2 : 1); } - // Analyse enemy's safe distance checks for sliders and knights + // Analyse the enemy's safe distance checks for sliders and knights safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]); b1 = pos.attacks_from(ksq) & safe; @@ -765,12 +702,8 @@ Value do_evaluate(const Position& pos, Value& margin) { attackUnits = std::min(99, std::max(0, attackUnits)); // Finally, extract the king danger score from the KingDanger[] - // array and subtract the score from evaluation. Set also margins[] - // 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. + // array and subtract the score from evaluation. score -= KingDanger[Us == Search::RootColor][attackUnits]; - margins[Us] += mg_value(KingDanger[Us == Search::RootColor][attackUnits]); } if (Trace) @@ -780,7 +713,49 @@ Value do_evaluate(const Position& pos, Value& margin) { } - // evaluate_passed_pawns<>() evaluates the passed pawns of the given color + // evaluate_threats() assigns bonuses according to the type of attacking piece + // and the type of attacked one. + + template + Score evaluate_threats(const Position& pos, const EvalInfo& ei) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Bitboard b, undefendedMinors, weakEnemies; + 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]; + + if (undefendedMinors) + score += UndefendedMinor; + + // Enemy pieces not defended by a pawn and under our attack + weakEnemies = pos.pieces(Them) + & ~ei.attackedBy[Them][PAWN] + & 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[0][type_of(pos.piece_on(lsb(b)))]; + + b = weakEnemies & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); + if (b) + score += Threat[1][type_of(pos.piece_on(lsb(b)))]; + } + + if (Trace) + Tracing::scores[Us][THREAT] = score; + + return score; + } + + + // evaluate_passed_pawns() evaluates the passed pawns of the given color template Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) { @@ -796,7 +771,7 @@ Value do_evaluate(const Position& pos, Value& margin) { { Square s = pop_lsb(&b); - assert(pos.pawn_is_passed(Us, s)); + assert(pos.pawn_passed(Us, s)); int r = int(relative_rank(Us, s) - RANK_2); int rr = r * (r - 1); @@ -809,16 +784,16 @@ Value do_evaluate(const Position& pos, Value& margin) { { Square blockSq = s + pawn_push(Us); - // Adjust bonus based on kings proximity - ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr); - ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 2 * rr); + // Adjust bonus based on the king's proximity + ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr) + - Value(square_distance(pos.king_square(Us ), blockSq) * 2 * rr); // If blockSq is not the queening square then consider also a second push if (relative_rank(Us, blockSq) != RANK_8) ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr); - // If the pawn is free to advance, increase bonus - if (pos.is_empty(blockSq)) + // If the pawn is free to advance, then increase the bonus + if (pos.empty(blockSq)) { squaresToQueen = forward_bb(Us, s); @@ -837,12 +812,14 @@ Value do_evaluate(const Position& pos, Value& margin) { else defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES]; - // If there aren't enemy attacks huge bonus, a bit smaller if at - // least block square is not attacked, otherwise smallest bonus. + // If there aren't any enemy attacks, then assign a huge bonus. + // The bonus will be a bit smaller if at least the block square + // isn't attacked, otherwise assign the smallest possible bonus. int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3; - // Big bonus if the path to queen is fully defended, a bit less - // if at least block square is defended. + // Assign a big bonus if the path to the queen is fully defended, + // otherwise assign a bit less of a bonus if at least the block + // square is defended. if (defendedSquares == squaresToQueen) k += 6; @@ -877,9 +854,7 @@ Value do_evaluate(const Position& pos, Value& margin) { ebonus -= ebonus / 4; } - // Increase the bonus if we have more non-pawn pieces - if (pos.count( Us) - pos.count( Us) > - pos.count(Them) - pos.count(Them)) + if (pos.count(Us) < pos.count(Them)) ebonus += ebonus / 4; score += make_score(mbonus, ebonus); @@ -889,165 +864,23 @@ Value do_evaluate(const Position& pos, Value& margin) { if (Trace) Tracing::scores[Us][PASSED] = apply_weight(score, Weights[PassedPawns]); - // Add the scores to the middle game and endgame eval + // Add the scores to the middlegame and endgame eval return apply_weight(score, Weights[PassedPawns]); } - // evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite - // conservative and returns a winning score only when we are very sure that the pawn is winning. - - Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei) { - - Bitboard b, b2, blockers, supporters, queeningPath, candidates; - Square s, blockSq, queeningSquare; - Color c, winnerSide, loserSide; - bool pathDefended, opposed; - int pliesToGo, movesToGo, oppMovesToGo, sacptg, blockersCount, minKingDist, kingptg, d; - int pliesToQueen[] = { 256, 256 }; - - // Step 1. Hunt for unstoppable passed pawns. If we find at least one, - // record how many plies are required for promotion. - for (c = WHITE; c <= BLACK; ++c) - { - // Skip if other side has non-pawn pieces - if (pos.non_pawn_material(~c)) - continue; - - b = ei.pi->passed_pawns(c); - - while (b) - { - s = pop_lsb(&b); - queeningSquare = relative_square(c, file_of(s) | RANK_8); - queeningPath = forward_bb(c, s); - - // Compute plies to queening and check direct advancement - movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2); - oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move()); - pathDefended = ((ei.attackedBy[c][ALL_PIECES] & queeningPath) == queeningPath); - - if (movesToGo >= oppMovesToGo && !pathDefended) - continue; - - // Opponent king cannot block because path is defended and position - // is not in check. So only friendly pieces can be blockers. - assert(!pos.checkers()); - 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)); - - if (movesToGo >= oppMovesToGo && !pathDefended) - continue; - - pliesToGo = 2 * movesToGo - int(c == pos.side_to_move()); - pliesToQueen[c] = std::min(pliesToQueen[c], pliesToGo); - } - } - - // Step 2. If either side cannot promote at least three plies before the other side then situation - // becomes too complex and we give up. Otherwise we determine the possibly "winning side" - if (abs(pliesToQueen[WHITE] - pliesToQueen[BLACK]) < 3) - return SCORE_ZERO; - - winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK); - loserSide = ~winnerSide; - - // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss? - b = candidates = pos.pieces(loserSide, PAWN); + // 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. - while (b) - { - s = pop_lsb(&b); + Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) { - // Compute plies from queening - queeningSquare = relative_square(loserSide, file_of(s) | RANK_8); - movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2); - pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move()); - - // Check if (without even considering any obstacles) we're too far away or doubled - if ( pliesToQueen[winnerSide] + 3 <= pliesToGo - || (forward_bb(loserSide, s) & pos.pieces(loserSide, PAWN))) - candidates ^= s; - } + Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us); - // If any candidate is already a passed pawn it _may_ promote in time. We give up. - if (candidates & ei.pi->passed_pawns(loserSide)) + if (!b || pos.non_pawn_material(~us)) return SCORE_ZERO; - // Step 4. Check new passed pawn creation through king capturing and pawn sacrifices - b = candidates; - - while (b) - { - s = pop_lsb(&b); - sacptg = blockersCount = 0; - minKingDist = kingptg = 256; - - // Compute plies from queening - queeningSquare = relative_square(loserSide, file_of(s) | RANK_8); - movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2); - pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move()); - - // Generate list of blocking pawns and supporters - supporters = adjacent_files_bb(file_of(s)) & candidates; - opposed = forward_bb(loserSide, s) & pos.pieces(winnerSide, PAWN); - blockers = passed_pawn_mask(loserSide, s) & pos.pieces(winnerSide, PAWN); - - assert(blockers); - - // How many plies does it take to remove all the blocking pawns? - while (blockers) - { - blockSq = pop_lsb(&blockers); - movesToGo = 256; - - // Check pawns that can give support to overcome obstacle, for instance - // black pawns: a4, b4 white: b2 then pawn in b4 is giving support. - if (!opposed) - { - b2 = supporters & in_front_bb(winnerSide, rank_of(blockSq + pawn_push(winnerSide))); - - if (b2) - { - d = square_distance(blockSq, backmost_sq(winnerSide, b2)) - 2; - movesToGo = std::min(movesToGo, d); - } - } - - // Check pawns that can be sacrificed against the blocking pawn - b2 = pawn_attack_span(winnerSide, blockSq) & candidates & ~SquareBB[s]; - - if (b2) - { - d = square_distance(blockSq, backmost_sq(winnerSide, b2)) - 2; - movesToGo = std::min(movesToGo, d); - } - - // If obstacle can be destroyed with an immediate pawn exchange / sacrifice, - // it's not a real obstacle and we have nothing to add to pliesToGo. - if (movesToGo <= 0) - continue; - - // Plies needed to sacrifice against all the blocking pawns - sacptg += movesToGo * 2; - blockersCount++; - - // Plies needed for the king to capture all the blocking pawns - d = square_distance(pos.king_square(loserSide), blockSq); - minKingDist = std::min(minKingDist, d); - kingptg = (minKingDist + blockersCount) * 2; - } - - // Check if pawn sacrifice or king capture plan _may_ save the day - if (pliesToQueen[winnerSide] + 3 > pliesToGo + std::min(kingptg, sacptg)) - return SCORE_ZERO; - } - - // Winning pawn is unstoppable and will promote as first, return big score - Score score = make_score(0, (Value) 1280 - 32 * pliesToQueen[winnerSide]); - return winnerSide == WHITE ? score : -score; + return Unstoppable * int(relative_rank(us, frontmost_sq(us, b))); } @@ -1083,7 +916,7 @@ Value do_evaluate(const Position& pos, Value& margin) { } - // interpolate() interpolates between a middle game and an endgame score, + // interpolate() interpolates between a middlegame and an endgame score, // based on game phase. It also scales the return value by a ScaleFactor array. Value interpolate(const Score& v, Phase ph, ScaleFactor sf) { @@ -1132,7 +965,7 @@ Value do_evaluate(const Position& pos, Value& margin) { Score bScore = scores[BLACK][idx]; switch (idx) { - case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL: + case PST: case IMBALANCE: case PAWN: case TOTAL: stream << std::setw(20) << name << " | --- --- | --- --- | " << std::setw(6) << to_cp(mg_value(wScore)) << " " << std::setw(6) << to_cp(eg_value(wScore)) << " \n"; @@ -1155,8 +988,7 @@ Value do_evaluate(const Position& pos, Value& margin) { stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score)); - Value margin; - do_evaluate(pos, margin); + do_evaluate(pos); std::string totals = stream.str(); stream.str(""); @@ -1176,7 +1008,6 @@ Value do_evaluate(const Position& pos, Value& margin) { row("King safety", KING); row("Threats", THREAT); row("Passed pawns", PASSED); - row("Unstoppable pawns", UNSTOPPABLE); row("Space", SPACE); stream << "---------------------+-------------+-------------+---------------\n";