]> git.sesse.net Git - stockfish/blobdiff - src/evaluate.cpp
Document why we use per-thread pawn and material tables
[stockfish] / src / evaluate.cpp
index 86cebecb7efb8ba736d8e818dcc6b31e09cf96dc..25451da5211aef741986626c52552a5a2d7fdff9 100644 (file)
@@ -23,6 +23,9 @@
 ////
 
 #include <cassert>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
 
 #include "bitcount.h"
 #include "evaluate.h"
@@ -45,11 +48,6 @@ namespace {
     // Pointer to pawn hash table entry
     PawnInfo* pi;
 
-    // updateKingTables[color] is set to true if we have enough material
-    // to trigger the opponent's king safety calculation. When is false we
-    // skip the time consuming update of the king attackers tables.
-    bool updateKingTables[2];
-
     // attackedBy[color][piece type] is a bitboard representing all squares
     // attacked by a given color and piece type, attackedBy[color][0] contains
     // all squares attacked by the given color.
@@ -173,6 +171,11 @@ namespace {
   // right to castle.
   const Value TrappedRookPenalty = Value(180);
 
+  // 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 TrappedBishopA1H1Penalty = make_score(100, 100);
+
   // The SpaceMask[Color] contains the area of the board which is considered
   // by the space evaluation. In the middle game, each side is given a bonus
   // based on how many squares inside this area are safe and available for
@@ -219,23 +222,35 @@ namespace {
   // weighted scores, indexed by color and by a calculated integer number.
   Score KingDangerTable[2][128];
 
+  // TracedTerms[Color][PieceType || TracedType] contains a breakdown of the
+  // evaluation terms, used when tracing.
+  Score TracedTerms[2][16];
+  std::stringstream TraceStream;
+
+  enum TracedType {
+      PST = 8, IMBALANCE = 9, MOBILITY = 10, THREAT = 11,
+      PASSED = 12, UNSTOPPABLE = 13, SPACE = 14, TOTAL = 15
+  };
+
   // Pawn and material hash tables, indexed by the current thread id.
-  // Note that they will be initialized at 0 being global variables.
+  // We use per-thread tables so that once we get a pointer to an entry
+  // its life time is unlimited and we don't have to care about someone
+  // changing the entry under our feet.
   MaterialInfoTable* MaterialTable[MAX_THREADS];
   PawnInfoTable* PawnTable[MAX_THREADS];
 
   // Function prototypes
-  template<bool HasPopCnt>
+  template<bool HasPopCnt, bool Trace>
   Value do_evaluate(const Position& pos, Value& margin);
 
   template<Color Us, bool HasPopCnt>
   void init_eval_info(const Position& pos, EvalInfo& ei);
 
-  template<Color Us, bool HasPopCnt>
+  template<Color Us, bool HasPopCnt, bool Trace>
   Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
 
-  template<Color Us, bool HasPopCnt>
-  Score evaluate_king(const Position& pos, EvalInfo& ei, Value& margin);
+  template<Color Us, bool HasPopCnt, bool Trace>
+  Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]);
 
   template<Color Us>
   Score evaluate_threats(const Position& pos, EvalInfo& ei);
@@ -246,23 +261,23 @@ namespace {
   template<Color Us>
   Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei);
 
-  Score apply_weight(Score v, Score weight);
+  template<bool HasPopCnt>
+  Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
+
+  inline Score apply_weight(Score v, Score weight);
   Value scale_by_game_phase(const Score& v, Phase ph, ScaleFactor sf);
   Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight);
   void init_safety();
 }
 
 
-////
-//// Functions
-////
-
-
-/// Prefetches in pawn hash tables
+/// prefetchTables() is called in do_move() to prefetch pawn and material
+/// hash tables data that will be needed shortly after in evaluation.
 
