]> git.sesse.net Git - stockfish/blobdiff - src/evaluate.cpp
Safer razoring formula
[stockfish] / src / evaluate.cpp
index 864af221de16ce2e490c5c13e8309c9a64ba5ada..12329fcdceef82a7872aa2f43af3c8dfd1344e2f 100644 (file)
@@ -1,13 +1,14 @@
 /*
-  Glaurung, a UCI chess playing engine.
-  Copyright (C) 2004-2008 Tord Romstad
+  Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+  Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+  Copyright (C) 2008 Marco Costalba
 
-  Glaurung is free software: you can redistribute it and/or modify
+  Stockfish is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
-  Glaurung is distributed in the hope that it will be useful,
+  Stockfish is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
@@ -51,6 +52,7 @@ namespace {
   int WeightPassedPawnsMidgame   = 0x100;
   int WeightPassedPawnsEndgame   = 0x100;
   int WeightKingSafety[2] = { 0x100, 0x100 };
+  int WeightSpace;
 
   // Internal evaluation weights.  These are applied on top of the evaluation
   // weights read from UCI parameters.  The purpose is to be able to change
@@ -62,8 +64,9 @@ namespace {
   const int WeightPawnStructureEndgameInternal = 0x100;
   const int WeightPassedPawnsMidgameInternal   = 0x100;
   const int WeightPassedPawnsEndgameInternal   = 0x100;
-  const int WeightKingSafetyInternal           = 0x100;
-  const int WeightKingOppSafetyInternal        = 0x100;
+  const int WeightKingSafetyInternal           = 0x110;
+  const int WeightKingOppSafetyInternal        = 0x110;
+  const int WeightSpaceInternal                = 0x30;
 
   // Visually better to define tables constants
   typedef Value V;
@@ -202,6 +205,19 @@ namespace {
     ((1ULL << SQ_A8) | (1ULL << SQ_H8))
   };
 
+  // The SpaceMask[color] contains area of the board which is consdered 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
+  // friendly minor pieces.
+  const Bitboard SpaceMask[2] = {
+    (1ULL<<SQ_C2) | (1ULL<<SQ_D2) | (1ULL<<SQ_E2) | (1ULL<<SQ_F2) |
+    (1ULL<<SQ_C3) | (1ULL<<SQ_D3) | (1ULL<<SQ_E3) | (1ULL<<SQ_F3) |
+    (1ULL<<SQ_C4) | (1ULL<<SQ_D4) | (1ULL<<SQ_E4) | (1ULL<<SQ_F4),
+    (1ULL<<SQ_C7) | (1ULL<<SQ_D7) | (1ULL<<SQ_E7) | (1ULL<<SQ_F7) |
+    (1ULL<<SQ_C6) | (1ULL<<SQ_D6) | (1ULL<<SQ_E6) | (1ULL<<SQ_F6) |
+    (1ULL<<SQ_C5) | (1ULL<<SQ_D5) | (1ULL<<SQ_E5) | (1ULL<<SQ_F5)
+  };
+
   /// King safety constants and variables.  The king safety scores are taken
   /// from the array SafetyTable[].  Various little "meta-bonuses" measuring
   /// the strength of the attack are added up into an integer, which is used
@@ -214,8 +230,7 @@ namespace {
   const int KnightAttackWeight = 2;
 
   // Bonuses for safe checks for each piece type.
-  int QueenContactCheckBonus = 4;
-  int RookContactCheckBonus  = 2;
+  int QueenContactCheckBonus = 3;
   int QueenCheckBonus        = 2;
   int RookCheckBonus         = 1;
   int BishopCheckBonus       = 1;
@@ -245,18 +260,18 @@ namespace {
   // in init_safety().
   Value SafetyTable[100];
 
-  // Pawn and material hash tables, indexed by the current thread id:
+  // Pawn and material hash tables, indexed by the current thread id
   PawnInfoTable *PawnTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
   MaterialInfoTable *MaterialTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 
-  // Sizes of pawn and material hash tables:
+  // Sizes of pawn and material hash tables
   const int PawnTableSize = 16384;
   const int MaterialTableSize = 1024;
 
   // Array which gives the number of nonzero bits in an 8-bit integer:
   uint8_t BitCount8Bit[256];
 
-  // Function prototypes:
+  // Function prototypes
   void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei);
   void evaluate_bishop(const Position &p, Square s, Color us, EvalInfo &ei);
   void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei);
@@ -269,6 +284,7 @@ namespace {
   void evaluate_trapped_bishop_a1h1(const Position &pos, Square s, Color us,
                                     EvalInfo &ei);
 
+  void evaluate_space(const Position &p, Color us, EvalInfo &ei);
   inline Value apply_weight(Value v, int w);
   Value scale_by_game_phase(Value mv, Value ev, Phase ph, const ScaleFactor sf[]);
 
@@ -322,8 +338,8 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) {
   ei.egValue += apply_weight(ei.pi->eg_value(), WeightPawnStructureEndgame);
 
   // Initialize king attack bitboards and king attack zones for both sides
-  ei.attackedBy[WHITE][KING] = pos.king_attacks(pos.king_square(WHITE));
-  ei.attackedBy[BLACK][KING] = pos.king_attacks(pos.king_square(BLACK));
+  ei.attackedBy[WHITE][KING] = pos.piece_attacks<KING>(pos.king_square(WHITE));
+  ei.attackedBy[BLACK][KING] = pos.piece_attacks<KING>(pos.king_square(BLACK));
   ei.kingZone[WHITE] = ei.attackedBy[BLACK][KING] | (ei.attackedBy[BLACK][KING] >> 8);
   ei.kingZone[BLACK] = ei.attackedBy[WHITE][KING] | (ei.attackedBy[WHITE][KING] << 8);
 
@@ -337,20 +353,20 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) {
   for (Color c = WHITE; c <= BLACK; c++)
   {
     // Knights
-    for (int i = 0; i < pos.knight_count(c); i++)
-        evaluate_knight(pos, pos.knight_list(c, i), c, ei);
+    for (int i = 0; i < pos.piece_count(c, KNIGHT); i++)
+        evaluate_knight(pos, pos.piece_list(c, KNIGHT, i), c, ei);
 
     // Bishops
-    for (int i = 0; i < pos.bishop_count(c); i++)
-        evaluate_bishop(pos, pos.bishop_list(c, i), c, ei);
+    for (int i = 0; i < pos.piece_count(c, BISHOP); i++)
+        evaluate_bishop(pos, pos.piece_list(c, BISHOP, i), c, ei);
 
     // Rooks
-    for (int i = 0; i < pos.rook_count(c); i++)
-        evaluate_rook(pos, pos.rook_list(c, i), c, ei);
+    for (int i = 0; i < pos.piece_count(c, ROOK); i++)
+        evaluate_rook(pos, pos.piece_list(c, ROOK, i), c, ei);
 
     // Queens
-    for(int i = 0; i < pos.queen_count(c); i++)
-        evaluate_queen(pos, pos.queen_list(c, i), c, ei);
+    for(int i = 0; i < pos.piece_count(c, QUEEN); i++)
+        evaluate_queen(pos, pos.piece_list(c, QUEEN, i), c, ei);
 
     // Special pattern: trapped bishops on a7/h7/a2/h2
     Bitboard b = pos.bishops(c) & MaskA7H7[c];
@@ -406,6 +422,13 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) {
 
         ei.mgValue += ei.pi->kingside_storm_value(WHITE)
                     - ei.pi->queenside_storm_value(BLACK);
+
+    // Evaluate space for both sides
+    if (ei.mi->space_weight() > 0)
+    {
+        evaluate_space(pos, WHITE, ei);
+        evaluate_space(pos, BLACK, ei);
+    }
   }
 
   // Mobility
@@ -413,7 +436,7 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) {
   ei.egValue += apply_weight(ei.egMobility, WeightMobilityEndgame);
 
   // If we don't already have an unusual scale factor, check for opposite
-  // colored bishop endgames, and use a lower scale for those:
+  // colored bishop endgames, and use a lower scale for those
   if (   phase < PHASE_MIDGAME
       && pos.opposite_colored_bishops()
       && (   (factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0))
@@ -427,7 +450,7 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) {
       {
           // 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.pawn_count(WHITE) + pos.pawn_count(BLACK) == 1);
+          bool one_pawn = (pos.piece_count(WHITE, PAWN) + pos.piece_count(BLACK, PAWN) == 1);
           sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
       }
       else
@@ -526,6 +549,7 @@ void read_weights(Color us) {
 
   WeightKingSafety[us]   = weight_option("Cowardice", WeightKingSafetyInternal);
   WeightKingSafety[them] = weight_option("Aggressiveness", WeightKingOppSafetyInternal);
+  WeightSpace = weight_option("Space", WeightSpaceInternal);
 
   init_safety();
 }
@@ -569,7 +593,7 @@ namespace {
     if (v && (p.pawn_attacks(them, s) & p.pawns(us)))
     {
         bonus += v / 2;
-        if (   p.knight_count(them) == 0
+        if (   p.piece_count(them, KNIGHT) == 0
             && (SquaresByColorBB[square_color(s)] & p.bishops(them)) == EmptyBoardBB)
             bonus += v;
     }
@@ -584,7 +608,7 @@ namespace {
 
   void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei) {
 
-    Bitboard b = p.knight_attacks(s);
+    Bitboard b = p.piece_attacks<KNIGHT>(s);
     ei.attackedBy[us][KNIGHT] |= b;
 
     // King attack, mobility and outposts
@@ -612,7 +636,6 @@ namespace {
 
   void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei) {
 
-    //Bitboard b = p.rook_attacks(s);
     Bitboard b = rook_attacks_bb(s, p.occupied_squares() & ~p.rooks_and_queens(us));
     ei.attackedBy[us][ROOK] |= b;
 
@@ -679,7 +702,7 @@ namespace {
 
   void evaluate_queen(const Position &p, Square s, Color us, EvalInfo &ei) {
 
-    Bitboard b = p.queen_attacks(s);
+    Bitboard b = p.piece_attacks<QUEEN>(s);
     ei.attackedBy[us][QUEEN] |= b;
 
     // King attack and mobility
@@ -724,7 +747,7 @@ namespace {
     // from optimally tuned.
     Color them = opposite_color(us);
 
-    if (   p.queen_count(them) >= 1
+    if (   p.piece_count(them, QUEEN) >= 1
         && ei.kingAttackersCount[them] >= 2
         && p.non_pawn_material(them) >= QueenValueMidgame + RookValueMidgame
         && ei.kingAdjacentZoneAttacksCount[them])
@@ -743,14 +766,14 @@ namespace {
       Bitboard occ = p.occupied_squares(), b, b2;
 
       // Initialize the 'attackUnits' variable, which is used later on as an
-      // index to the SafetyTable[] array.  The initial is based on the number
-      // and types of the attacking pieces, the number of attacked and
+      // index to the SafetyTable[] array.  The initial value is based on the
+      // number and types of the attacking pieces, the number of attacked and
       // undefended squares around the king, the square of the king, and the
       // quality of the pawn shelter.
       int attackUnits =
             Min((ei.kingAttackersCount[them] * ei.kingAttackersWeight[them]) / 2, 25)
           + (ei.kingAdjacentZoneAttacksCount[them] + count_1s_max_15(undefended)) * 3
-          + InitKingDanger[relative_square(us, s)] - shelter / 32;
+          + InitKingDanger[relative_square(us, s)] - (shelter >> 5);
 
       // Analyse safe queen contact checks
       b = undefended & ei.attacked_by(them, QUEEN) & ~p.pieces_of_color(them);
@@ -772,22 +795,22 @@ namespace {
           if (QueenContactMates && !p.is_check())
           {
             Bitboard escapeSquares =
-                p.king_attacks(s) & ~p.pieces_of_color(us) & ~attackedByOthers;
+                p.piece_attacks<KING>(s) & ~p.pieces_of_color(us) & ~attackedByOthers;
 
             while (b)
             {
                 Square from, to = pop_1st_bit(&b);
-                if (!(escapeSquares & ~queen_attacks_bb(to, occ & clear_mask_bb(s))))
+                if (!(escapeSquares & ~queen_attacks_bb(to, occ & ClearMaskBB[s])))
                 {
                     // We have a mate, unless the queen is pinned or there
                     // is an X-ray attack through the queen.
-                    for (int i = 0; i < p.queen_count(them); i++)
+                    for (int i = 0; i < p.piece_count(them, QUEEN); i++)
                     {
-                        from = p.queen_list(them, i);
-                        if (    bit_is_set(p.queen_attacks(from), to)
+                        from = p.piece_list(them, QUEEN, i);
+                        if (    bit_is_set(p.piece_attacks<QUEEN>(from), to)
                             && !bit_is_set(p.pinned_pieces(them), from)
-                            && !(rook_attacks_bb(to, occ & clear_mask_bb(from)) & p.rooks_and_queens(us))
-                            && !(rook_attacks_bb(to, occ & clear_mask_bb(from)) & p.rooks_and_queens(us)))
+                            && !(rook_attacks_bb(to, occ & ClearMaskBB[from]) & p.rooks_and_queens(us))
+                            && !(rook_attacks_bb(to, occ & ClearMaskBB[from]) & p.rooks_and_queens(us)))
                             
                             ei.mateThreat[them] = make_move(from, to);
                     }
@@ -796,28 +819,11 @@ namespace {
           }
         }
       }
-      // Analyse safe rook contact checks:
-      if (RookContactCheckBonus)
-      {
-          b = undefended & ei.attacked_by(them, ROOK) & ~p.pieces_of_color(them);
-          if (b)
-          {
-              Bitboard attackedByOthers =
-                    ei.attacked_by(them, PAWN)   | ei.attacked_by(them, KNIGHT)
-                  | ei.attacked_by(them, BISHOP) | ei.attacked_by(them, QUEEN);
-
-              b &= attackedByOthers;
-              if (b)
-              {
-                  int count = count_1s_max_15(b);
-                  attackUnits += (RookContactCheckBonus * count * (sente? 2 : 1));
-              }
-          }
-      }
-      // Analyse safe distance checks:
+
+      // Analyse safe distance checks
       if (QueenCheckBonus > 0 || RookCheckBonus > 0)
       {
-          b = p.rook_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
+          b = p.piece_attacks<ROOK>(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
 
           // Queen checks
           b2 = b & ei.attacked_by(them, QUEEN);
@@ -831,7 +837,7 @@ namespace {
       }
       if (QueenCheckBonus > 0 || BishopCheckBonus > 0)
       {
-          b = p.bishop_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
+          b = p.piece_attacks<BISHOP>(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
 
           // Queen checks
           b2 = b & ei.attacked_by(them, QUEEN);
@@ -845,7 +851,7 @@ namespace {
       }
       if (KnightCheckBonus > 0)
       {
-          b = p.knight_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
+          b = p.piece_attacks<KNIGHT>(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
 
           // Knight checks
           b2 = b & ei.attacked_by(them, KNIGHT);
@@ -930,6 +936,13 @@ namespace {
           b2 = squares_in_front_of(us, s);
           b3 = b2 & ei.attacked_by(them);
           b4 = b2 & ei.attacked_by(us);
+
+          // If there is an enemy rook or queen attacking the pawn from behind,
+          // add all X-ray attacks by the rook or queen.
+          if(bit_is_set(ei.attacked_by(them,ROOK)|ei.attacked_by(them,QUEEN),s)
+             && squares_behind(us, s) & pos.rooks_and_queens(them))
+            b3 = b2;
+
           if((b2 & pos.pieces_of_color(them)) == EmptyBoardBB) {
             // There are no enemy pieces in the pawn's path!  Are any of the
             // squares in the pawn's path attacked by the enemy?
@@ -994,7 +1007,7 @@ namespace {
         // value if the other side has a rook or queen.
         if(square_file(s) == FILE_A || square_file(s) == FILE_H) {
           if(pos.non_pawn_material(them) == KnightValueMidgame
-             && pos.knight_count(them) == 1)
+             && pos.piece_count(them, KNIGHT) == 1)
             ebonus += ebonus / 4;
           else if(pos.rooks_and_queens(them))
             ebonus -= ebonus / 4;
@@ -1100,14 +1113,54 @@ namespace {
   }
 
 
-  // apply_weight applies an evaluation weight to a value.
+  // evaluate_space() computes the space evaluation for a given side. The
+  // space evaluation is a simple bonus based on the number of safe squares
+  // available for minor pieces on the central four files on ranks 2--4. Safe
+  // squares one, two or three squares behind a friendly pawn are counted
+  // twice. Finally, the space bonus is scaled by a weight taken from the
+  // material hash table.
+
+  void evaluate_space(const Position &pos, Color us, EvalInfo &ei) {
+
+    Color them = opposite_color(us);
+
+    // Find the safe squares for our pieces inside the area defined by
+    // SpaceMask[us].  A square is unsafe it is attacked by an enemy
+    // pawn, or if it is undefended and attacked by an enemy piece.
+
+    Bitboard safeSquares =
+      SpaceMask[us] & ~pos.pawns(us) & ~ei.attacked_by(them, PAWN)
+      & ~(~ei.attacked_by(us) & ei.attacked_by(them));
+
+    // Find all squares which are at most three squares behind some friendly
+    // pawn.
+    Bitboard behindFriendlyPawns = pos.pawns(us);
+    if(us == WHITE) {
+      behindFriendlyPawns |= (behindFriendlyPawns >> 8);
+      behindFriendlyPawns |= (behindFriendlyPawns >> 16);
+    }
+    else {
+      behindFriendlyPawns |= (behindFriendlyPawns << 8);
+      behindFriendlyPawns |= (behindFriendlyPawns << 16);
+    }
+
+    int space =
+      count_1s_max_15(safeSquares)
+      + count_1s_max_15(behindFriendlyPawns & safeSquares);
+
+    ei.mgValue += Sign[us] *
+      apply_weight(Value(space * ei.mi->space_weight()), WeightSpace);
+  }
+
+
+  // apply_weight() applies an evaluation weight to a value
 
   inline Value apply_weight(Value v, int w) {
     return (v*w) / 0x100;
   }
 
 
-  // scale_by_game_phase interpolates between a middle game and an endgame
+  // scale_by_game_phase() interpolates between a middle game and an endgame
   // score, based on game phase.  It also scales the return value by a
   // ScaleFactor array.
 
@@ -1119,13 +1172,7 @@ namespace {
 
     ev = apply_scale_factor(ev, sf[(ev > Value(0) ? WHITE : BLACK)]);
 
-    // Linearized sigmoid interpolator
-    int sph = int(ph);
-    sph -= (64 - sph) / 4;
-    sph = Min(PHASE_MIDGAME, Max(PHASE_ENDGAME, sph));
-
-    Value result = Value(int((mv * sph + ev * (128 - sph)) / 128));
-
+    Value result = Value(int((mv * ph + ev * (128 - ph)) / 128));
     return Value(int(result) & ~(GrainSize - 1));
   }
 
@@ -1161,7 +1208,6 @@ namespace {
   void init_safety() {
 
     QueenContactCheckBonus = get_option_value_int("Queen Contact Check Bonus");
-    RookContactCheckBonus  = get_option_value_int("Rook Contact Check Bonus");
     QueenCheckBonus        = get_option_value_int("Queen Check Bonus");
     RookCheckBonus         = get_option_value_int("Rook Check Bonus");
     BishopCheckBonus       = get_option_value_int("Bishop Check Bonus");