along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <algorithm>
#include <cassert>
#include <iomanip>
#include <sstream>
-#include <algorithm>
#include "bitcount.h"
#include "evaluate.h"
}
// Evaluation weights, initialized from UCI options
- enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
- struct Weight { int mg, eg; } Weights[6];
+ enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety };
+ struct Weight { int mg, eg; } Weights[5];
typedef Value V;
#define S(mg, eg) make_score(mg, eg)
//
// Values modified by Joona Kiiski
const Score WeightsInternal[] = {
- S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(271, 0), S(307, 0)
+ S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(289, 0)
};
// MobilityBonus[PieceType][attacked] contains bonuses for middle and end
S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
};
+ // Hanging[side to move] contains a bonus for each enemy hanging piece
+ const Score Hanging[2] = { S(23, 20) , S(35, 45) };
+
#undef S
const Score Tempo = make_score(24, 11);
- const Score RookOn7th = make_score(11, 20);
const Score RookOnPawn = make_score(10, 28);
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);
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.
- Score KingDanger[COLOR_NB][128];
-
- // Function prototypes
- template<bool Trace>
- Value do_evaluate(const Position& pos);
-
- template<Color Us>
- void init_eval_info(const Position& pos, EvalInfo& ei);
-
- template<PieceType Pt, Color Us, bool Trace>
- Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, Bitboard* mobilityArea);
-
- template<Color Us, bool Trace>
- Score evaluate_king(const Position& pos, const EvalInfo& ei);
-
- template<Color Us, bool Trace>
- Score evaluate_threats(const Position& pos, const EvalInfo& ei);
-
- template<Color Us, bool Trace>
- Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei);
-
- template<Color Us>
- int evaluate_space(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, const Weight& w);
- Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight);
-}
-
-
-namespace Eval {
-
- /// evaluate() is the main evaluation function. It always computes two
- /// values, an endgame score and a middlegame score, and interpolates
- /// between them based on the remaining material.
-
- Value evaluate(const Position& pos) {
- return do_evaluate<false>(pos);
- }
-
-
- /// 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);
- }
-
-
- /// init() computes evaluation weights from the corresponding UCI parameters
- /// and setup king tables.
-
- void init() {
-
- Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]);
- Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]);
- Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
- Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
- Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
- Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
-
- const int MaxSlope = 30;
- const int Peak = 1280;
-
- for (int t = 0, i = 1; i < 100; ++i)
- {
- t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope));
-
- KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
- KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
- }
- }
-
-} // namespace Eval
-
-
-namespace {
-
-template<bool Trace>
-Value do_evaluate(const Position& pos) {
+ // KingDanger[attackUnits] contains the actual king danger weighted
+ // scores, indexed by a calculated integer number.
+ Score KingDanger[128];
- assert(!pos.checkers());
- 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) 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);
-
- // Probe the material hash table
- ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames);
- score += ei.mi->material_value();
-
- // 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);
-
- // Probe the pawn hash table
- ei.pi = Pawns::probe(pos, thisThread->pawnsTable);
- score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
-
- // Initialize attack and king safety bitboards
- init_eval_info<WHITE>(pos, ei);
- init_eval_info<BLACK>(pos, ei);
-
- ei.attackedBy[WHITE][ALL_PIECES] |= ei.attackedBy[WHITE][KING];
- ei.attackedBy[BLACK][ALL_PIECES] |= ei.attackedBy[BLACK][KING];
-
- // Do not include in mobility squares protected by enemy pawns or occupied by our pieces
- Bitboard mobilityArea[] = { ~(ei.attackedBy[BLACK][PAWN] | pos.pieces(WHITE, PAWN, KING)),
- ~(ei.attackedBy[WHITE][PAWN] | pos.pieces(BLACK, PAWN, KING)) };
-
- // Evaluate pieces and mobility
- score += evaluate_pieces<KNIGHT, WHITE, Trace>(pos, ei, mobility, mobilityArea);
- 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<WHITE, Trace>(pos, ei)
- - evaluate_king<BLACK, Trace>(pos, ei);
-
- // Evaluate tactical threats, we need full attack information including king
- score += evaluate_threats<WHITE, Trace>(pos, ei)
- - evaluate_threats<BLACK, Trace>(pos, ei);
-
- // Evaluate passed pawns, we need full attack information including king
- score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
- - evaluate_passed_pawns<BLACK, Trace>(pos, ei);
-
- // 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, WHITE, ei)
- - evaluate_unstoppable_pawns(pos, BLACK, ei);
-
- // Evaluate space for both sides, only in middlegame
- if (ei.mi->space_weight())
- {
- int s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
- score += apply_weight(s * ei.mi->space_weight(), Weights[Space]);
+ // 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);
}
- // 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);
- // 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_bishops()
- && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
- {
- // 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)
- {
- // Check for KBP vs KB with only a single pawn that is almost
- // certainly a draw or at least two pawns.
- bool one_pawn = (pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK) == 1);
- sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
- }
- else
- // Endgame with opposite-colored bishops, but also other pieces. Still
- // a bit drawish, but not as drawish as with only the two bishops.
- sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL);
- }
+ // weight_option() computes the value of an evaluation weight, by combining
+ // two UCI-configurable weights (midgame and endgame) with an internal weight.
- Value v = interpolate(score, ei.mi->game_phase(), sf);
+ Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
- // In case of tracing add all single evaluation contributions for both white and black
- if (Trace)
- {
- Tracing::add_term(Tracing::PST, pos.psq_score());
- Tracing::add_term(Tracing::IMBALANCE, ei.mi->material_value());
- Tracing::add_term(PAWN, ei.pi->pawns_value());
- Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility])
- , apply_weight(mobility[BLACK], Weights[Mobility]));
- Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
- Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
- Tracing::add_term(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
- Tracing::add_term(Tracing::TOTAL, score);
- Tracing::ei = ei;
- Tracing::sf = sf;
+ Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100,
+ Options[egOpt] * eg_value(internalWeight) / 100 };
+ return w;
}
- return pos.side_to_move() == WHITE ? v : -v;
-}
-
// init_eval_info() initializes king bitboards for given color adding
// pawn attacks. To be done at the beginning of the evaluation.
Square s;
Score score = SCORE_ZERO;
+ const PieceType NextPt = (Us == WHITE ? Pt : PieceType(Pt + 1));
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square* pl = pos.list<Pt>(Us);
if (Pt == BISHOP)
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
- // Penalty for knight when there are few enemy pawns
- if (Pt == KNIGHT)
- score -= KnightPawns * std::max(5 - pos.count<PAWN>(Them), 0);
-
// Bishop and knight outposts squares
if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
score += evaluate_outposts<Pt, Us>(pos, ei, s);
if (Pt == ROOK)
{
- // Rook on 7th rank and enemy king trapped on 8th
- if ( relative_rank(Us, s) == RANK_7
- && relative_rank(Us, pos.king_square(Them)) == RANK_8)
- score += RookOn7th;
-
// Rook piece attacking enemy pawns on the same rank/file
if (relative_rank(Us, s) >= RANK_5)
{
}
// Give a bonus for a rook on a open or semi-open file
- if (ei.pi->semiopen(Us, file_of(s)))
- score += ei.pi->semiopen(Them, file_of(s)) ? RookOpenFile : RookSemiopenFile;
+ 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(Us, file_of(s)))
+ if (mob > 3 || ei.pi->semiopen_file(Us, file_of(s)))
continue;
Square ksq = pos.king_square(Us);
// 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))
- score -= (TrappedRook - make_score(mob * 8, 0)) * (pos.can_castle(Us) ? 1 : 2);
+ && !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq)))
+ score -= (TrappedRook - make_score(mob * 8, 0)) * (1 + !pos.can_castle(Us));
}
// An important Chess960 pattern: A cornered bishop blocked by a friendly
if (Trace)
Tracing::terms[Us][Pt] = score;
- const PieceType NextPt = (Us == WHITE ? Pt : PieceType(Pt + 1));
-
return score - evaluate_pieces<NextPt, Them, Trace>(pos, ei, mobility, mobilityArea);
}
// Finally, extract the king danger score from the KingDanger[]
// array and subtract the score from evaluation.
- score -= KingDanger[Us == Search::RootColor][attackUnits];
+ score -= KingDanger[attackUnits];
}
if (Trace)
const Color Them = (Us == WHITE ? BLACK : WHITE);
- Bitboard b, undefendedMinors, weakEnemies;
+ Bitboard b, 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
+ // Enemies 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]);
+ b = weakEnemies & (ei.attackedBy[Us][PAWN] | 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)))];
+
+ b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES];
+ if (b)
+ score += more_than_one(b) ? Hanging[Us != pos.side_to_move()] * popcount<Max15>(b)
+ : Hanging[Us == pos.side_to_move()];
}
if (Trace)
const Color Them = (Us == WHITE ? BLACK : WHITE);
- Bitboard b, squaresToQueen, defendedSquares, unsafeSquares, supportingPawns;
+ Bitboard b, squaresToQueen, defendedSquares, unsafeSquares;
Score score = SCORE_ZERO;
b = ei.pi->passed_pawns(Us);
assert(pos.pawn_passed(Us, s));
- int r = int(relative_rank(Us, s) - RANK_2);
+ int r = relative_rank(Us, s) - RANK_2;
int rr = r * (r - 1);
// Base bonus based on rank
- Value mbonus = Value(17 * rr);
- Value ebonus = Value(7 * (rr + r + 1));
+ Value mbonus = Value(17 * rr), ebonus = Value(7 * (rr + r + 1));
if (rr)
{
Square blockSq = s + pawn_push(Us);
// 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);
+ ebonus += square_distance(pos.king_square(Them), blockSq) * 5 * rr
+ - 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(rr * square_distance(pos.king_square(Us), blockSq + pawn_push(Us)));
+ ebonus -= square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr;
// If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq))
else
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
- // 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;
+ // If there aren't any enemy attacks, assign a big bonus. Otherwise
+ // assign a smaller bonus if the block square isn't attacked.
+ int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 0;
- // 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 the path to queen is fully defended, assign a big bonus.
+ // Otherwise assign a smaller bonus if the block square is defended.
if (defendedSquares == squaresToQueen)
k += 6;
else if (defendedSquares & blockSq)
- k += (unsafeSquares & defendedSquares) == unsafeSquares ? 4 : 2;
+ k += 4;
- mbonus += Value(k * rr), ebonus += Value(k * rr);
+ mbonus += k * rr, ebonus += k * rr;
}
} // rr != 0
- // 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(Us, PAWN) & adjacent_files_bb(file_of(s));
- if (supportingPawns & rank_bb(s))
- ebonus += Value(r * 20);
-
- else if (supportingPawns & rank_bb(s - pawn_push(Us)))
- ebonus += Value(r * 12);
-
- // Rook pawns are a special case: They are sometimes worse, and
- // sometimes better than other passed pawns. It is difficult to find
- // good rules for determining whether they are good or bad. For now,
- // we try the following: Increase the value for rook pawns if the
- // other side has no pieces apart from a knight, and decrease the
- // 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) <= KnightValueMg)
- ebonus += ebonus / 4;
-
- else if (pos.pieces(Them, ROOK, QUEEN))
- ebonus -= ebonus / 4;
- }
-
if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them))
ebonus += ebonus / 4;
}
- // interpolate() interpolates between a middlegame and an endgame score,
- // based on game phase. It also scales the return value by a ScaleFactor array.
+ // do_evaluate() is the evaluation entry point, called directly from evaluate()
- Value interpolate(const Score& v, Phase ph, ScaleFactor sf) {
+ template<bool Trace>
+ Value do_evaluate(const Position& pos) {
- assert(-VALUE_INFINITE < mg_value(v) && mg_value(v) < VALUE_INFINITE);
- assert(-VALUE_INFINITE < eg_value(v) && eg_value(v) < VALUE_INFINITE);
- assert(PHASE_ENDGAME <= ph && ph <= PHASE_MIDGAME);
+ assert(!pos.checkers());
- int eg = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
- return Value((mg_value(v) * int(ph) + eg * int(PHASE_MIDGAME - ph)) / PHASE_MIDGAME);
- }
+ 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) 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);
+
+ // Probe the material hash table
+ ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames);
+ score += ei.mi->material_value();
+
+ // 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);
+
+ // Probe the pawn hash table
+ ei.pi = Pawns::probe(pos, thisThread->pawnsTable);
+ score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
+
+ // Initialize attack and king safety bitboards
+ init_eval_info<WHITE>(pos, ei);
+ init_eval_info<BLACK>(pos, ei);
+
+ ei.attackedBy[WHITE][ALL_PIECES] |= ei.attackedBy[WHITE][KING];
+ ei.attackedBy[BLACK][ALL_PIECES] |= ei.attackedBy[BLACK][KING];
+
+ // Do not include in mobility squares protected by enemy pawns or occupied by our pawns or king
+ Bitboard mobilityArea[] = { ~(ei.attackedBy[BLACK][PAWN] | pos.pieces(WHITE, PAWN, KING)),
+ ~(ei.attackedBy[WHITE][PAWN] | pos.pieces(BLACK, PAWN, KING)) };
+
+ // Evaluate pieces and mobility
+ score += evaluate_pieces<KNIGHT, WHITE, Trace>(pos, ei, mobility, mobilityArea);
+ 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<WHITE, Trace>(pos, ei)
+ - evaluate_king<BLACK, Trace>(pos, ei);
+
+ // Evaluate tactical threats, we need full attack information including king
+ score += evaluate_threats<WHITE, Trace>(pos, ei)
+ - evaluate_threats<BLACK, Trace>(pos, ei);
+
+ // Evaluate passed pawns, we need full attack information including king
+ score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
+ - evaluate_passed_pawns<BLACK, Trace>(pos, ei);
+
+ // 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, WHITE, ei)
+ - evaluate_unstoppable_pawns(pos, BLACK, ei);
+
+ // Evaluate space for both sides, only in middlegame
+ if (ei.mi->space_weight())
+ {
+ int s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
+ score += apply_weight(s * ei.mi->space_weight(), Weights[Space]);
+ }
- // apply_weight() weights score v by score w trying to prevent overflow
- Score apply_weight(Score v, const Weight& w) {
+ // 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);
- return make_score(mg_value(v) * w.mg / 256, eg_value(v) * w.eg / 256);
- }
+ // 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_bishops()
+ && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
+ {
+ // 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)
+ {
+ // Check for KBP vs KB with only a single pawn that is almost
+ // certainly a draw or at least two pawns.
+ bool one_pawn = (pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK) == 1);
+ sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
+ }
+ else
+ // Endgame with opposite-colored bishops, but also other pieces. Still
+ // a bit drawish, but not as drawish as with only the two bishops.
+ sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL);
+ }
- // weight_option() computes the value of an evaluation weight, by combining
- // two UCI-configurable weights (midgame and endgame) with an internal weight.
+ // Interpolate between a middlegame and a (scaled by 'sf') endgame score
+ Value v = mg_value(score) * int(ei.mi->game_phase())
+ + eg_value(score) * int(PHASE_MIDGAME - ei.mi->game_phase()) * sf / SCALE_FACTOR_NORMAL;
- Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
+ v /= int(PHASE_MIDGAME);
- Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100,
- Options[egOpt] * eg_value(internalWeight) / 100 };
- return w;
+ // In case of tracing add all single evaluation contributions for both white and black
+ if (Trace)
+ {
+ Tracing::add_term(Tracing::PST, pos.psq_score());
+ Tracing::add_term(Tracing::IMBALANCE, ei.mi->material_value());
+ Tracing::add_term(PAWN, ei.pi->pawns_value());
+ Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility])
+ , apply_weight(mobility[BLACK], Weights[Mobility]));
+ Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
+ Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
+ Tracing::add_term(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
+ Tracing::add_term(Tracing::TOTAL, score);
+ Tracing::ei = ei;
+ Tracing::sf = sf;
+ }
+
+ return pos.side_to_move() == WHITE ? v : -v;
}
return ss.str();
}
-}
+
+} // namespace
+
+
+namespace Eval {
+
+ /// evaluate() is the main evaluation function. It returns a static evaluation
+ /// of the position always from the point of view of the side to move.
+
+ Value evaluate(const Position& pos) {
+ return do_evaluate<false>(pos);
+ }
+
+
+ /// 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);
+ }
+
+
+ /// init() computes evaluation weights from the corresponding UCI parameters
+ /// and setup king tables.
+
+ void init() {
+
+ Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]);
+ Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]);
+ Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
+ Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
+ Weights[KingSafety] = weight_option("King Safety", "King Safety", WeightsInternal[KingSafety]);
+
+ const double MaxSlope = 30;
+ const double Peak = 1280;
+
+ for (int t = 0, i = 1; i < 100; ++i)
+ {
+ t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope)));
+ KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]);
+ }
+ }
+
+} // namespace Eval