"Fossies" - the Fresh Open Source Software Archive

Member "stockfish-11-linux/src/material.cpp" (18 Jan 2020, 8195 Bytes) of package /linux/privat/stockfish-11-linux.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "material.cpp": 9-linux_vs_10-linux.

    1 /*
    2   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
    3   Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
    4   Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
    5   Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
    6 
    7   Stockfish is free software: you can redistribute it and/or modify
    8   it under the terms of the GNU General Public License as published by
    9   the Free Software Foundation, either version 3 of the License, or
   10   (at your option) any later version.
   11 
   12   Stockfish is distributed in the hope that it will be useful,
   13   but WITHOUT ANY WARRANTY; without even the implied warranty of
   14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15   GNU General Public License for more details.
   16 
   17   You should have received a copy of the GNU General Public License
   18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
   19 */
   20 
   21 #include <cassert>
   22 #include <cstring>   // For std::memset
   23 
   24 #include "material.h"
   25 #include "thread.h"
   26 
   27 using namespace std;
   28 
   29 namespace {
   30 
   31   // Polynomial material imbalance parameters
   32 
   33   constexpr int QuadraticOurs[][PIECE_TYPE_NB] = {
   34     //            OUR PIECES
   35     // pair pawn knight bishop rook queen
   36     {1438                               }, // Bishop pair
   37     {  40,   38                         }, // Pawn
   38     {  32,  255, -62                    }, // Knight      OUR PIECES
   39     {   0,  104,   4,    0              }, // Bishop
   40     { -26,   -2,  47,   105,  -208      }, // Rook
   41     {-189,   24, 117,   133,  -134, -6  }  // Queen
   42   };
   43 
   44   constexpr int QuadraticTheirs[][PIECE_TYPE_NB] = {
   45     //           THEIR PIECES
   46     // pair pawn knight bishop rook queen
   47     {   0                               }, // Bishop pair
   48     {  36,    0                         }, // Pawn
   49     {   9,   63,   0                    }, // Knight      OUR PIECES
   50     {  59,   65,  42,     0             }, // Bishop
   51     {  46,   39,  24,   -24,    0       }, // Rook
   52     {  97,  100, -42,   137,  268,    0 }  // Queen
   53   };
   54 
   55   // Endgame evaluation and scaling functions are accessed directly and not through
   56   // the function maps because they correspond to more than one material hash key.
   57   Endgame<KXK>    EvaluateKXK[] = { Endgame<KXK>(WHITE),    Endgame<KXK>(BLACK) };
   58 
   59   Endgame<KBPsK>  ScaleKBPsK[]  = { Endgame<KBPsK>(WHITE),  Endgame<KBPsK>(BLACK) };
   60   Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
   61   Endgame<KPsK>   ScaleKPsK[]   = { Endgame<KPsK>(WHITE),   Endgame<KPsK>(BLACK) };
   62   Endgame<KPKP>   ScaleKPKP[]   = { Endgame<KPKP>(WHITE),   Endgame<KPKP>(BLACK) };
   63 
   64   // Helper used to detect a given material distribution
   65   bool is_KXK(const Position& pos, Color us) {
   66     return  !more_than_one(pos.pieces(~us))
   67           && pos.non_pawn_material(us) >= RookValueMg;
   68   }
   69 
   70   bool is_KBPsK(const Position& pos, Color us) {
   71     return   pos.non_pawn_material(us) == BishopValueMg
   72           && pos.count<PAWN  >(us) >= 1;
   73   }
   74 
   75   bool is_KQKRPs(const Position& pos, Color us) {
   76     return  !pos.count<PAWN>(us)
   77           && pos.non_pawn_material(us) == QueenValueMg
   78           && pos.count<ROOK>(~us) == 1
   79           && pos.count<PAWN>(~us) >= 1;
   80   }
   81 
   82   /// imbalance() calculates the imbalance by comparing the piece count of each
   83   /// piece type for both colors.
   84   template<Color Us>
   85   int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
   86 
   87     constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
   88 
   89     int bonus = 0;
   90 
   91     // Second-degree polynomial material imbalance, by Tord Romstad
   92     for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
   93     {
   94         if (!pieceCount[Us][pt1])
   95             continue;
   96 
   97         int v = 0;
   98 
   99         for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
  100             v +=  QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
  101                 + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
  102 
  103         bonus += pieceCount[Us][pt1] * v;
  104     }
  105 
  106     return bonus;
  107   }
  108 
  109 } // namespace
  110 
  111 namespace Material {
  112 
  113 /// Material::probe() looks up the current position's material configuration in
  114 /// the material hash table. It returns a pointer to the Entry if the position
  115 /// is found. Otherwise a new Entry is computed and stored there, so we don't
  116 /// have to recompute all when the same material configuration occurs again.
  117 
  118 Entry* probe(const Position& pos) {
  119 
  120   Key key = pos.material_key();
  121   Entry* e = pos.this_thread()->materialTable[key];
  122 
  123   if (e->key == key)
  124       return e;
  125 
  126   std::memset(e, 0, sizeof(Entry));
  127   e->key = key;
  128   e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
  129 
  130   Value npm_w = pos.non_pawn_material(WHITE);
  131   Value npm_b = pos.non_pawn_material(BLACK);
  132   Value npm   = clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
  133 
  134   // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
  135   e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
  136 
  137   // Let's look if we have a specialized evaluation function for this particular
  138   // material configuration. Firstly we look for a fixed configuration one, then
  139   // for a generic one if the previous search failed.
  140   if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
  141       return e;
  142 
  143   for (Color c : { WHITE, BLACK })
  144       if (is_KXK(pos, c))
  145       {
  146           e->evaluationFunction = &EvaluateKXK[c];
  147           return e;
  148       }
  149 
  150   // OK, we didn't find any special evaluation function for the current material
  151   // configuration. Is there a suitable specialized scaling function?
  152   const auto* sf = Endgames::probe<ScaleFactor>(key);
  153 
  154   if (sf)
  155   {
  156       e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
  157       return e;
  158   }
  159 
  160   // We didn't find any specialized scaling function, so fall back on generic
  161   // ones that refer to more than one material distribution. Note that in this
  162   // case we don't return after setting the function.
  163   for (Color c : { WHITE, BLACK })
  164   {
  165     if (is_KBPsK(pos, c))
  166         e->scalingFunction[c] = &ScaleKBPsK[c];
  167 
  168     else if (is_KQKRPs(pos, c))
  169         e->scalingFunction[c] = &ScaleKQKRPs[c];
  170   }
  171 
  172   if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board
  173   {
  174       if (!pos.count<PAWN>(BLACK))
  175       {
  176           assert(pos.count<PAWN>(WHITE) >= 2);
  177 
  178           e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
  179       }
  180       else if (!pos.count<PAWN>(WHITE))
  181       {
  182           assert(pos.count<PAWN>(BLACK) >= 2);
  183 
  184           e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
  185       }
  186       else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
  187       {
  188           // This is a special case because we set scaling functions
  189           // for both colors instead of only one.
  190           e->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
  191           e->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
  192       }
  193   }
  194 
  195   // Zero or just one pawn makes it difficult to win, even with a small material
  196   // advantage. This catches some trivial draws like KK, KBK and KNK and gives a
  197   // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
  198   if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
  199       e->factor[WHITE] = uint8_t(npm_w <  RookValueMg   ? SCALE_FACTOR_DRAW :
  200                                  npm_b <= BishopValueMg ? 4 : 14);
  201 
  202   if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
  203       e->factor[BLACK] = uint8_t(npm_b <  RookValueMg   ? SCALE_FACTOR_DRAW :
  204                                  npm_w <= BishopValueMg ? 4 : 14);
  205 
  206   // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
  207   // for the bishop pair "extended piece", which allows us to be more flexible
  208   // in defining bishop pair bonuses.
  209   const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
  210   { pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
  211     pos.count<BISHOP>(WHITE)    , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
  212   { pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
  213     pos.count<BISHOP>(BLACK)    , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
  214 
  215   e->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
  216   return e;
  217 }
  218 
  219 } // namespace Material