X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fevaluate.cpp;h=9a55fbd88789a5096731061fb66d4109591cf8b6;hb=cd065dd584d080f56735007f7ff52bd1ada2fea3;hp=ede94ec0d7beaaeaf5d34b2d035a961fc7dde37c;hpb=db229504e2c68a92177985bfbf7a870349257301;p=stockfish
diff --git a/src/evaluate.cpp b/src/evaluate.cpp
index ede94ec0..9a55fbd8 100644
--- a/src/evaluate.cpp
+++ b/src/evaluate.cpp
@@ -17,10 +17,10 @@
along with this program. If not, see .
*/
+#include
#include
#include
#include
-#include
#include "bitcount.h"
#include "evaluate.h"
@@ -76,7 +76,7 @@ namespace {
namespace Tracing {
enum Terms { // First 8 entries are for PieceType
- PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB
+ MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB
};
Score terms[COLOR_NB][TERMS_NB];
@@ -89,23 +89,15 @@ namespace {
std::string do_trace(const Position& pos);
}
- // Evaluation weights, initialized from UCI options
- enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
- struct Weight { int mg, eg; } Weights[6];
+ // 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}
+ };
typedef Value V;
#define S(mg, eg) make_score(mg, eg)
- // Internal evaluation weights. These are applied on top of the evaluation
- // weights read from UCI parameters. The purpose is to be able to change
- // the evaluation weights while keeping the default values of the UCI
- // parameters at 100, which looks prettier.
- //
- // 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)
- };
-
// MobilityBonus[PieceType][attacked] contains bonuses for middle and end
// game, indexed by piece type and number of attacked squares not occupied by
// friendly pieces.
@@ -155,19 +147,20 @@ namespace {
// ThreatenedByPawn[PieceType] contains a penalty according to which piece
// type is attacked by an enemy pawn.
const Score ThreatenedByPawn[] = {
- S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
+ S(0, 0), S(0, 0), S(80, 119), S(80, 119), S(117, 199), S(127, 218)
};
+ // Hanging contains a bonus for each enemy hanging piece
+ const Score Hanging = S(23, 20);
+
#undef S
- const Score Tempo = make_score(24, 11);
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 MinorBehindPawn = make_score(16, 0);
- const Score UndefendedMinor = make_score(25, 10);
- const Score TrappedRook = make_score(90, 0);
+ const Score TrappedRook = make_score(92, 0);
const Score Unstoppable = make_score( 0, 20);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
@@ -200,10 +193,11 @@ namespace {
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];
+ // KingDanger[attackUnits] contains the actual king danger weighted
+ // scores, indexed by a calculated integer number.
+ Score KingDanger[128];
+ const int ScalePawnSpan[2] = { 38, 56 };
// apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow
Score apply_weight(Score v, const Weight& w) {
@@ -211,17 +205,6 @@ namespace {
}
- // weight_option() computes the value of an evaluation weight, by combining
- // two UCI-configurable weights (midgame and endgame) with an internal weight.
-
- Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
-
- Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100,
- Options[egOpt] * eg_value(internalWeight) / 100 };
- return w;
- }
-
-
// init_eval_info() initializes king bitboards for given color adding
// pawn attacks. To be done at the beginning of the evaluation.
@@ -249,10 +232,10 @@ namespace {
}
- // evaluate_outposts() evaluates bishop and knight outpost squares
+ // evaluate_outpost() evaluates bishop and knight outpost squares
template
- Score evaluate_outposts(const Position& pos, EvalInfo& ei, Square s) {
+ Score evaluate_outpost(const Position& pos, const EvalInfo& ei, Square s) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -272,7 +255,7 @@ namespace {
bonus += bonus / 2;
}
- return make_score(bonus, bonus);
+ return make_score(bonus * 2, bonus / 2);
}
@@ -333,9 +316,9 @@ namespace {
if (Pt == BISHOP)
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
- // Bishop and knight outposts squares
+ // Bishop and knight outpost square
if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
- score += evaluate_outposts(pos, ei, s);
+ score += evaluate_outpost(pos, ei, s);
// Bishop or knight behind a pawn
if ( relative_rank(Us, s) < RANK_5
@@ -367,7 +350,7 @@ namespace {
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 * 8, 0)) * (1 + !pos.can_castle(Us));
+ score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us));
}
// An important Chess960 pattern: A cornered bishop blocked by a friendly
@@ -443,9 +426,7 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
if (b)
- attackUnits += QueenContactCheck
- * popcount(b)
- * (Them == pos.side_to_move() ? 2 : 1);
+ attackUnits += QueenContactCheck * popcount(b);
}
// Analyse the enemy's safe rook contact checks. Firstly, find the
@@ -463,9 +444,7 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b)
- attackUnits += RookContactCheck
- * popcount(b)
- * (Them == pos.side_to_move() ? 2 : 1);
+ attackUnits += RookContactCheck * popcount(b);
}
// Analyse the enemy's safe distance checks for sliders and knights
@@ -499,7 +478,7 @@ namespace {
// 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)
@@ -517,17 +496,19 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
- Bitboard b, undefendedMinors, weakEnemies;
+ Bitboard b, weakEnemies, protectedEnemies;
Score score = SCORE_ZERO;
+ enum { Minor, Major };
- // Undefended minors get penalized even if they are not under attack
- undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT)
- & ~ei.attackedBy[Them][ALL_PIECES];
+ // Protected enemies
+ protectedEnemies = (pos.pieces(Them) ^ pos.pieces(Them,PAWN))
+ & ei.attackedBy[Them][PAWN]
+ & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
- if (undefendedMinors)
- score += UndefendedMinor;
+ if(protectedEnemies)
+ score += Threat[Minor][type_of(pos.piece_on(lsb(protectedEnemies)))];
- // 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];
@@ -537,11 +518,15 @@ namespace {
{
b = weakEnemies & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
if (b)
- score += Threat[0][type_of(pos.piece_on(lsb(b)))];
+ score += Threat[Minor][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)))];
+ score += Threat[Major][type_of(pos.piece_on(lsb(b)))];
+
+ b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES];
+ if (b)
+ score += more_than_one(b) ? Hanging * popcount(b) : Hanging;
}
if (Trace)
@@ -590,22 +575,18 @@ namespace {
// If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq))
{
- squaresToQueen = forward_bb(Us, s);
-
- // 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 ( unlikely(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][ALL_PIECES] | pos.pieces(Them));
-
- if ( unlikely(forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN))
- && (forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN) & pos.attacks_from(s)))
- defendedSquares = squaresToQueen;
- else
- defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
+ // If there is a rook or queen attacking/defending the pawn from behind,
+ // consider all the squaresToQueen. Otherwise consider only the squares
+ // in the pawn's path attacked or occupied by the enemy.
+ defendedSquares = unsafeSquares = squaresToQueen = forward_bb(Us, s);
+
+ Bitboard bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from(s);
+
+ if (!(pos.pieces(Us) & bb))
+ defendedSquares &= ei.attackedBy[Us][ALL_PIECES];
+
+ if (!(pos.pieces(Them) & bb))
+ unsafeSquares &= ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
// If there aren't any enemy attacks, assign a big bonus. Otherwise
// assign a smaller bonus if the block square isn't attacked.
@@ -621,6 +602,8 @@ namespace {
mbonus += k * rr, ebonus += k * rr;
}
+ else if(pos.pieces(Us) & blockSq)
+ mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2;
} // rr != 0
if (pos.count(Us) < pos.count(Them))
@@ -638,17 +621,14 @@ namespace {
// 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.
+ // candidate pawns. In case both players have no pieces but pawns, this is
+ // somewhat related to the possibility that pawns are unstoppable.
- Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) {
+ Score evaluate_unstoppable_pawns(Color us, const EvalInfo& ei) {
Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us);
- if (!b || pos.non_pawn_material(~us))
- return SCORE_ZERO;
-
- return Unstoppable * int(relative_rank(us, frontmost_sq(us, b)));
+ return b ? Unstoppable * int(relative_rank(us, frontmost_sq(us, b))) : SCORE_ZERO;
}
@@ -696,9 +676,9 @@ namespace {
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);
+ // in the position object (material + piece square tables).
+ // Score is computed from the point of view of white.
+ score = pos.psq_score();
// Probe the material hash table
ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames);
@@ -741,10 +721,10 @@ namespace {
score += evaluate_passed_pawns(pos, ei)
- evaluate_passed_pawns(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);
+ // 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);
// Evaluate space for both sides, only in middlegame
if (ei.mi->space_weight())
@@ -754,41 +734,47 @@ namespace {
}
// 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);
+ Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;
+ ScaleFactor sf = ei.mi->scale_factor(pos, strongSide);
- // If we don't already have an unusual scale factor, check for opposite
- // colored bishop endgames, and use a lower scale for those.
+ // If we don't already have an unusual scale factor, check for certain
+ // types of 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(WHITE) + pos.count(BLACK) == 1);
- sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
+ if (pos.opposite_bishops()) {
+ // 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(WHITE) + pos.count(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);
+ } else if ( abs(eg_value(score)) <= BishopValueEg
+ && ei.pi->pawn_span(strongSide) <= 1
+ && !pos.pawn_passed(~strongSide, pos.king_square(~strongSide))) {
+ // Endings where weaker side can be place his king in front of the opponent's pawns are drawish.
+ sf = ScaleFactor(ScalePawnSpan[ei.pi->pawn_span(strongSide)]);
}
- 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);
}
// 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;
- v /= PHASE_MIDGAME;
+ v /= int(PHASE_MIDGAME);
// 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::MATERIAL, 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])
@@ -821,13 +807,13 @@ namespace {
Score bScore = terms[BLACK][idx];
switch (idx) {
- case PST: case IMBALANCE: case PAWN: case TOTAL:
- ss << std::setw(20) << name << " | --- --- | --- --- | "
+ case MATERIAL: case IMBALANCE: case PAWN: case TOTAL:
+ ss << std::setw(15) << name << " | --- --- | --- --- | "
<< std::setw(5) << to_cp(mg_value(wScore - bScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n";
break;
default:
- ss << std::setw(20) << name << " | " << std::noshowpos
+ ss << std::setw(15) << name << " | " << std::noshowpos
<< std::setw(5) << to_cp(mg_value(wScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore)) << " | "
<< std::setw(5) << to_cp(mg_value(bScore)) << " "
@@ -846,12 +832,12 @@ namespace {
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";
+ << " 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, "Material", MATERIAL);
+ format_row(ss, "Imbalance", IMBALANCE);
format_row(ss, "Pawns", PAWN);
format_row(ss, "Knights", KNIGHT);
format_row(ss, "Bishops", BISHOP);
@@ -863,7 +849,7 @@ namespace {
format_row(ss, "Passed pawns", PASSED);
format_row(ss, "Space", SPACE);
- ss << "---------------------+-------------+-------------+-------------\n";
+ ss << "----------------+-------------+-------------+-------------\n";
format_row(ss, "Total", TOTAL);
ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n";
@@ -880,7 +866,7 @@ namespace Eval {
/// of the position always from the point of view of the side to move.
Value evaluate(const Position& pos) {
- return do_evaluate(pos);
+ return do_evaluate(pos) + Tempo;
}
@@ -898,22 +884,13 @@ namespace Eval {
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 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[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
- KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
+ KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]);
}
}