- {}, {},
- { S(-35,-30), S(-22,-20), S(-9,-10), S( 3, 0), S(15, 10), S(27, 20), // Knights
- S( 37, 28), S( 42, 31), S(44, 33) },
- { S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops
- S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77),
- S( 84, 79), S( 86, 81) },
- { S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks
- S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119),
- S( 35,122), S( 36,123), S(37,124) },
- { S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens
- S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40),
- S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41),
- S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
- S( 25, 41), S( 25, 41), S(25, 41), S(25, 41) }
+ {}, {},
+ { S(-35,-30), S(-22,-20), S(-9,-10), S( 3, 0), S(15, 10), S(27, 20), // Knights
+ S( 37, 28), S( 42, 31), S(44, 33) },
+ { S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops
+ S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77),
+ S( 84, 79), S( 86, 81) },
+ { S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks
+ S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119),
+ S( 35,122), S( 36,123), S(37,124) },
+ { S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens
+ S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40),
+ S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41),
+ S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
+ S( 25, 41), S( 25, 41), S(25, 41), S(25, 41) }
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0),
V(0), V(0), V(4), V(8), V(8), V(4), V(0), V(0),
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0),
V(0), V(0), V(4), V(8), V(8), V(4), V(0), V(0),
- {}, {},
- { 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
- 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;
- // evaluate_threats<>() assigns bonuses according to the type of attacking piece
- // and the type of attacked one.
-
- template<Color Us, bool Trace>
- 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() assigns bonuses and penalties to all the
- score += evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea);
- score += evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea);
- score += evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea);
- score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
+ Score score = evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea)
+ + evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea)
+ + evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea)
+ + evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
- Tracing::scores[Us][KING] = score;
+ Tracing::terms[Us][KING] = score;
+
+ return score;
+ }
+
+
+ // evaluate_threats() assigns bonuses according to the type of attacking piece
+ // and the type of attacked one.
+
+ template<Color Us, bool Trace>
+ 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::terms[Us][Tracing::THREAT] = score;
- // 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);
+ // 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.
- 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);
-
- while (b)
- {
- s = pop_lsb(&b);
-
- // 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;
- }
-
- // If any candidate is already a passed pawn it _may_ promote in time. We give up.
- if (candidates & ei.pi->passed_pawns(loserSide))
- 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)));
- stream.str("");
- stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
- std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score));
-
- Value margin;
- do_evaluate<true>(pos, margin);
-
- std::string totals = stream.str();
- stream.str("");
-
- stream << std::setw(21) << "Eval term " << "| White | Black | Total \n"
- << " | MG EG | MG EG | MG EG \n"
- << "---------------------+-------------+-------------+---------------\n";
-
- row("Material, PST, Tempo", PST);
- row("Material imbalance", IMBALANCE);
- row("Pawns", PAWN);
- row("Knights", KNIGHT);
- row("Bishops", BISHOP);
- row("Rooks", ROOK);
- row("Queens", QUEEN);
- row("Mobility", MOBILITY);
- row("King safety", KING);
- row("Threats", THREAT);
- row("Passed pawns", PASSED);
- row("Unstoppable pawns", UNSTOPPABLE);
- row("Space", SPACE);
-
- stream << "---------------------+-------------+-------------+---------------\n";
- row("Total", TOTAL);
- stream << totals;
-
- return stream.str();
+ std::memset(terms, 0, sizeof(terms));
+
+ Value v = do_evaluate<true>(pos);
+ v = pos.side_to_move() == WHITE ? v : -v; // White's point of view
+
+ std::stringstream ss;
+ ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
+ << " Eval term | White | Black | Total \n"
+ << " | MG EG | MG EG | MG EG \n"
+ << "---------------------+-------------+-------------+-------------\n";
+
+ format_row(ss, "Material, PST, Tempo", PST);
+ format_row(ss, "Material 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);
+
+ ss << "---------------------+-------------+-------------+-------------\n";
+ format_row(ss, "Total", TOTAL);
+
+ ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n";
+
+ return ss.str();