X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=c7549ce29237d41f8586cf9dab8a03fccbdb435e;hp=c03aa99f51940f0f0ec96fcbc7dce8b5ec9b4e69;hb=6b909b2343190f2989d21c8f69f40e9f09c530c0;hpb=dbe5e28eaa284aeaa4927ddde8a4341200e0e601 diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c03aa99f..c7549ce2 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -36,8 +36,8 @@ namespace { struct EvalInfo { // Pointers to material and pawn hash table entries - MaterialInfo* mi; - PawnInfo* pi; + MaterialEntry* mi; + PawnEntry* pi; // attackedBy[color][piece type] is a bitboard representing all squares // attacked by a given color and piece type, attackedBy[color][0] contains @@ -138,7 +138,7 @@ namespace { {}, {}, { 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(-1, 29), S(15, 49), S(15, 49), S( 0, 0), S(24, 49) }, // ROOK + { 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 }; @@ -150,12 +150,19 @@ namespace { #undef S - // Rooks and queens on the 7th rank (modified by Joona Kiiski) - const Score RookOn7thBonus = make_score(47, 98); - const Score QueenOn7thBonus = make_score(27, 54); + // Bonus for having the side to move (modified by Joona Kiiski) + const Score Tempo = make_score(24, 11); + + // Rooks and queens on the 7th rank + const Score RookOn7thBonus = make_score(3, 20); + const Score QueenOn7thBonus = make_score(1, 8); + + // Rooks and queens attacking pawns on the same rank + const Score RookOnPawnBonus = make_score(3, 48); + const Score QueenOnPawnBonus = make_score(1, 40); // Rooks on open files (modified by Joona Kiiski) - const Score RookOpenFileBonus = make_score(43, 21); + const Score RookOpenFileBonus = make_score(43, 21); const Score RookHalfOpenFileBonus = make_score(19, 10); // Penalty for rooks trapped inside a friendly king which has lost the @@ -167,8 +174,8 @@ namespace { // happen in Chess960 games. const Score TrappedBishopA1H1Penalty = make_score(100, 100); - // Penalty for BNR that is not defended by anything - const Score UndefendedPiecePenalty = make_score(25, 10); + // Penalty for an undefended bishop or knight + const Score UndefendedMinorPenalty = make_score(25, 10); // 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 @@ -250,8 +257,7 @@ namespace { Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei); - inline Score apply_weight(Score v, Score weight); - Value scale_by_game_phase(const Score& v, Phase ph, ScaleFactor sf); + Value interpolate(const Score& v, Phase ph, ScaleFactor sf); Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight); double to_cp(Value v); void trace_add(int idx, Score term_w, Score term_b = SCORE_ZERO); @@ -261,8 +267,6 @@ namespace { namespace Eval { - Color RootColor; - /// evaluate() is the main evaluation function. It always computes two /// values, an endgame score and a middle game score, and interpolates /// between them based on the remaining material. @@ -313,7 +317,7 @@ namespace Eval { Value margin; std::string totals; - RootColor = pos.side_to_move(); + Search::RootColor = pos.side_to_move(); TraceStream.str(""); TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); @@ -357,23 +361,23 @@ namespace { template Value do_evaluate(const Position& pos, Value& margin) { + assert(!pos.in_check()); + EvalInfo ei; Value margins[2]; Score score, mobilityWhite, mobilityBlack; - assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS); - assert(!pos.in_check()); - - // Initialize score by reading the incrementally updated scores included - // in the position object (material + piece square tables). - score = pos.value(); - // 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 + // Tempo bonus. Score is computed from the point of view of white. + score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo); + // Probe the material hash table - ei.mi = Threads[pos.thread()].materialTable.material_info(pos); + ei.mi = pos.this_thread()->materialTable.probe(pos); score += ei.mi->material_value(); // If we have a specialized evaluation function for the current material @@ -385,7 +389,7 @@ Value do_evaluate(const Position& pos, Value& margin) { } // Probe the pawn hash table - ei.pi = Threads[pos.thread()].pawnTable.pawn_info(pos); + ei.pi = pos.this_thread()->pawnTable.probe(pos); score += ei.pi->pawns_value(); // Initialize attack and king safety bitboards @@ -429,12 +433,12 @@ Value do_evaluate(const Position& pos, Value& margin) { // If we don't already have an unusual scale factor, check for opposite // colored bishop endgames, and use a lower scale for those. if ( ei.mi->game_phase() < PHASE_MIDGAME - && pos.opposite_colored_bishops() + && pos.opposite_bishops() && sf == SCALE_FACTOR_NORMAL) { // Only the two bishops ? - if ( pos.non_pawn_material(WHITE) == BishopValueMidgame - && pos.non_pawn_material(BLACK) == BishopValueMidgame) + 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. @@ -447,14 +451,13 @@ Value do_evaluate(const Position& pos, Value& margin) { sf = ScaleFactor(50); } - // Interpolate between the middle game and the endgame score margin = margins[pos.side_to_move()]; - Value v = scale_by_game_phase(score, ei.mi->game_phase(), sf); + Value v = interpolate(score, ei.mi->game_phase(), sf); // In case of tracing add all single evaluation contributions for both white and black if (Trace) { - trace_add(PST, pos.value()); + trace_add(PST, pos.psq_score()); trace_add(IMBALANCE, ei.mi->material_value()); trace_add(PAWN, ei.pi->pawns_value()); trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility])); @@ -491,7 +494,7 @@ Value do_evaluate(const Position& pos, Value& margin) { // Init king safety tables only if we are going to use them if ( pos.piece_count(Us, QUEEN) - && pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame) + && pos.non_pawn_material(Us) >= QueenValueMg + RookValueMg) { ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8)); b &= ei.attackedBy[Us][PAWN]; @@ -518,8 +521,8 @@ Value do_evaluate(const Position& pos, Value& margin) { // no minor piece which can exchange the outpost piece. if (bonus && (ei.attackedBy[Us][PAWN] & s)) { - if ( !pos.pieces(KNIGHT, Them) - && !(same_color_squares(s) & pos.pieces(BISHOP, Them))) + if ( !pos.pieces(Them, KNIGHT) + && !(same_color_squares(s) & pos.pieces(Them, BISHOP))) bonus += bonus + bonus / 2; else bonus += bonus / 2; @@ -550,9 +553,9 @@ Value do_evaluate(const Position& pos, Value& margin) { if (Piece == KNIGHT || Piece == QUEEN) b = pos.attacks_from(s); else if (Piece == BISHOP) - b = attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN, Us)); + b = attacks_bb(s, pos.pieces() ^ pos.pieces(Us, QUEEN)); else if (Piece == ROOK) - b = attacks_bb(s, pos.pieces() ^ pos.pieces(ROOK, QUEEN, Us)); + b = attacks_bb(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN)); else assert(false); @@ -580,8 +583,8 @@ Value do_evaluate(const Position& pos, Value& margin) { assert(b); - if (single_bit(b) && (b & pos.pieces(Them))) - score += ThreatBonus[Piece][type_of(pos.piece_on(first_1(b)))]; + if (!more_than_one(b) && (b & pos.pieces(Them))) + score += ThreatBonus[Piece][type_of(pos.piece_on(lsb(b)))]; } // Decrease score if we are attacked by an enemy pawn. Remaining part @@ -591,15 +594,21 @@ Value do_evaluate(const Position& pos, Value& margin) { // Bishop and knight outposts squares if ( (Piece == BISHOP || Piece == KNIGHT) - && !(pos.pieces(PAWN, Them) & attack_span_mask(Us, s))) + && !(pos.pieces(Them, PAWN) & attack_span_mask(Us, s))) score += evaluate_outposts(pos, ei, s); - // Queen or rook on 7th rank - if ( (Piece == ROOK || Piece == QUEEN) - && relative_rank(Us, s) == RANK_7 - && relative_rank(Us, pos.king_square(Them)) == RANK_8) + if ((Piece == ROOK || Piece == QUEEN) && relative_rank(Us, s) >= RANK_5) { - score += (Piece == ROOK ? RookOn7thBonus : QueenOn7thBonus); + // Major piece on 7th rank + if ( relative_rank(Us, s) == RANK_7 + && relative_rank(Us, pos.king_square(Them)) == RANK_8) + score += (Piece == ROOK ? RookOn7thBonus : QueenOn7thBonus); + + // Major piece attacking pawns on the same rank + Bitboard pawns = pos.pieces(Them, PAWN) & rank_bb(s); + if (pawns) + score += (Piece == ROOK ? RookOnPawnBonus + : QueenOnPawnBonus) * popcount(pawns); } // Special extra evaluation for bishops @@ -613,7 +622,7 @@ Value do_evaluate(const Position& pos, Value& margin) { Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); if (pos.piece_on(s + d) == make_piece(Us, PAWN)) { - if (!pos.square_is_empty(s + d + pawn_push(Us))) + if (!pos.is_empty(s + d + pawn_push(Us))) score -= 2*TrappedBishopA1H1Penalty; else if (pos.piece_on(s + 2*d) == make_piece(Us, PAWN)) score -= TrappedBishopA1H1Penalty; @@ -679,23 +688,23 @@ Value do_evaluate(const Position& pos, Value& margin) { const Color Them = (Us == WHITE ? BLACK : WHITE); - Bitboard b; + Bitboard b, undefendedMinors, weakEnemies; Score score = SCORE_ZERO; - // Undefended pieces get penalized even if not under attack - Bitboard undefended = pos.pieces(Them) & ~ei.attackedBy[Them][0]; - const Bitboard undefendedMinors = undefended & (pos.pieces(BISHOP) | pos.pieces(KNIGHT)); - + // Undefended minors get penalized even if not under attack + undefendedMinors = pos.pieces(Them) + & (pos.pieces(BISHOP) | pos.pieces(KNIGHT)) + & ~ei.attackedBy[Them][0]; + if (undefendedMinors) - score += single_bit(undefendedMinors) ? UndefendedPiecePenalty - : UndefendedPiecePenalty * 2; - if (undefended & pos.pieces(ROOK)) - score += UndefendedPiecePenalty; + score += more_than_one(undefendedMinors) ? UndefendedMinorPenalty * 2 + : UndefendedMinorPenalty; // Enemy pieces not defended by a pawn and under our attack - Bitboard weakEnemies = pos.pieces(Them) - & ~ei.attackedBy[Them][PAWN] - & ei.attackedBy[Us][0]; + weakEnemies = pos.pieces(Them) + & ~ei.attackedBy[Them][PAWN] + & ei.attackedBy[Us][0]; + if (!weakEnemies) return score; @@ -751,8 +760,8 @@ Value do_evaluate(const Position& pos, Value& margin) { int attackUnits; const Square ksq = pos.king_square(Us); - // King shelter - Score score = ei.pi->king_shelter(pos, ksq); + // 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. @@ -774,7 +783,7 @@ Value do_evaluate(const Position& pos, Value& margin) { attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount(undefended)) + InitKingDanger[relative_square(Us, ksq)] - - mg_value(ei.pi->king_shelter(pos, ksq)) / 32; + - mg_value(score) / 32; // Analyse enemy's safe queen contact checks. First find undefended // squares around the king attacked by enemy queen... @@ -842,8 +851,8 @@ Value do_evaluate(const Position& pos, Value& margin) { // 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. - score -= KingDangerTable[Us == Eval::RootColor][attackUnits]; - margins[Us] += mg_value(KingDangerTable[Us == Eval::RootColor][attackUnits]); + score -= KingDangerTable[Us == Search::RootColor][attackUnits]; + margins[Us] += mg_value(KingDangerTable[Us == Search::RootColor][attackUnits]); } if (Trace) @@ -869,7 +878,7 @@ Value do_evaluate(const Position& pos, Value& margin) { return SCORE_ZERO; do { - Square s = pop_1st_bit(&b); + Square s = pop_lsb(&b); assert(pos.pawn_is_passed(Us, s)); @@ -893,16 +902,16 @@ Value do_evaluate(const Position& pos, Value& margin) { ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr); // If the pawn is free to advance, increase bonus - if (pos.square_is_empty(blockSq)) + if (pos.is_empty(blockSq)) { - squaresToQueen = squares_in_front_of(Us, s); + squaresToQueen = forward_bb(Us, s); defendedSquares = squaresToQueen & ei.attackedBy[Us][0]; // 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 ( (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them)) - && (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from(s))) + if ( (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][0] | pos.pieces(Them)); @@ -922,7 +931,7 @@ Value do_evaluate(const Position& pos, Value& margin) { // Increase the bonus if the passed pawn is supported by a friendly pawn // on the same rank and a bit smaller if it's on the previous rank. - supportingPawns = pos.pieces(PAWN, Us) & adjacent_files_bb(file_of(s)); + supportingPawns = pos.pieces(Us, PAWN) & adjacent_files_bb(file_of(s)); if (supportingPawns & rank_bb(s)) ebonus += Value(r * 20); @@ -937,9 +946,9 @@ Value do_evaluate(const Position& pos, Value& margin) { // value if the other side has a rook or queen. if (file_of(s) == FILE_A || file_of(s) == FILE_H) { - if (pos.non_pawn_material(Them) <= KnightValueMidgame) + if (pos.non_pawn_material(Them) <= KnightValueMg) ebonus += ebonus / 4; - else if (pos.pieces(ROOK, QUEEN, Them)) + else if (pos.pieces(Them, ROOK, QUEEN)) ebonus -= ebonus / 4; } score += make_score(mbonus, ebonus); @@ -975,9 +984,9 @@ Value do_evaluate(const Position& pos, Value& margin) { while (b) { - s = pop_1st_bit(&b); - queeningSquare = relative_square(c, make_square(file_of(s), RANK_8)); - queeningPath = squares_in_front_of(c, s); + 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); @@ -1012,20 +1021,20 @@ Value do_evaluate(const Position& pos, Value& margin) { loserSide = ~winnerSide; // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss? - b = candidates = pos.pieces(PAWN, loserSide); + b = candidates = pos.pieces(loserSide, PAWN); while (b) { - s = pop_1st_bit(&b); + s = pop_lsb(&b); // Compute plies from queening - queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8)); + 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 - || (squares_in_front_of(loserSide, s) & pos.pieces(PAWN, loserSide))) + || (forward_bb(loserSide, s) & pos.pieces(loserSide, PAWN))) candidates ^= s; } @@ -1038,26 +1047,26 @@ Value do_evaluate(const Position& pos, Value& margin) { while (b) { - s = pop_1st_bit(&b); + s = pop_lsb(&b); sacptg = blockersCount = 0; minKingDist = kingptg = 256; // Compute plies from queening - queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8)); + 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 = squares_in_front_of(loserSide, s) & pos.pieces(PAWN, winnerSide); - blockers = passed_pawn_mask(loserSide, s) & pos.pieces(PAWN, winnerSide); + 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_1st_bit(&blockers); + blockSq = pop_lsb(&blockers); movesToGo = 256; // Check pawns that can give support to overcome obstacle, for instance @@ -1068,7 +1077,7 @@ Value do_evaluate(const Position& pos, Value& margin) { while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) { - d = square_distance(blockSq, pop_1st_bit(&b2)) - 2; + d = square_distance(blockSq, pop_lsb(&b2)) - 2; movesToGo = std::min(movesToGo, d); } } @@ -1078,7 +1087,7 @@ Value do_evaluate(const Position& pos, Value& margin) { while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) { - d = square_distance(blockSq, pop_1st_bit(&b2)) - 2; + d = square_distance(blockSq, pop_lsb(&b2)) - 2; movesToGo = std::min(movesToGo, d); } @@ -1107,7 +1116,7 @@ Value do_evaluate(const Position& pos, Value& margin) { } // Winning pawn is unstoppable and will promote as first, return big score - Score score = make_score(0, (Value) 0x500 - 0x20 * pliesToQueen[winnerSide]); + Score score = make_score(0, (Value) 1280 - 32 * pliesToQueen[winnerSide]); return winnerSide == WHITE ? score : -score; } @@ -1127,12 +1136,12 @@ Value do_evaluate(const Position& pos, Value& margin) { // SpaceMask[]. A square is unsafe if it is attacked by an enemy // pawn, or if it is undefended and attacked by an enemy piece. Bitboard safe = SpaceMask[Us] - & ~pos.pieces(PAWN, Us) + & ~pos.pieces(Us, PAWN) & ~ei.attackedBy[Them][PAWN] & (ei.attackedBy[Us][0] | ~ei.attackedBy[Them][0]); // Find all squares which are at most three squares behind some friendly pawn - Bitboard behind = pos.pieces(PAWN, Us); + Bitboard behind = pos.pieces(Us, PAWN); behind |= (Us == WHITE ? behind >> 8 : behind << 8); behind |= (Us == WHITE ? behind >> 16 : behind << 16); @@ -1140,18 +1149,10 @@ Value do_evaluate(const Position& pos, Value& margin) { } - // apply_weight() applies an evaluation weight to a value trying to prevent overflow - - inline Score apply_weight(Score v, Score w) { - return make_score((int(mg_value(v)) * mg_value(w)) / 0x100, - (int(eg_value(v)) * eg_value(w)) / 0x100); - } - - - // scale_by_game_phase() interpolates between a middle game and an endgame score, + // interpolate() interpolates between a middle game and an endgame score, // based on game phase. It also scales the return value by a ScaleFactor array. - Value scale_by_game_phase(const Score& v, Phase ph, ScaleFactor sf) { + Value interpolate(const Score& v, Phase ph, ScaleFactor sf) { assert(mg_value(v) > -VALUE_INFINITE && mg_value(v) < VALUE_INFINITE); assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE); @@ -1179,7 +1180,7 @@ Value do_evaluate(const Position& pos, Value& margin) { // A couple of little helpers used by tracing code, to_cp() converts a value to // a double in centipawns scale, trace_add() stores white and black scores. - double to_cp(Value v) { return double(v) / double(PawnValueMidgame); } + double to_cp(Value v) { return double(v) / double(PawnValueMg); } void trace_add(int idx, Score wScore, Score bScore) {