X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=615df1bacd1a194510930306a29a85fad7e3a00d;hp=8e8cc0912e57ddd4e37c12cb579631eaee9de289;hb=69d3be42a112645a9e599df615f730d61a5dca8c;hpb=d40d04c17ceadff6f15d1cb1d4d469f823a35a02 diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8e8cc091..615df1ba 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -35,7 +35,7 @@ namespace Trace { enum Tracing { NO_TRACE, TRACE }; enum Term { // The first 8 entries are reserved for PieceType - MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB + MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, WINNABLE, TOTAL, TERM_NB }; Score scores[TERM_NB][COLOR_NB]; @@ -59,7 +59,7 @@ namespace Trace { std::ostream& operator<<(std::ostream& os, Term t) { - if (t == MATERIAL || t == IMBALANCE || t == INITIATIVE || t == TOTAL) + if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL) os << " ---- ----" << " | " << " ---- ----"; else os << scores[t][WHITE] << " | " << scores[t][BLACK]; @@ -127,31 +127,34 @@ namespace { }; // Assorted bonuses and penalties + constexpr Score BishopKingProtector = S( 6, 9); + constexpr Score BishopOnKingRing = S( 24, 0); + constexpr Score BishopOutpost = S( 30, 23); constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); - constexpr Score BishopKingProtector = S( 6, 9); constexpr Score KnightKingProtector = S( 8, 9); constexpr Score KnightOnQueen = S( 16, 11); + constexpr Score KnightOutpost = S( 56, 36); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score KnightOutpost = S( 56, 36); - constexpr Score BishopOutpost = S( 30, 23); - constexpr Score ReachableOutpost = S( 31, 22); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); + constexpr Score QueenInfiltration = S( -2, 14); + constexpr Score ReachableOutpost = S( 31, 22); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnKingRing = S( 16, 0); - constexpr Score RookOnQueenFile = S( 5, 9); - constexpr Score SliderOnQueen = S( 59, 18); + constexpr Score RookOnQueenFile = S( 6, 11); + constexpr Score SliderOnQueen = S( 60, 18); constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score TrappedRook = S( 55, 13); - constexpr Score WeakQueen = S( 51, 14); - constexpr Score WeakQueenProtection = S( 15, 0); + constexpr Score WeakQueenProtection = S( 14, 0); + constexpr Score WeakQueen = S( 56, 15); + #undef S @@ -172,8 +175,7 @@ namespace { template Score threats() const; template Score passed() const; template Score space() const; - ScaleFactor scale_factor(Value eg) const; - Score initiative(Score score) const; + Value winnable(Score score) const; const Position& pos; Material::Entry* me; @@ -215,6 +217,7 @@ namespace { // Evaluation::initialize() computes king and pawn attacks, and the king ring // bitboard for a given color. This is done at the beginning of the evaluation. + template template void Evaluation::initialize() { @@ -235,7 +238,7 @@ namespace { mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them)); // Initialize attackedBy[] for king and pawns - attackedBy[Us][KING] = pos.attacks_from(ksq); + attackedBy[Us][KING] = attacks_bb(ksq); attackedBy[Us][PAWN] = pe->pawn_attacks(Us); attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); @@ -243,7 +246,7 @@ namespace { // Init our king safety tables Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G), Utility::clamp(rank_of(ksq), RANK_2, RANK_7)); - kingRing[Us] = PseudoAttacks[KING][s] | s; + kingRing[Us] = attacks_bb(s) | s; kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; @@ -254,6 +257,7 @@ namespace { // Evaluation::pieces() scores pieces of a given color and type + template template Score Evaluation::pieces() { @@ -273,10 +277,10 @@ namespace { // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN)) : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK)) - : pos.attacks_from(s); + : attacks_bb(s, pos.pieces()); if (pos.blockers_for_king(Us) & s) - b &= LineBB[pos.square(Us)][s]; + b &= line_bb(pos.square(Us), s); attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b; attackedBy[Us][Pt] |= b; @@ -288,9 +292,13 @@ namespace { kingAttackersWeight[Us] += KingAttackWeights[Pt]; kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]); } + else if (Pt == ROOK && (file_bb(s) & kingRing[Them])) score += RookOnKingRing; + else if (Pt == BISHOP && (attacks_bb(s, pos.pieces(PAWN)) & kingRing[Them])) + score += BishopOnKingRing; + int mob = popcount(b & mobilityArea[Us]); mobility[Us] += MobilityBonus[Pt - 2][mob]; @@ -323,7 +331,7 @@ namespace { * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles)); // Penalty for all enemy pawns x-rayed - score -= BishopXRayPawns * popcount(PseudoAttacks[BISHOP][s] & pos.pieces(Them, PAWN)); + score -= BishopXRayPawns * popcount(attacks_bb(s) & pos.pieces(Them, PAWN)); // Bonus for bishop on a long diagonal which can "see" both center squares if (more_than_one(attacks_bb(s, pos.pieces(PAWN)) & Center)) @@ -369,6 +377,10 @@ namespace { Bitboard queenPinners; if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners)) score -= WeakQueen; + + // Bonus for queen on weak square in enemy camp + if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s)) + score += QueenInfiltration; } } if (T) @@ -379,6 +391,7 @@ namespace { // Evaluation::king() assigns bonuses and penalties to a king of a given color + template template Score Evaluation::king() const { @@ -438,7 +451,7 @@ namespace { unsafeChecks |= b2 & attackedBy[Them][BISHOP]; // Enemy knights checks - knightChecks = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; + knightChecks = attacks_bb(ksq) & attackedBy[Them][KNIGHT]; if (knightChecks & safe) kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 162/100 : KnightSafeCheck; @@ -487,6 +500,7 @@ namespace { // Evaluation::threats() assigns bonuses according to the types of the // attacking and the attacked pieces. + template template Score Evaluation::threats() const { @@ -564,12 +578,12 @@ namespace { Square s = pos.square(Them); safe = mobilityArea[Us] & ~stronglyProtected; - b = attackedBy[Us][KNIGHT] & pos.attacks_from(s); + b = attackedBy[Us][KNIGHT] & attacks_bb(s); score += KnightOnQueen * popcount(b & safe); - b = (attackedBy[Us][BISHOP] & pos.attacks_from(s)) - | (attackedBy[Us][ROOK ] & pos.attacks_from(s)); + b = (attackedBy[Us][BISHOP] & attacks_bb(s, pos.pieces())) + | (attackedBy[Us][ROOK ] & attacks_bb(s, pos.pieces())); score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]); } @@ -672,16 +686,15 @@ namespace { } - // Evaluation::space() computes the space evaluation for a given side. The - // space evaluation is a simple bonus based on the number of safe squares - // available for minor pieces on the central four files on ranks 2--4. Safe - // squares one, two or three squares behind a friendly pawn are counted - // twice. Finally, the space bonus is multiplied by a weight. The aim is to - // improve play on game opening. + // Evaluation::space() computes a space evaluation for a given side, aiming to improve game + // play in the opening. It is based on the number of safe squares on the 4 central files + // on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice. + // Finally, the space bonus is multiplied by a weight which decreases according to occupancy. template template Score Evaluation::space() const { + // Early exit if, for example, both queens or 6 minor pieces have been exchanged if (pos.non_pawn_material() < SpaceThreshold) return SCORE_ZERO; @@ -712,12 +725,12 @@ namespace { } - // Evaluation::initiative() computes the initiative correction value - // for the position. It is a second order bonus/malus based on the - // known attacking/defending status of the players. + // Evaluation::winnable() adjusts the mg and eg score components based on the + // known attacking/defending status of the players. A single value is derived + // by interpolation from the mg and eg values and returned. template - Score Evaluation::initiative(Score score) const { + Value Evaluation::winnable(Score score) const { int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); @@ -739,7 +752,6 @@ namespace { + 24 * infiltration + 51 * !pos.non_pawn_material() - 43 * almostUnwinnable - - 2 * pos.rule50_count() -110 ; Value mg = mg_value(score); @@ -751,18 +763,10 @@ namespace { int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); - if (T) - Trace::add(INITIATIVE, make_score(u, v)); - - return make_score(u, v); - } - - - // Evaluation::scale_factor() computes the scale factor for the winning side - - template - ScaleFactor Evaluation::scale_factor(Value eg) const { + mg += u; + eg += v; + // Compute the scale factor for the winning side Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; int sf = me->scale_factor(pos, strongSide); @@ -777,11 +781,32 @@ namespace { else sf = 22 + 3 * pos.count(strongSide); } + else if ( pos.non_pawn_material(WHITE) == RookValueMg + && pos.non_pawn_material(BLACK) == RookValueMg + && !pe->passed_pawns(strongSide) + && pos.count(strongSide) - pos.count(~strongSide) <= 1 + && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) + && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) + sf = 36; + else if (pos.count() == 1) + sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK) + : pos.count(WHITE) + pos.count(WHITE)); else sf = std::min(sf, 36 + 7 * pos.count(strongSide)); } - return ScaleFactor(sf); + // Interpolate between the middlegame and (scaled by 'sf') endgame score + v = mg * int(me->game_phase()) + + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL; + v /= PHASE_MIDGAME; + + if (T) + { + Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score))); + Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL)); + } + + return Value(v); } @@ -817,12 +842,11 @@ namespace { return pos.side_to_move() == WHITE ? v : -v; // Main evaluation begins here - initialize(); initialize(); // Pieces evaluated first (also populates attackedBy, attackedBy2). - // Note that the order of evaluation of the terms is left unspecified + // Note that the order of evaluation of the terms is left unspecified. score += pieces() - pieces() + pieces() - pieces() + pieces() - pieces() @@ -836,14 +860,8 @@ namespace { + passed< WHITE>() - passed< BLACK>() + space< WHITE>() - space< BLACK>(); - score += initiative(score); - - // Interpolate between a middlegame and a (scaled by 'sf') endgame score - ScaleFactor sf = scale_factor(eg_value(score)); - v = mg_value(score) * int(me->game_phase()) - + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL; - - v /= PHASE_MIDGAME; + // Derive single value from mg and eg parts of score + v = winnable(score); // In case of tracing add all remaining individual evaluation terms if (T) @@ -852,11 +870,18 @@ namespace { Trace::add(IMBALANCE, me->imbalance()); Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK)); Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]); - Trace::add(TOTAL, score); } + // Evaluation grain + v = (v / 16) * 16; + // Side to move point of view - return (pos.side_to_move() == WHITE ? v : -v) + Tempo; + v = (pos.side_to_move() == WHITE ? v : -v) + Tempo; + + // Damp down the evaluation linearly when shuffling + v = v * (100 - pos.rule50_count()) / 100; + + return v; } } // namespace @@ -904,11 +929,11 @@ std::string Eval::trace(const Position& pos) { << " Threats | " << Term(THREAT) << " Passed | " << Term(PASSED) << " Space | " << Term(SPACE) - << " Initiative | " << Term(INITIATIVE) + << " Winnable | " << Term(WINNABLE) << " ------------+-------------+-------------+------------\n" << " Total | " << Term(TOTAL); - ss << "\nTotal evaluation: " << to_cp(v) << " (white side)\n"; + ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; return ss.str(); }