]> git.sesse.net Git - stockfish/blobdiff - src/evaluate.cpp
Rearrange evaluation constants definitions
[stockfish] / src / evaluate.cpp
index e96e635c876f44901a456b41189c98eddbcb85e9..98ecf72de59c4c32d84210f48ff2e4c7cc3dbf00 100644 (file)
   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"
@@ -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,37 +89,29 @@ 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.
   const Score MobilityBonus[][32] = {
     {}, {},
-    { S(-35,-30), S(-22,-20), S(-9,-10), S( 3,  0), S(15, 10), S(27, 20), // Knights
+    { S(-65,-50), S(-42,-30), 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(-52,-47), S(-28,-23), 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(-47,-53), S(-31,-26), 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( 0,  0), S( 6, 10), S(11, 19), // Queens
+    { S(-42,-40), S(-28,-23), S(-5, -7), S( 0,  0), S( 6, 10), S(11, 19), // Queens
       S( 13, 29), S( 18, 38), S(20, 40), S(21, 41), S(22, 41), S(22, 41),
       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),
@@ -155,27 +147,27 @@ 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)
   };
 
-  #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);
+  // Assorted bonuses and penalties used by evaluation
+  const Score KingOnPawnOne    = S(0 , 64);
+  const Score KingOnPawnMany   = S(0 ,128);
+  const Score RookOnPawn       = S(10, 28);
+  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(23, 20);
 
   // 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
   // happen in Chess960 games.
-  const Score TrappedBishopA1H1 = make_score(50, 50);
+  const Score TrappedBishopA1H1 = S(50, 50);
+
+  #undef S
 
   // SpaceMask[Color] contains the area of the board which is considered
   // by the space evaluation. In the middlegame, each side is given a bonus
@@ -202,195 +194,17 @@ 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];
 
-  // Function prototypes
-  template<bool Trace>
-  Value do_evaluate(const Position& pos);
-
-  template<Color Us>
-  void init_eval_info(const Position& pos, EvalInfo& ei);
-
-  template<bool Trace>
-  Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility);
-
-  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) {
-
-  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);
-
-  // Evaluate pieces and mobility
-  score += evaluate_pieces<Trace>(pos, ei, mobility);
-
-  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]);
-  }
-
-  // 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);
-  }
+  const int ScalePawnSpan[2] = { 38, 56 };
 
-  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)
-  {
-      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());
-      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;
+  // 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);
   }
 
-  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.
@@ -404,7 +218,7 @@ Value do_evaluate(const Position& pos) {
     ei.pinnedPieces[Us] = pos.pinned_pieces(Us);
 
     Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
-    ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
+    ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
 
     // Init king safety tables only if we are going to use them
     if (pos.count<QUEEN>(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
@@ -419,10 +233,10 @@ Value do_evaluate(const Position& pos) {
   }
 
 
-  // evaluate_outposts() evaluates bishop and knight outpost squares
+  // evaluate_outpost() evaluates bishop and knight outpost squares
 
   template<PieceType Pt, Color Us>
-  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);
 
@@ -442,19 +256,20 @@ Value do_evaluate(const Position& pos) {
             bonus += bonus / 2;
     }
 
-    return make_score(bonus, bonus);
+    return make_score(bonus * 2, bonus / 2);
   }
 
 
   // evaluate_pieces() assigns bonuses and penalties to the pieces of a given color
 
   template<PieceType Pt, Color Us, bool Trace>
-  Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, Bitboard mobilityArea) {
+  Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, Bitboard* mobilityArea) {
 
     Bitboard b;
     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);
 