-void prefetchPawn(Key key, int threadID) {
+void prefetchTables(Key pKey, Key mKey, int threadID) {
 
-    PawnTable[threadID]->prefetch(key);
+    PawnTable[threadID]->prefetch(pKey);
+    MaterialTable[threadID]->prefetch(mKey);
 }
 
 
@@ -271,16 +286,25 @@ void prefetchPawn(Key key, int threadID) {
 /// between them based on the remaining material.
 Value evaluate(const Position& pos, Value& margin) {
 
-    return CpuHasPOPCNT ? do_evaluate<true>(pos, margin)
-                        : do_evaluate<false>(pos, margin);
+    return CpuHasPOPCNT ? do_evaluate<true, false>(pos, margin)
+                        : do_evaluate<false, false>(pos, margin);
 }
 
 namespace {
 
-template<bool HasPopCnt>
+double to_cp(Value v) { return double(v) / double(PawnValueMidgame); }
+
+void trace_add(int idx, Score term_w, Score term_b = Score(0)) {
+
+    TracedTerms[WHITE][idx] = term_w;
+    TracedTerms[BLACK][idx] = term_b;
+}
+
+template<bool HasPopCnt, bool Trace>
 Value do_evaluate(const Position& pos, Value& margin) {
 
   EvalInfo ei;
+  Value margins[2];
   Score mobilityWhite, mobilityBlack;
 
   assert(pos.is_ok());
@@ -291,9 +315,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
   // in the position object (material + piece square tables).
   Score bonus = pos.value();
 
-  // margin is the uncertainty estimation of position's evaluation
-  // and typically is used by the search for pruning decisions.
-  margin = VALUE_ZERO;
+  // margins[] store the uncertainty estimation of position's evaluation
+  // that typically is used by the search for pruning decisions.
+  margins[WHITE] = margins[BLACK] = VALUE_ZERO;
 
   // Probe the material hash table
   MaterialInfo* mi = MaterialTable[pos.thread()]->get_material_info(pos);
@@ -302,7 +326,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
   // If we have a specialized evaluation function for the current material
   // configuration, call it and return.
   if (mi->specialized_eval_exists())
+  {
+      margin = VALUE_ZERO;
       return mi->evaluate(pos);
+  }
 
   // Probe the pawn hash table
   ei.pi = PawnTable[pos.thread()]->get_pawn_info(pos);
@@ -313,15 +340,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
   init_eval_info<BLACK, HasPopCnt>(pos, ei);
 
   // Evaluate pieces and mobility
-  bonus +=  evaluate_pieces_of_color<WHITE, HasPopCnt>(pos, ei, mobilityWhite)
-          - evaluate_pieces_of_color<BLACK, HasPopCnt>(pos, ei, mobilityBlack);
+  bonus +=  evaluate_pieces_of_color<WHITE, HasPopCnt, Trace>(pos, ei, mobilityWhite)
+          - evaluate_pieces_of_color<BLACK, HasPopCnt, Trace>(pos, ei, mobilityBlack);
 
   bonus += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]);
 
   // Evaluate kings after all other pieces because we need complete attack
   // information when computing the king safety evaluation.
-  bonus +=  evaluate_king<WHITE, HasPopCnt>(pos, ei, margin)
-          - evaluate_king<BLACK, HasPopCnt>(pos, ei, margin);
+  bonus +=  evaluate_king<WHITE, HasPopCnt, Trace>(pos, ei, margins)
+          - evaluate_king<BLACK, HasPopCnt, Trace>(pos, ei, margins);
 
   // Evaluate tactical threats, we need full attack information including king
   bonus +=  evaluate_threats<WHITE>(pos, ei)
@@ -331,18 +358,31 @@ Value do_evaluate(const Position& pos, Value& margin) {
   bonus +=  evaluate_passed_pawns<WHITE>(pos, ei)
           - evaluate_passed_pawns<BLACK>(pos, ei);
 
-  Phase phase = mi->game_phase();
+  // If one side has only a king, check whether exists any unstoppable passed pawn
+  if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
+  {
+      bonus += evaluate_unstoppable_pawns<HasPopCnt>(pos, ei);
+
+      if (Trace)
+          trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns<HasPopCnt>(pos, ei));
+  }
 
   // Evaluate space for both sides, only in middle-game.
-  if (phase > PHASE_ENDGAME && mi->space_weight() > 0)
+  if (mi->space_weight())
   {
-      int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(pos, ei);
-      bonus += apply_weight(make_score(s * mi->space_weight(), 0), Weights[Space]);
+      int s_w = evaluate_space<WHITE, HasPopCnt>(pos, ei);
+      int s_b = evaluate_space<BLACK, HasPopCnt>(pos, ei);
+      bonus += apply_weight(make_score((s_w - s_b) * mi->space_weight(), 0), Weights[Space]);
+
+      if (Trace)
+          trace_add(SPACE, apply_weight(make_score(s_w * mi->space_weight(), make_score(0, 0)), Weights[Space]),
+                           apply_weight(make_score(s_b * mi->space_weight(), make_score(0, 0)), Weights[Space]));
   }
 
   // Scale winning side if position is more drawish that what it appears
-  ScaleFactor sf = eg_value(bonus) > VALUE_ZERO ? mi->scale_factor(pos, WHITE)
+  ScaleFactor sf = eg_value(bonus) > VALUE_DRAW ? mi->scale_factor(pos, WHITE)
                                                 : mi->scale_factor(pos, BLACK);
+  Phase phase = mi->game_phase();
 
   // If we don't already have an unusual scale factor, check for opposite
   // colored bishop endgames, and use a lower scale for those.
@@ -366,7 +406,27 @@ Value do_evaluate(const Position& pos, Value& margin) {
   }
 
   // Interpolate between the middle game and the endgame score
+  margin = margins[pos.side_to_move()];
   Value v = scale_by_game_phase(bonus, phase, sf);
+
+  if (Trace)
+  {
+      trace_add(PST, pos.value());
+      trace_add(IMBALANCE, mi->material_value());
+      trace_add(PAWN, apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]));
+      trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility]));
+      trace_add(THREAT, evaluate_threats<WHITE>(pos, ei), evaluate_threats<BLACK>(pos, ei));
+      trace_add(PASSED, evaluate_passed_pawns<WHITE>(pos, ei), evaluate_passed_pawns<BLACK>(pos, ei));
+      trace_add(TOTAL, bonus);
+      TraceStream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
+                  << ", Black: " << to_cp(margins[BLACK])
+                  << "\nScaling: " << std::noshowpos
+                  << std::setw(6) << 100.0 * phase/128.0 << "% MG, "
+                  << std::setw(6) << 100.0 * (1.0 - phase/128.0) << "% * "
+                  << std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n"
+                  << "Total evaluation: " << to_cp(v);
+  }
+
   return pos.side_to_move() == WHITE ? v : -v;
 }
 
