X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=6a27108d9f4b6c3f2da3bfcc239760081142428d;hp=b47570e86303863cc968e632ca49b15ca9e175b0;hb=c7332d5610faf6086ce0b0fe795f5ffc92cc39b0;hpb=0a1f54975fe3cb59a5274cbfee51f5b53e64071b diff --git a/src/evaluate.cpp b/src/evaluate.cpp index b47570e8..6a27108d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -19,6 +19,7 @@ #include #include +#include // For std::memset #include #include @@ -26,7 +27,6 @@ #include "evaluate.h" #include "material.h" #include "pawns.h" -#include "thread.h" namespace { @@ -91,10 +91,10 @@ namespace { // 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} + {289, 344}, {233, 201}, {221, 273}, {46, 0}, {321, 0} }; - typedef Value V; + #define V(v) Value(v) #define S(mg, eg) make_score(mg, eg) // MobilityBonus[PieceType][attacked] contains bonuses for middle and end @@ -152,16 +152,16 @@ namespace { }; // Assorted bonuses and penalties used by evaluation - const Score KingOnOne = S( 2, 58); - const Score KingOnMany = S( 6,125); - const Score RookOnPawn = S( 7, 27); - 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(31, 26); + const Score KingOnOne = S( 2, 58); + const Score KingOnMany = S( 6,125); + const Score RookOnPawn = S( 7, 27); + const Score RookOnOpenFile = S(43, 21); + const Score RookOnSemiOpenFile = 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(31, 26); // 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 @@ -169,6 +169,7 @@ namespace { const Score TrappedBishopA1H1 = S(50, 50); #undef S + #undef V // SpaceMask[Color] contains the area of the board which is considered // by the space evaluation. In the middlegame, each side is given a bonus @@ -179,29 +180,29 @@ namespace { (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB) }; - // King danger constants and variables. The king danger scores are taken - // from KingDanger[]. Various little "meta-bonuses" measuring the strength + // King danger constants and variables. The king danger scores are looked-up + // in 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 }; + const int KingAttackWeights[] = { 0, 0, 6, 2, 5, 5 }; // Bonuses for enemy's safe checks - const int QueenContactCheck = 24; - const int RookContactCheck = 16; - const int QueenCheck = 12; - const int RookCheck = 8; - const int BishopCheck = 2; - const int KnightCheck = 3; + const int QueenContactCheck = 92; + const int RookContactCheck = 68; + const int QueenCheck = 50; + const int RookCheck = 36; + const int BishopCheck = 7; + const int KnightCheck = 14; // KingDanger[attackUnits] contains the actual king danger weighted // scores, indexed by a calculated integer number. - Score KingDanger[128]; + Score KingDanger[512]; - // 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); + // apply_weight() weighs score 's' by weight 'w' trying to prevent overflow + Score apply_weight(Score s, const Weight& w) { + return make_score(mg_value(s) * w.mg / 256, eg_value(s) * w.eg / 256); } @@ -312,65 +313,65 @@ namespace { if (Pt == BISHOP || Pt == KNIGHT) { - // Penalty for bishop with same colored pawns - if (Pt == BISHOP) - score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); - - // Bishop and knight outpost square + // Bonus for outpost square if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s))) score += evaluate_outpost(pos, ei, s); - // Bishop or knight behind a pawn + // Bonus when behind a pawn if ( relative_rank(Us, s) < RANK_5 && (pos.pieces(PAWN) & (s + pawn_push(Us)))) score += MinorBehindPawn; + + // Penalty for pawns on same color square of bishop + if (Pt == BISHOP) + score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); + + // An important Chess960 pattern: A cornered bishop blocked by a friendly + // pawn diagonally in front of it is a very serious problem, especially + // when that pawn is also blocked. + if ( Pt == BISHOP + && pos.is_chess960() + && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) + { + Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); + if (pos.piece_on(s + d) == make_piece(Us, PAWN)) + score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 + : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2 + : TrappedBishopA1H1; + } } if (Pt == ROOK) { - // Rook piece attacking enemy pawns on the same rank/file + // Bonus for aligning with enemy pawns on the same rank/file if (relative_rank(Us, s) >= RANK_5) { - Bitboard pawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]; - if (pawns) - score += popcount(pawns) * RookOnPawn; + Bitboard alignedPawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]; + if (alignedPawns) + score += popcount(alignedPawns) * RookOnPawn; } - // Give a bonus for a rook on a open or semi-open file + // Bonus when on an 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; - - if (mob > 3 || ei.pi->semiopen_file(Us, file_of(s))) - continue; - - Square ksq = pos.king_square(Us); + score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOnOpenFile : RookOnSemiOpenFile; - // 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_side(Us, file_of(ksq), file_of(s) < file_of(ksq))) - score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us)); - } + // Penalize when trapped by the king, even more if king cannot castle + if (mob <= 3 && !ei.pi->semiopen_file(Us, file_of(s))) + { + Square ksq = pos.king_square(Us); - // An important Chess960 pattern: A cornered bishop blocked by a friendly - // pawn diagonally in front of it is a very serious problem, especially - // when that pawn is also blocked. - if ( Pt == BISHOP - && pos.is_chess960() - && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) - { - Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); - if (pos.piece_on(s + d) == make_piece(Us, PAWN)) - score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 - : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2 - : TrappedBishopA1H1; + 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 * 22, 0)) * (1 + !pos.can_castle(Us)); + } } } if (Trace) Tracing::write(Pt, Us, score); + // Recursively call evaluate_pieces() of next piece type until KING excluded return score - evaluate_pieces(pos, ei, mobility, mobilityArea); } @@ -406,33 +407,32 @@ namespace { | 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 + // index into 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 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)) - + 2 * (ei.pinnedPieces[Us] != 0) - - mg_value(score) / 32 - - !pos.count(Them) * 15; + attackUnits = std::min(77, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) + + 10 * ei.kingAdjacentZoneAttacksCount[Them] + + 19 * popcount(undefended) + + 9 * (ei.pinnedPieces[Us] != 0) + - mg_value(score) * 63 / 512 + - !pos.count(Them) * 60; // Analyse the enemy's safe queen contact checks. Firstly, find the - // undefended squares around the king that are attacked by the enemy's - // queen... + // undefended squares around the king reachable by the enemy queen... b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them); if (b) { // ...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]); + b &= ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] + | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]; if (b) - attackUnits += QueenContactCheck * popcount(b); + attackUnits += QueenContactCheck * popcount(b); } // Analyse the enemy's safe rook contact checks. Firstly, find the - // undefended squares around the king that are attacked by the enemy's - // rooks... + // undefended squares around the king reachable by the enemy rooks... b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them); // Consider only squares where the enemy's rook gives check @@ -445,13 +445,13 @@ namespace { | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); if (b) - attackUnits += RookContactCheck * popcount(b); + attackUnits += RookContactCheck * popcount(b); } // Analyse the enemy's safe distance checks for sliders and knights - safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]); + safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them)); - b1 = pos.attacks_from(ksq) & safe; + b1 = pos.attacks_from(ksq) & safe; b2 = pos.attacks_from(ksq) & safe; // Enemy queen safe checks @@ -474,12 +474,9 @@ namespace { if (b) attackUnits += KnightCheck * popcount(b); - // To index KingDanger[] attackUnits must be in [0, 99] range - attackUnits = std::min(99, std::max(0, attackUnits)); - // Finally, extract the king danger score from the KingDanger[] // array and subtract the score from evaluation. - score -= KingDanger[attackUnits]; + score -= KingDanger[std::max(std::min(attackUnits, 399), 0)]; } if (Trace) @@ -503,10 +500,9 @@ namespace { Bitboard b, weak, defended; Score score = SCORE_ZERO; - // Non-pawn enemies defended by a pawn and under our attack + // Non-pawn enemies defended by a pawn defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) - & ei.attackedBy[Them][PAWN] - & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]); + & ei.attackedBy[Them][PAWN]; // Add a bonus according to the kind of attacking pieces if (defended) @@ -636,26 +632,14 @@ namespace { } - // 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); - - return b ? Unstoppable * int(relative_rank(us, frontmost_sq(us, b))) : SCORE_ZERO; - } - - // evaluate_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 scaled by a weight taken from the - // material hash table. The aim is to improve play on game opening. + // twice. Finally, the space bonus is multiplied by a weight. The aim is to + // improve play on game opening. template - int evaluate_space(const Position& pos, const EvalInfo& ei) { + Score evaluate_space(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -676,7 +660,11 @@ namespace { assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0); // Count safe + (behind & safe) with a single popcount - return popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); + int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); + int weight = pos.count(Us) + pos.count(Us) + + pos.count(Them) + pos.count(Them); + + return make_score(bonus * weight * weight, 0); } @@ -689,7 +677,6 @@ namespace { EvalInfo ei; Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO }; - Thread* thisThread = pos.this_thread(); // Initialize score by reading the incrementally updated scores included // in the position object (material + piece square tables). @@ -697,17 +684,17 @@ namespace { score = pos.psq_score(); // Probe the material hash table - ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames); - score += ei.mi->material_value(); + ei.mi = Material::probe(pos); + score += ei.mi->imbalance(); // 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) + Eval::Tempo; + return ei.mi->evaluate(pos); // Probe the pawn hash table - ei.pi = Pawns::probe(pos, thisThread->pawnsTable); - score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]); + ei.pi = Pawns::probe(pos); + score += apply_weight(ei.pi->pawns_score(), Weights[PawnStructure]); // Initialize attack and king safety bitboards init_eval_info(pos, ei); @@ -739,14 +726,20 @@ namespace { // 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); + { + Bitboard b; + if ((b = ei.pi->passed_pawns(WHITE)) != 0) + score += int(relative_rank(WHITE, frontmost_sq(WHITE, b))) * Unstoppable; + + if ((b = ei.pi->passed_pawns(BLACK)) != 0) + score -= int(relative_rank(BLACK, frontmost_sq(BLACK, b))) * Unstoppable; + } - // Evaluate space for both sides, only in middlegame - if (ei.mi->space_weight()) + // Evaluate space for both sides, only during opening + if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) { - int s = evaluate_space(pos, ei) - evaluate_space(pos, ei); - score += apply_weight(s * ei.mi->space_weight(), Weights[Space]); + Score s = evaluate_space(pos, ei) - evaluate_space(pos, ei); + score += apply_weight(s, Weights[Space]); } // Scale winning side if position is more drawish than it appears @@ -789,13 +782,12 @@ namespace { if (Trace) { 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::IMBALANCE, ei.mi->imbalance()); + Tracing::write(PAWN, ei.pi->pawns_score()); 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::write(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); + Tracing::write(Tracing::SPACE, apply_weight(evaluate_space(pos, ei), Weights[Space]) + , apply_weight(evaluate_space(pos, ei), Weights[Space])); Tracing::write(Tracing::TOTAL, score); Tracing::ei = ei; Tracing::sf = sf; @@ -895,17 +887,18 @@ namespace Eval { } - /// init() computes evaluation weights. + /// init() computes evaluation weights, usually at startup void init() { - const double MaxSlope = 30; + const double MaxSlope = 7.5; const double Peak = 1280; + double t = 0.0; - for (int t = 0, i = 1; i < 100; ++i) + for (int i = 1; i < 400; ++i) { - t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope))); - KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]); + t = std::min(Peak, std::min(0.025 * i * i, t + MaxSlope)); + KingDanger[i] = apply_weight(make_score(int(t), 0), Weights[KingSafety]); } }