@@ -470,7 +285,7 @@ Value do_evaluate(const Position& pos) {
         if (ei.pinnedPieces[Us] & s)
             b &= LineBB[pos.king_square(Us)][s];
 
-        ei.attackedBy[Us][Pt] |= b;
+        ei.attackedBy[Us][ALL_PIECES] |= ei.attackedBy[Us][Pt] |= b;
 
         if (b & ei.kingRing[Them])
         {
@@ -486,8 +301,8 @@ Value do_evaluate(const Position& pos) {
                    | ei.attackedBy[Them][BISHOP]
                    | ei.attackedBy[Them][ROOK]);
 
-        int mob = Pt != QUEEN ? popcount<Max15>(b & mobilityArea)
-                              : popcount<Full >(b & mobilityArea);
+        int mob = Pt != QUEEN ? popcount<Max15>(b & mobilityArea[Us])
+                              : popcount<Full >(b & mobilityArea[Us]);
 
         mobility[Us] += MobilityBonus[Pt][mob];
 
@@ -502,13 +317,9 @@ Value do_evaluate(const Position& pos) {
             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
+            // Bishop and knight outpost square
             if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
-                score += evaluate_outposts<Pt, Us>(pos, ei, s);
+                score += evaluate_outpost<Pt, Us>(pos, ei, s);
 
             // Bishop or knight behind a pawn
             if (    relative_rank(Us, s) < RANK_5
@@ -518,11 +329,6 @@ Value do_evaluate(const Position& pos) {
 
         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)
             {
@@ -532,10 +338,10 @@ Value do_evaluate(const Position& pos) {
             }
 
             // 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);
@@ -544,8 +350,8 @@ Value do_evaluate(const Position& pos) {
             // 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 * 22, 0)) * (1 + !pos.can_castle(Us));
         }
 
         // An important Chess960 pattern: A cornered bishop blocked by a friendly
@@ -566,46 +372,13 @@ Value do_evaluate(const Position& pos) {
     if (Trace)
         Tracing::terms[Us][Pt] = score;
 
-    return score;
+    return score - evaluate_pieces<NextPt, Them, Trace>(pos, ei, mobility, mobilityArea);
   }
 
-
-  // evaluate_pieces() assigns bonuses and penalties to all the pieces of both colors
-
-  template<bool Trace>
-  Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility) {
-
-    // Do not include in mobility squares protected by enemy pawns or occupied by our pieces
-    const Bitboard whiteMobilityArea = ~(ei.attackedBy[BLACK][PAWN] | pos.pieces(WHITE, PAWN, KING));
-    const Bitboard blackMobilityArea = ~(ei.attackedBy[WHITE][PAWN] | pos.pieces(BLACK, PAWN, KING));
-
-    Score score;
-
-    score  =  evaluate_pieces<KNIGHT, WHITE, Trace>(pos, ei, mobility, whiteMobilityArea)
-            - evaluate_pieces<KNIGHT, BLACK, Trace>(pos, ei, mobility, blackMobilityArea);
-    score +=  evaluate_pieces<BISHOP, WHITE, Trace>(pos, ei, mobility, whiteMobilityArea)
-            - evaluate_pieces<BISHOP, BLACK, Trace>(pos, ei, mobility, blackMobilityArea);
-    score +=  evaluate_pieces<  ROOK, WHITE, Trace>(pos, ei, mobility, whiteMobilityArea)
-            - evaluate_pieces<  ROOK, BLACK, Trace>(pos, ei, mobility, blackMobilityArea);
-    score +=  evaluate_pieces< QUEEN, WHITE, Trace>(pos, ei, mobility, whiteMobilityArea)
-            - evaluate_pieces< QUEEN, BLACK, Trace>(pos, ei, mobility, blackMobilityArea);
-
-    // Sum up all attacked squares (updated in evaluate_pieces)
-    ei.attackedBy[WHITE][ALL_PIECES] =  ei.attackedBy[WHITE][PAWN]   | ei.attackedBy[WHITE][KNIGHT]
-                                      | ei.attackedBy[WHITE][BISHOP] | ei.attackedBy[WHITE][ROOK]
-                                      | ei.attackedBy[WHITE][QUEEN]  | ei.attackedBy[WHITE][KING];
-
-    ei.attackedBy[BLACK][ALL_PIECES] =  ei.attackedBy[BLACK][PAWN]   | ei.attackedBy[BLACK][KNIGHT]
-                                      | ei.attackedBy[BLACK][BISHOP] | ei.attackedBy[BLACK][ROOK]
-                                      | ei.attackedBy[BLACK][QUEEN]  | ei.attackedBy[BLACK][KING];
-    if (Trace)
-    {
-        Tracing::terms[WHITE][Tracing::MOBILITY] = apply_weight(mobility[WHITE], Weights[Mobility]);
-        Tracing::terms[BLACK][Tracing::MOBILITY] = apply_weight(mobility[BLACK], Weights[Mobility]);
-    }
-
-    return score;
-  }
+  template<>
+  Score evaluate_pieces<KING, WHITE, false>(const Position&, EvalInfo&, Score*, Bitboard*) { return SCORE_ZERO; }
+  template<>
+  Score evaluate_pieces<KING, WHITE,  true>(const Position&, EvalInfo&, Score*, Bitboard*) { return SCORE_ZERO; }
 
 
   // evaluate_king() assigns bonuses and penalties to a king of a given color
@@ -654,9 +427,7 @@ Value do_evaluate(const Position& pos) {
                   | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
 
             if (b)
-                attackUnits +=  QueenContactCheck
-                              * popcount<Max15>(b)
-                              * (Them == pos.side_to_move() ? 2 : 1);
+                attackUnits +=  QueenContactCheck * popcount<Max15>(b);
         }
 
         // Analyse the enemy's safe rook contact checks. Firstly, find the
@@ -674,9 +445,7 @@ Value do_evaluate(const Position& pos) {
                   | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
 
             if (b)
-                attackUnits +=  RookContactCheck
-                              * popcount<Max15>(b)
-                              * (Them == pos.side_to_move() ? 2 : 1);
+                attackUnits +=  RookContactCheck * popcount<Max15>(b);
         }
 
         // Analyse the enemy's safe distance checks for sliders and knights
@@ -710,7 +479,7 @@ Value do_evaluate(const Position& pos) {
 
         // 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)
@@ -728,17 +497,19 @@ Value do_evaluate(const Position& pos) {
 
     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];
@@ -748,12 +519,20 @@ Value do_evaluate(const Position& pos) {
     {
         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<Max15>(b) : Hanging;
+
+        b = weakEnemies & pos.pieces(Them, PAWN) & ei.attackedBy[Us][KING];
+        if (b)
+            score += more_than_one(b) ? KingOnPawnMany : KingOnPawnOne;
+       }
 
     if (Trace)
         Tracing::terms[Us][Tracing::THREAT] = score;
@@ -769,7 +548,7 @@ Value do_evaluate(const Position& pos) {
 
     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);
@@ -780,87 +559,58 @@ Value do_evaluate(const Position& pos) {
 
         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))
             {
-                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<ROOK>(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<ROOK>(s)))
-                    defendedSquares = squaresToQueen;
-                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;
-
-                // 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 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<ROOK>(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.
+                int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 0;
+
+                // 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;
             }
+            else if(pos.pieces(Us) & blockSq)
+                mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2;
         } // 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;
 
@@ -876,17 +626,14 @@ Value do_evaluate(const Position& pos) {
 
 
   // 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;
   }
 
 
@@ -922,33 +669,130 @@ Value do_evaluate(const Position& pos) {
   }
 
 
-  // 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).
+    // 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);
+    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 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())
+    {
+        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
+    Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;
+    ScaleFactor sf = ei.mi->scale_factor(pos, strongSide);
 
-    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 certain
+    // types of endgames, and use a lower scale for those.
+    if (    ei.mi->game_phase() < PHASE_MIDGAME
+        && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
+    {
+        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<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);
+        } 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)]);
+        }
+    }
 
-  // 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::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])
+                                           , 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;
   }
 
 
@@ -968,13 +812,13 @@ Value do_evaluate(const Position& pos) {
     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)) << " "
@@ -993,12 +837,12 @@ Value do_evaluate(const Position& pos) {
 
     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);
@@ -1010,11 +854,49 @@ Value do_evaluate(const Position& pos) {
     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";
 
     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) + Tempo;
+  }
+
+
+  /// 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() {
+
+    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