@@ -408,7 +468,7 @@ void quit_eval() {
 
 /// read_weights() reads evaluation weights from the corresponding UCI parameters
 
-void read_weights(Color us) {
+void read_evaluation_uci_options(Color us) {
 
   // King safety is asymmetrical. Our king danger level is weighted by
   // "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness".
@@ -424,7 +484,7 @@ void read_weights(Color us) {
 
   // If running in analysis mode, make sure we use symmetrical king safety. We do this
   // by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average.
-  if (get_option_value_bool("UCI_AnalyseMode"))
+  if (Options["UCI_AnalyseMode"].value<bool>())
       Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2;
 
   init_safety();
@@ -443,15 +503,18 @@ namespace {
     const Color Them = (Us == WHITE ? BLACK : WHITE);
 
     Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
-    ei.kingZone[Us] = (b | (Us == WHITE ? b >> 8 : b << 8));
     ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
-    ei.updateKingTables[Us] = pos.piece_count(Us, QUEEN) && pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame;
-    if (ei.updateKingTables[Us])
+
+    // Init king safety tables only if we are going to use them
+    if (   pos.piece_count(Us, QUEEN)
+        && pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame)
     {
+        ei.kingZone[Us] = (b | (Us == WHITE ? b >> 8 : b << 8));
         b &= ei.attackedBy[Us][PAWN];
-        ei.kingAttackersCount[Us] = b ? count_1s<Max15>(b) / 2 : EmptyBoardBB;
-        ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = EmptyBoardBB;
-    }
+        ei.kingAttackersCount[Us] = b ? count_1s<Max15>(b) / 2 : 0;
+        ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
+    } else
+        ei.kingZone[Us] = ei.kingAttackersCount[Us] = 0;
   }
 
 
@@ -483,7 +546,7 @@ namespace {
 
   // evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color
 
-  template<PieceType Piece, Color Us, bool HasPopCnt>
+  template<PieceType Piece, Color Us, bool HasPopCnt, bool Trace>
   Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) {
 
     Bitboard b;
@@ -515,7 +578,7 @@ namespace {
         ei.attackedBy[Us][Piece] |= b;
 
         // King attacks
-        if (ei.updateKingTables[Us] && (b & ei.kingZone[Us]))
+        if (b & ei.kingZone[Us])
         {
             ei.kingAttackersCount[Us]++;
             ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
@@ -547,6 +610,27 @@ namespace {
             bonus += (Piece == ROOK ? RookOn7thBonus : QueenOn7thBonus);
         }
 
+        // Special extra evaluation for bishops
+        if (Piece == BISHOP && pos.is_chess960())
+        {
+            // An important Chess960 pattern: A cornered bishop blocked by
+            // a friendly pawn diagonally in front of it is a very serious
+            // problem, especially when that pawn is also blocked.
+            if (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))
+            {
+                Square d = pawn_push(Us) + (square_file(s) == FILE_A ? DELTA_E : DELTA_W);
+                if (pos.piece_on(s + d) == make_piece(Us, PAWN))
+                {
+                    if (!pos.square_is_empty(s + d + pawn_push(Us)))
+                        bonus -= 2*TrappedBishopA1H1Penalty;
+                    else if (pos.piece_on(s + 2*d) == make_piece(Us, PAWN))
+                        bonus -= TrappedBishopA1H1Penalty;
+                    else
+                        bonus -= TrappedBishopA1H1Penalty / 2;
+                }
+            }
+        }
+
         // Special extra evaluation for rooks
         if (Piece == ROOK)
         {
@@ -587,6 +671,10 @@ namespace {
             }
         }
     }
+
+    if (Trace)
+        TracedTerms[Us][Piece] = bonus;
+
     return bonus;
   }
 
@@ -627,7 +715,7 @@ namespace {
   // evaluate_pieces_of_color<>() assigns bonuses and penalties to all the
   // pieces of a given color.
 
-  template<Color Us, bool HasPopCnt>
+  template<Color Us, bool HasPopCnt, bool Trace>
   Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) {
 
     const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -637,10 +725,10 @@ namespace {
     // Do not include in mobility squares protected by enemy pawns or occupied by our pieces
     const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces_of_color(Us));
 
-    bonus += evaluate_pieces<KNIGHT, Us, HasPopCnt>(pos, ei, mobility, mobilityArea);
-    bonus += evaluate_pieces<BISHOP, Us, HasPopCnt>(pos, ei, mobility, mobilityArea);
-    bonus += evaluate_pieces<ROOK,   Us, HasPopCnt>(pos, ei, mobility, mobilityArea);
-    bonus += evaluate_pieces<QUEEN,  Us, HasPopCnt>(pos, ei, mobility, mobilityArea);
+    bonus += evaluate_pieces<KNIGHT, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
+    bonus += evaluate_pieces<BISHOP, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
+    bonus += evaluate_pieces<ROOK,   Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
+    bonus += evaluate_pieces<QUEEN,  Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
 
     // Sum up all attacked squares
     ei.attackedBy[Us][0] =   ei.attackedBy[Us][PAWN]   | ei.attackedBy[Us][KNIGHT]
@@ -652,8 +740,8 @@ namespace {
 
   // evaluate_king<>() assigns bonuses and penalties to a king of a given color
 
-  template<Color Us, bool HasPopCnt>
-  Score evaluate_king(const Position& pos, EvalInfo& ei, Value& margin) {
+  template<Color Us, bool HasPopCnt, bool Trace>
+  Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) {
 
     const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
     const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -667,8 +755,7 @@ namespace {
 
     // King safety. This is quite complicated, and is almost certainly far
     // from optimally tuned.
-    if (   ei.updateKingTables[Them]
-        && ei.kingAttackersCount[Them] >= 2
+    if (   ei.kingAttackersCount[Them] >= 2
         && ei.kingAdjacentZoneAttacksCount[Them])
     {
         // Find the attacked squares around the king which has no defenders
@@ -755,9 +842,12 @@ namespace {
         // be very big, and so capturing a single attacking piece can therefore
         // result in a score change far bigger than the value of the captured piece.
         bonus -= KingDangerTable[Us][attackUnits];
-        if (pos.side_to_move() == Us)
-            margin += mg_value(KingDangerTable[Us][attackUnits]);
+        margins[Us] += mg_value(KingDangerTable[Us][attackUnits]);
     }
+
+    if (Trace)
+        TracedTerms[Us][KING] = bonus;
+
     return bonus;
   }
 
@@ -806,8 +896,8 @@ namespace {
                 // If there is an enemy rook or queen attacking the pawn from behind,
                 // add all X-ray attacks by the rook or queen. Otherwise consider only
                 // the squares in the pawn's path attacked or occupied by the enemy.
-                if (   (squares_behind(Us, s) & pos.pieces(ROOK, QUEEN, Them))
-                    && (squares_behind(Us, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<ROOK>(s)))
+                if (   (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them))
+                    && (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<ROOK>(s)))
                     unsafeSquares = squaresToQueen;
                 else
                     unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces_of_color(Them));
@@ -859,6 +949,168 @@ namespace {
     return apply_weight(bonus, Weights[PassedPawns]);
   }
 
+  // evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides
+  template<bool HasPopCnt>
+  Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) {
+
+    const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
+
+    // Step 1. Hunt for unstoppable pawns. If we find at least one, record how many plies
+    // are required for promotion
+    int pliesToGo[2] = {256, 256};
+
+    for (Color c = WHITE; c <= BLACK; c++)
+    {
+        // Skip if other side has non-pawn pieces
+        if (pos.non_pawn_material(opposite_color(c)))
+            continue;
+
+        Bitboard b = ei.pi->passed_pawns(c);
+
+        while (b)
+        {
+            Square s = pop_1st_bit(&b);
+            Square queeningSquare = relative_square(c, make_square(square_file(s), RANK_8));
+
+            int mtg = RANK_8 - relative_rank(c, s) - int(relative_rank(c, s) == RANK_2);
+            int oppmtg = square_distance(pos.king_square(opposite_color(c)), queeningSquare) - int(c != pos.side_to_move());
+            bool pathDefended = ((ei.attackedBy[c][0] & squares_in_front_of(c, s)) == squares_in_front_of(c, s));
+
+            if (mtg >= oppmtg && !pathDefended)
+                continue;
+
+            int blockerCount = count_1s<Max15>(squares_in_front_of(c, s) & pos.occupied_squares());
+            mtg += blockerCount;
+
+            if (mtg >= oppmtg && !pathDefended)
+                continue;
+
+            int ptg = 2 * mtg - int(c == pos.side_to_move());
+
+            if (ptg < pliesToGo[c])
+                pliesToGo[c] = ptg;
+        }
+    }
+
+    // Step 2. If either side cannot promote at least three plies before the other side then
+    // situation becomes too complex and we give up. Otherwise we determine the possibly "winning side"
+    if (abs(pliesToGo[WHITE] - pliesToGo[BLACK]) < 3)
+        return make_score(0, 0);
+
+    Color winnerSide = (pliesToGo[WHITE] < pliesToGo[BLACK] ? WHITE : BLACK);
+    Color loserSide = opposite_color(winnerSide);
+
+    // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
+    // We collect the potential candidates in potentialBB.
+    Bitboard pawnBB = pos.pieces(PAWN, loserSide);
+    Bitboard potentialBB = pawnBB;
+    const Bitboard passedBB = ei.pi->passed_pawns(loserSide);
+
+    while(pawnBB)
+    {
+        Square psq = pop_1st_bit(&pawnBB);
+
+        // Check direct advancement
+        int mtg = RANK_8 - relative_rank(loserSide, psq) - int(relative_rank(loserSide, psq) == RANK_2);
+        int ptg = 2 * mtg - int(loserSide == pos.side_to_move());
+
+        // Check if (without even considering any obstacles) we're too far away
+        if (pliesToGo[winnerSide] + 3 <= ptg)
+        {
+            clear_bit(&potentialBB, psq);
+            continue;
+        }
+
+        // If this is passed pawn, then it _may_ promote in time. We give up.
+        if (bit_is_set(passedBB, psq))
+            return make_score(0, 0);
+
+        // Doubled pawn is worthless
+        if (squares_in_front_of(loserSide, psq) & (pos.pieces(PAWN, loserSide)))
+        {
+            clear_bit(&potentialBB, psq);
+            continue;
+        }
+    }
+
+    // Step 4. Check new passed pawn creation through king capturing and sacrifises
+    pawnBB = potentialBB;
+
+    while(pawnBB)
+    {
+        Square psq = pop_1st_bit(&pawnBB);
+
+        int mtg = RANK_8 - relative_rank(loserSide, psq) - int(relative_rank(loserSide, psq) == RANK_2);
+        int ptg = 2 * mtg - int(loserSide == pos.side_to_move());
+
+        // Generate list of obstacles
+        Bitboard obsBB = passed_pawn_mask(loserSide, psq) & pos.pieces(PAWN, winnerSide);
+        const bool pawnIsOpposed = squares_in_front_of(loserSide, psq) & obsBB;
+        assert(obsBB);
+
+        // How many plies does it take to remove all the obstacles?
+        int sacptg = 0;
+        int realObsCount = 0;
+        int minKingDist = 256;
+
+        while(obsBB)
+        {
+            Square obSq = pop_1st_bit(&obsBB);
+            int minMoves = 256;
+
+            // Check pawns that can give support to overcome obstacle (Eg. wp: a4,b4 bp: b2. b4 is giving support)
+            if (!pawnIsOpposed && square_file(psq) != square_file(obSq))
+            {
+                Bitboard supBB =   in_front_bb(winnerSide, Square(obSq + (winnerSide == WHITE ? 8 : -8)))
+                                 & neighboring_files_bb(psq) & potentialBB;
+
+                while(supBB) // This while-loop could be replaced with supSq = LSB/MSB(supBB) (depending on color)
+                {
+                    Square supSq = pop_1st_bit(&supBB);
+                    int dist = square_distance(obSq, supSq);
+                    minMoves = Min(minMoves, dist - 2);
+                }
+
+            }
+
+            // Check pawns that can be sacrifised
+            Bitboard sacBB = passed_pawn_mask(winnerSide, obSq) & neighboring_files_bb(obSq) & potentialBB & ~(1ULL << psq);
+
+            while(sacBB) // This while-loop could be replaced with sacSq = LSB/MSB(sacBB) (depending on color)
+            {
+                Square sacSq = pop_1st_bit(&sacBB);
+                int dist = square_distance(obSq, sacSq);
+                minMoves = Min(minMoves, dist - 2);
+            }
+
+            // If obstacle can be destroyed with immediate pawn sacrifise, it's not real obstacle
+            if (minMoves <= 0)
+                continue;
+
+            // Pawn sac calculations
+            sacptg += minMoves * 2;
+
+            // King capture calc
+            realObsCount++;
+            int kingDist = square_distance(pos.king_square(loserSide), obSq);
+            minKingDist = Min(minKingDist, kingDist);
+        }
+
+        // Check if pawn sac plan _may_ save the day
+        if (pliesToGo[winnerSide] + 3 > ptg + sacptg)
+            return make_score(0, 0);
+
+        // Check if king capture plan _may_ save the day (contains some false positives)
+        int kingptg = (minKingDist + realObsCount) * 2;
+        if (pliesToGo[winnerSide] + 3 > ptg + kingptg)
+            return make_score(0, 0);
+    }
+
+    // Step 5. Assign bonus
+    const int Sign[2] = {1, -1};
+    return Sign[winnerSide] * make_score(0, (Value) 0x500 - 0x20 * pliesToGo[winnerSide]);
+  }
+
 
   // evaluate_space() computes the space evaluation for a given side. The
   // space evaluation is a simple bonus based on the number of safe squares
@@ -906,11 +1158,9 @@ namespace {
     assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE);
     assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME);
 
-    Value eg = eg_value(v);
-    Value ev = Value((eg * int(sf)) / SCALE_FACTOR_NORMAL);
-
+    int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
     int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128;
-    return Value(result & ~(GrainSize - 1));
+    return Value((result + GrainSize / 2) & ~(GrainSize - 1));
   }
 
 
@@ -920,8 +1170,8 @@ namespace {
   Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
 
     // Scale option value from 100 to 256
-    int mg = get_option_value_int(mgOpt) * 256 / 100;
-    int eg = get_option_value_int(egOpt) * 256 / 100;
+    int mg = Options[mgOpt].value<int>() * 256 / 100;
+    int eg = Options[egOpt].value<int>() * 256 / 100;
 
     return apply_weight(make_score(mg, eg), internalWeight);
   }
@@ -952,4 +1202,75 @@ namespace {
         for (int i = 0; i < 100; i++)
             KingDangerTable[c][i] = apply_weight(make_score(t[i], 0), Weights[KingDangerUs + c]);
   }
+
+
+  // trace_row() is an helper function used by tracing code to register the
+  // values of a single evaluation term.
+
+  void trace_row(const char *name, int idx) {
+
+    Score term_w = TracedTerms[WHITE][idx];
+    Score term_b = TracedTerms[BLACK][idx];
+
+    switch (idx) {
+    case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL:
+        TraceStream << std::setw(20) << name << " |   ---   --- |   ---   --- | "
+                    << std::setw(6)  << to_cp(mg_value(term_w)) << " "
+                    << std::setw(6)  << to_cp(eg_value(term_w)) << " \n";
+        break;
+    default:
+        TraceStream << std::setw(20) << name << " | " << std::noshowpos
+                    << std::setw(5)  << to_cp(mg_value(term_w)) << " "
+                    << std::setw(5)  << to_cp(eg_value(term_w)) << " | "
+                    << std::setw(5)  << to_cp(mg_value(term_b)) << " "
+                    << std::setw(5)  << to_cp(eg_value(term_b)) << " | "
+                    << std::showpos
+                    << std::setw(6)  << to_cp(mg_value(term_w - term_b)) << " "
+                    << std::setw(6)  << to_cp(eg_value(term_w - term_b)) << " \n";
+    }
+  }
+}
+
+
+/// trace_evaluate() is like evaluate() but instead of a value returns a string
+/// suitable to be print on stdout with the detailed descriptions and values of
+/// each evaluation term. Used mainly for debugging.
+
+std::string trace_evaluate(const Position& pos) {
+
+    Value margin;
+    std::string totals;
+
+    TraceStream.str("");
+    TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
+    memset(TracedTerms, 0, 2 * 16 * sizeof(Score));
+
+    do_evaluate<false, true>(pos, margin);
+
+    totals = TraceStream.str();
+    TraceStream.str("");
+
+    TraceStream << std::setw(21) << "Eval term " << "|    White    |    Black    |     Total     \n"
+                <<             "                     |   MG    EG  |   MG    EG  |   MG     EG   \n"
+                <<             "---------------------+-------------+-------------+---------------\n";
+
+    trace_row("Material, PST, Tempo", PST);
+    trace_row("Material imbalance", IMBALANCE);
+    trace_row("Pawns", PAWN);
+    trace_row("Knights", KNIGHT);
+    trace_row("Bishops", BISHOP);
+    trace_row("Rooks", ROOK);
+    trace_row("Queens", QUEEN);
+    trace_row("Mobility", MOBILITY);
+    trace_row("King safety", KING);
+    trace_row("Threats", THREAT);
+    trace_row("Passed pawns", PASSED);
+    trace_row("Unstoppable pawns", UNSTOPPABLE);
+    trace_row("Space", SPACE);
+
+    TraceStream <<             "---------------------+-------------+-------------+---------------\n";
+    trace_row("Total", TOTAL);
+    TraceStream << totals;
+
+    return TraceStream.str();
 }