]> git.sesse.net Git - stockfish/blobdiff - src/evaluate.cpp
Big reshuffle in evaluate.cpp
[stockfish] / src / evaluate.cpp
index 3228c9a129df5e84903d2984e7e581a24482f07c..121f2c603293845e3b2ff29998a9e4a4e8c89fbb 100644 (file)
@@ -206,199 +206,37 @@ namespace {
   // 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);
+  // apply_weight() weights score v by score 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);
   }
 
 
-  /// 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;
+  // weight_option() computes the value of an evaluation weight, by combining
+  // two UCI-configurable weights (midgame and endgame) with an internal weight.
 
-    for (int t = 0, i = 1; i < 100; ++i)
-    {
-        t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope));
+  Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
 
-        KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
-        KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
-    }
+    Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100,
+                 Options[egOpt] * eg_value(internalWeight) / 100 };
+    return w;
   }
 
-} // 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);
-
-  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]);
-  }
-
-  // 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);
+  // interpolate() interpolates between a middlegame and an endgame score,
+  // based on game phase. It also scales the return value by a ScaleFactor array.
 
-  // 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);
-  }
+  Value interpolate(const Score& v, Phase ph, ScaleFactor sf) {
 
-  Value v = interpolate(score, ei.mi->game_phase(), sf);
+    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);
 
-  // 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;
+    int eg = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
+    return Value((mg_value(v) * int(ph) + eg * int(PHASE_MIDGAME - ph)) / PHASE_MIDGAME);
   }
 
-  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.
@@ -463,6 +301,7 @@ Value do_evaluate(const Position& pos) {
     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);
 
@@ -574,8 +413,6 @@ Value do_evaluate(const Position& pos) {
     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);
   }
 
@@ -899,33 +736,120 @@ 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) 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() 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.
+    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;
   }
 
 
@@ -994,4 +918,52 @@ Value do_evaluate(const Position& pos) {
 
     return ss.str();
   }
-}
+
+} // namespace
+
+
+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