Material???

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Ralph Stoesser
Posts: 408
Joined: Sat Mar 06, 2010 9:28 am

Material???

Post by Ralph Stoesser »

If I disable the line

bonus += mi->material_value();

in SF 2.0.1 sources, I get a decrease of only 1 ELO point after 20,000 hyper bullet games.

Especially with Kaufman's and Romstad's formulas regarding material imbalances in mind this was a highly unexpected result. Following the maxim to remove all code which doesn't add ELO, I wonder whether material evaluation could be a serious candidate for code removal.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Material???

Post by mcostalba »

Ralph Stoesser wrote:If I disable the line

bonus += mi->material_value();

in SF 2.0.1 sources, I get a decrease of only 1 ELO point after 20,000 hyper bullet games.

Especially with Kaufman's and Romstad's formulas regarding material imbalances in mind this was a highly unexpected result. Following the maxim to remove all code which doesn't add ELO, I wonder whether material evaluation could be a serious candidate for code removal.
I think is not, we have tried to remove independently both Kaufmann and Tord stuff we have also tried to change somehow the coefficients and always we got a weaker engine, although by not much say 5 ELO with Kaufmann's and 10-15 ELO with Tord's.

I suspect you should validate the test at longer time controls.
Ralph Stoesser
Posts: 408
Joined: Sat Mar 06, 2010 9:28 am

Re: Material???

Post by Ralph Stoesser »

But you haven't removed the material value bonus completely like I did, right? That's understandable, because it sounds like an unsane idea to try it. The more surprising the result. If I would be able to automatically re-tune the eval params with material disabled, I would try it right now.
Ralph Stoesser
Posts: 408
Joined: Sat Mar 06, 2010 9:28 am

Re: Material???

Post by Ralph Stoesser »

60,000 games @10 sec/game: +8295, -8443, =13998
ELO difference: -2.

Not that much.
User avatar
Eelco de Groot
Posts: 4565
Joined: Sun Mar 12, 2006 2:40 am
Full name:   

Idea for Tord?

Post by Eelco de Groot »

mcostalba wrote:
Ralph Stoesser wrote:If I disable the line

bonus += mi->material_value();

in SF 2.0.1 sources, I get a decrease of only 1 ELO point after 20,000 hyper bullet games.

Especially with Kaufman's and Romstad's formulas regarding material imbalances in mind this was a highly unexpected result. Following the maxim to remove all code which doesn't add ELO, I wonder whether material evaluation could be a serious candidate for code removal.
I think is not, we have tried to remove independently both Kaufmann and Tord stuff we have also tried to change somehow the coefficients and always we got a weaker engine, although by not much say 5 ELO with Kaufmann's and 10-15 ELO with Tord's.

I suspect you should validate the test at longer time controls.
Hoping that the material imbalance tables do not get axed entirely, and Stockfish will still know that the Bishop pair is an important chessterm, and in case somone might try retuning all this again, here is a version with a small change in the 2.0.1 version coefficients used, I think it could be easily transferred to 2.1.1, if necessary. It is not elegant I suppose to do it this way but I did not want to try to retune everything by myself, it is impossible anyway to tune on a single computer, and changing the computation that exists would mean new coefficients in the whole table, probably. So there is just some, at present very small, adjustment added on top of the existing calculation, if there is no piece present of any of the 6 possible types.

The original table assumes there is no synergy possible in that case and the calculation of change in matValue from this piecetype is zero.

That is all fine in theory but I think there is a small problem when, in case the piecenumber is not zero, all the "synergy" calculations in Tord's second order material imbalance table, of this piecetype with the other pieces, of your own colour or of the opponent, are strictly linear with the number of pieces of either type. I think in that case you are ignoring the possible redundancies, except of course for the separate bigger terms introduced by Larry Kaufman;

Code: Select all

    // Redundancy of major pieces, formula based on Kaufman's paper
    // "The Evaluation of Material Imbalances in Chess"
    // http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
You could correct this with introducing more second order terms, but that would require a total retuning of Tord's table I think. And I'm not totally sure you would want to treat the Bishop pair just like this as it is just another case of synergy, apart from the fact that there is no differentiation in Stockfish between black squared and white squared bishops, like in Rybka.

So at the moment I just tried to add a "hack", a penalty mostly, in case the piece number is zero, the idea is that this increases the calculated difference in general between all the cases where a piece number is zero and piece number is one. The effect should be that the synergies are no longer necessarily linear with the number of pieces of either type. (The interaction of any piece type with itself is not linear because of the second order term that describes this, but all other interactions are of the type cXY where X is one piecetype and Y is another, c is a constant.)

Modified material.cpp 2.0 version:

Code: Select all

/*
  Stockfish, a UCI chess playing engine derived from Glaurung 2.1
  Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
  Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad

  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.

  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.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http&#58;//www.gnu.org/licenses/>.
*/


////
//// Includes
////

#include <cassert>
#include <cstring>
#include <map>

#include "material.h"

using namespace std;


////
//// Local definitions
////

namespace &#123;

  // Values modified by Joona Kiiski
  const Value MidgameLimit = Value&#40;15581&#41;;
  const Value EndgameLimit = Value&#40;3998&#41;;

  // Polynomial material balance parameters
  const Value RedundantQueenPenalty = Value&#40;320&#41;;
  const Value RedundantRookPenalty  = Value&#40;554&#41;;

  const int LinearCoefficients&#91;6&#93; = &#123; 1617, -162, -1172, -190, 105, 26 &#125;;

  const int QuadraticCoefficientsSameColor&#91;&#93;&#91;8&#93; = &#123;
  &#123; 7, 7, 7, 7, 7, 7 &#125;, &#123; 39, 2, 7, 7, 7, 7 &#125;, &#123; 35, 271, -4, 7, 7, 7 &#125;,
  &#123; 7, 25, 4, 7, 7, 7 &#125;, &#123; -27, -2, 46, 100, 56, 7 &#125;, &#123; 58, 29, 83, 148, -3, -25 &#125; &#125;;

  const int QuadraticCoefficientsOppositeColor&#91;&#93;&#91;8&#93; = &#123;
  &#123; 41, 41, 41, 41, 41, 41 &#125;, &#123; 37, 41, 41, 41, 41, 41 &#125;, &#123; 10, 62, 41, 41, 41, 41 &#125;,
  &#123; 57, 64, 39, 41, 41, 41 &#125;, &#123; 50, 40, 23, -22, 41, 41 &#125;, &#123; 106, 101, 3, 151, 171, 41 &#125; &#125;;

  const int MissingPiecetypeCoefficientsSameColor&#91;&#93;&#91;8&#93; = &#123;
  &#123; 3, 1, 1, 1, 1, 1 &#125;, &#123; 1, 3, 1, 1, 1, 1 &#125;, &#123; 1, 1, 3, 1, 1, 1 &#125;,
  &#123; 1, 1, 1, 3, 1, 1 &#125;, &#123; 1, 1, 1, 1, 3, 1 &#125;, &#123; 1, 1, 1, 1, 1, 3 &#125; &#125;;

  const int MissingPiecetypeCoefficientsOppositeColor&#91;&#93;&#91;8&#93; = &#123;
  &#123; 3, 1, 1, 1, 1, 1 &#125;, &#123; 1, 3, 1, 1, 1, 1 &#125;, &#123; 1, 1, 3, 1, 1, 1 &#125;,
  &#123; 1, 1, 1, 3, 1, 1 &#125;, &#123; 1, 1, 1, 1, 3, 1 &#125;, &#123; 1, 1, 1, 1, 1, 3 &#125; &#125;;

  typedef EndgameEvaluationFunctionBase EF;
  typedef EndgameScalingFunctionBase SF;
  typedef map<Key, EF*> EFMap;
  typedef map<Key, SF*> SFMap;

  // Endgame evaluation and scaling functions accessed direcly and not through
  // the function maps because correspond to more then one material hash key.
  EvaluationFunction<KmmKm> EvaluateKmmKm&#91;&#93; = &#123; EvaluationFunction<KmmKm>&#40;WHITE&#41;, EvaluationFunction<KmmKm>&#40;BLACK&#41; &#125;;
  EvaluationFunction<KXK>   EvaluateKXK&#91;&#93;   = &#123; EvaluationFunction<KXK>&#40;WHITE&#41;,   EvaluationFunction<KXK>&#40;BLACK&#41; &#125;;
  ScalingFunction<KBPsK>    ScaleKBPsK&#91;&#93;    = &#123; ScalingFunction<KBPsK>&#40;WHITE&#41;,    ScalingFunction<KBPsK>&#40;BLACK&#41; &#125;;
  ScalingFunction<KQKRPs>   ScaleKQKRPs&#91;&#93;   = &#123; ScalingFunction<KQKRPs>&#40;WHITE&#41;,   ScalingFunction<KQKRPs>&#40;BLACK&#41; &#125;;
  ScalingFunction<KPsK>     ScaleKPsK&#91;&#93;     = &#123; ScalingFunction<KPsK>&#40;WHITE&#41;,     ScalingFunction<KPsK>&#40;BLACK&#41; &#125;;
  ScalingFunction<KPKP>     ScaleKPKP&#91;&#93;     = &#123; ScalingFunction<KPKP>&#40;WHITE&#41;,     ScalingFunction<KPKP>&#40;BLACK&#41; &#125;;

  // Helper templates used to detect a given material distribution
  template<Color Us> bool is_KXK&#40;const Position& pos&#41; &#123;
    const Color Them = &#40;Us == WHITE ? BLACK &#58; WHITE&#41;;
    return   pos.non_pawn_material&#40;Them&#41; == VALUE_ZERO
          && pos.piece_count&#40;Them, PAWN&#41; == 0
          && pos.non_pawn_material&#40;Us&#41;   >= RookValueMidgame;
  &#125;

  template<Color Us> bool is_KBPsK&#40;const Position& pos&#41; &#123;
    return   pos.non_pawn_material&#40;Us&#41;   == BishopValueMidgame
          && pos.piece_count&#40;Us, BISHOP&#41; == 1
          && pos.piece_count&#40;Us, PAWN&#41;   >= 1;
  &#125;

  template<Color Us> bool is_KQKRPs&#40;const Position& pos&#41; &#123;
    const Color Them = &#40;Us == WHITE ? BLACK &#58; WHITE&#41;;
    return   pos.piece_count&#40;Us, PAWN&#41;    == 0
          && pos.non_pawn_material&#40;Us&#41;    == QueenValueMidgame
          && pos.piece_count&#40;Us, QUEEN&#41;   == 1
          && pos.piece_count&#40;Them, ROOK&#41;  == 1
          && pos.piece_count&#40;Them, PAWN&#41;  >= 1;
  &#125;
&#125;


////
//// Classes
////

/// EndgameFunctions class stores endgame evaluation and scaling functions
/// in two std&#58;&#58;map. Because STL library is not guaranteed to be thread
/// safe even for read access, the maps, although with identical content,
/// are replicated for each thread. This is faster then using locks.

class EndgameFunctions &#123;
public&#58;
  EndgameFunctions&#40;);
  ~EndgameFunctions&#40;);
  template<class T> T* get&#40;Key key&#41; const;

private&#58;
  template<class T> void add&#40;const string& keyCode&#41;;

  static Key buildKey&#40;const string& keyCode&#41;;
  static const string swapColors&#40;const string& keyCode&#41;;

  // Here we store two maps, for evaluate and scaling functions...
  pair<EFMap, SFMap> maps;

  // ...and here is the accessing template function
  template<typename T> const map<Key, T*>& get&#40;) const;
&#125;;

// Explicit specializations of a member function shall be declared in
// the namespace of which the class template is a member.
template<> const EFMap& EndgameFunctions&#58;&#58;get<EF>() const &#123; return maps.first; &#125;
template<> const SFMap& EndgameFunctions&#58;&#58;get<SF>() const &#123; return maps.second; &#125;


////
//// Functions
////

/// MaterialInfoTable c'tor and d'tor, called once by each thread

MaterialInfoTable&#58;&#58;MaterialInfoTable&#40;) &#123;

  entries = new MaterialInfo&#91;MaterialTableSize&#93;;
  funcs = new EndgameFunctions&#40;);

  if (!entries || !funcs&#41;
  &#123;
      cerr << "Failed to allocate " << MaterialTableSize * sizeof&#40;MaterialInfo&#41;
           << " bytes for material hash table." << endl;
      exit&#40;EXIT_FAILURE&#41;;
  &#125;
  memset&#40;entries, 0, MaterialTableSize * sizeof&#40;MaterialInfo&#41;);
&#125;

MaterialInfoTable&#58;&#58;~MaterialInfoTable&#40;) &#123;

  delete funcs;
  delete &#91;&#93; entries;
&#125;


/// MaterialInfoTable&#58;&#58;game_phase&#40;) calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialInfo.

Phase MaterialInfoTable&#58;&#58;game_phase&#40;const Position& pos&#41; &#123;

  Value npm = pos.non_pawn_material&#40;WHITE&#41; + pos.non_pawn_material&#40;BLACK&#41;;

  if &#40;npm >= MidgameLimit&#41;
      return PHASE_MIDGAME;

  if &#40;npm <= EndgameLimit&#41;
      return PHASE_ENDGAME;

  return Phase&#40;(&#40;npm - EndgameLimit&#41; * 128&#41; / &#40;MidgameLimit - EndgameLimit&#41;);
&#125;

/// MaterialInfoTable&#58;&#58;get_material_info&#40;) takes a position object as input,
/// computes or looks up a MaterialInfo object, and returns a pointer to it.
/// If the material configuration is not already present in the table, it
/// is stored there, so we don't have to recompute everything when the
/// same material configuration occurs again.

MaterialInfo* MaterialInfoTable&#58;&#58;get_material_info&#40;const Position& pos&#41; &#123;

  Key key = pos.get_material_key&#40;);
  unsigned index = unsigned&#40;key & &#40;MaterialTableSize - 1&#41;);
  MaterialInfo* mi = entries + index;

  // If mi->key matches the position's material hash key, it means that we
  // have analysed this material configuration before, and we can simply
  // return the information we found the last time instead of recomputing it.
  if &#40;mi->key == key&#41;
      return mi;

  // Clear the MaterialInfo object, and set its key
  memset&#40;mi, 0, sizeof&#40;MaterialInfo&#41;);
  mi->factor&#91;WHITE&#93; = mi->factor&#91;BLACK&#93; = &#40;uint8_t&#41;SCALE_FACTOR_NORMAL;
  mi->key = key;

  // Store game phase
  mi->gamePhase = MaterialInfoTable&#58;&#58;game_phase&#40;pos&#41;;

  // Let's look if we have a specialized evaluation function for this
  // particular material configuration. First we look for a fixed
  // configuration one, then a generic one if previous search failed.
  if (&#40;mi->evaluationFunction = funcs->get<EF>&#40;key&#41;) != NULL&#41;
      return mi;

  if &#40;is_KXK<WHITE>&#40;pos&#41; || is_KXK<BLACK>&#40;pos&#41;)
  &#123;
      mi->evaluationFunction = is_KXK<WHITE>&#40;pos&#41; ? &EvaluateKXK&#91;WHITE&#93; &#58; &EvaluateKXK&#91;BLACK&#93;;
      return mi;
  &#125;

  if (   pos.pieces&#40;PAWN&#41;  == EmptyBoardBB
      && pos.pieces&#40;ROOK&#41;  == EmptyBoardBB
      && pos.pieces&#40;QUEEN&#41; == EmptyBoardBB&#41;
  &#123;
      // Minor piece endgame with at least one minor piece per side and
      // no pawns. Note that the case KmmK is already handled by KXK.
      assert&#40;&#40;pos.pieces&#40;KNIGHT, WHITE&#41; | pos.pieces&#40;BISHOP, WHITE&#41;));
      assert&#40;&#40;pos.pieces&#40;KNIGHT, BLACK&#41; | pos.pieces&#40;BISHOP, BLACK&#41;));

      if (   pos.piece_count&#40;WHITE, BISHOP&#41; + pos.piece_count&#40;WHITE, KNIGHT&#41; <= 2
          && pos.piece_count&#40;BLACK, BISHOP&#41; + pos.piece_count&#40;BLACK, KNIGHT&#41; <= 2&#41;
      &#123;
          mi->evaluationFunction = &EvaluateKmmKm&#91;WHITE&#93;;
          return mi;
      &#125;
  &#125;

  // OK, we didn't find any special evaluation function for the current
  // material configuration. Is there a suitable scaling function?
  //
  // We face problems when there are several conflicting applicable
  // scaling functions and we need to decide which one to use.
  SF* sf;

  if (&#40;sf = funcs->get<SF>&#40;key&#41;) != NULL&#41;
  &#123;
      mi->scalingFunction&#91;sf->color&#40;)&#93; = sf;
      return mi;
  &#125;

  // Generic scaling functions that refer to more then one material
  // distribution. Should be probed after the specialized ones.
  // Note that these ones don't return after setting the function.
  if &#40;is_KBPsK<WHITE>&#40;pos&#41;)
      mi->scalingFunction&#91;WHITE&#93; = &ScaleKBPsK&#91;WHITE&#93;;

  if &#40;is_KBPsK<BLACK>&#40;pos&#41;)
      mi->scalingFunction&#91;BLACK&#93; = &ScaleKBPsK&#91;BLACK&#93;;

  if &#40;is_KQKRPs<WHITE>&#40;pos&#41;)
      mi->scalingFunction&#91;WHITE&#93; = &ScaleKQKRPs&#91;WHITE&#93;;

  else if &#40;is_KQKRPs<BLACK>&#40;pos&#41;)
      mi->scalingFunction&#91;BLACK&#93; = &ScaleKQKRPs&#91;BLACK&#93;;

  if &#40;pos.non_pawn_material&#40;WHITE&#41; + pos.non_pawn_material&#40;BLACK&#41; == VALUE_ZERO&#41;
  &#123;
      if &#40;pos.piece_count&#40;BLACK, PAWN&#41; == 0&#41;
      &#123;
          assert&#40;pos.piece_count&#40;WHITE, PAWN&#41; >= 2&#41;;
          mi->scalingFunction&#91;WHITE&#93; = &ScaleKPsK&#91;WHITE&#93;;
      &#125;
      else if &#40;pos.piece_count&#40;WHITE, PAWN&#41; == 0&#41;
      &#123;
          assert&#40;pos.piece_count&#40;BLACK, PAWN&#41; >= 2&#41;;
          mi->scalingFunction&#91;BLACK&#93; = &ScaleKPsK&#91;BLACK&#93;;
      &#125;
      else if &#40;pos.piece_count&#40;WHITE, PAWN&#41; == 1 && pos.piece_count&#40;BLACK, PAWN&#41; == 1&#41;
      &#123;
          // This is a special case because we set scaling functions
          // for both colors instead of only one.
          mi->scalingFunction&#91;WHITE&#93; = &ScaleKPKP&#91;WHITE&#93;;
          mi->scalingFunction&#91;BLACK&#93; = &ScaleKPKP&#91;BLACK&#93;;
      &#125;
  &#125;

  // Compute the space weight
  if &#40;pos.non_pawn_material&#40;WHITE&#41; + pos.non_pawn_material&#40;BLACK&#41; >=
      2*QueenValueMidgame + 4*RookValueMidgame + 2*KnightValueMidgame&#41;
  &#123;
      int minorPieceCount =  pos.piece_count&#40;WHITE, KNIGHT&#41;
                           + pos.piece_count&#40;BLACK, KNIGHT&#41;
                           + pos.piece_count&#40;WHITE, BISHOP&#41;
                           + pos.piece_count&#40;BLACK, BISHOP&#41;;

      mi->spaceWeight = minorPieceCount * minorPieceCount;
  &#125;

  // Evaluate the material balance
  const int pieceCount&#91;2&#93;&#91;8&#93; = &#123;
  &#123; pos.piece_count&#40;WHITE, BISHOP&#41; > 1, pos.piece_count&#40;WHITE, PAWN&#41;, pos.piece_count&#40;WHITE, KNIGHT&#41;,
    pos.piece_count&#40;WHITE, BISHOP&#41;, pos.piece_count&#40;WHITE, ROOK&#41;, pos.piece_count&#40;WHITE, QUEEN&#41; &#125;,
  &#123; pos.piece_count&#40;BLACK, BISHOP&#41; > 1, pos.piece_count&#40;BLACK, PAWN&#41;, pos.piece_count&#40;BLACK, KNIGHT&#41;,
    pos.piece_count&#40;BLACK, BISHOP&#41;, pos.piece_count&#40;BLACK, ROOK&#41;, pos.piece_count&#40;BLACK, QUEEN&#41; &#125; &#125;;

  Color c, them;
  int sign, pt1, pt2, pc;
  int32_t v = 0, vv = 0, matValue = 0;

  for &#40;c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign&#41;
  &#123;
    // No pawns makes it difficult to win, even with a material advantage
    if (   pos.piece_count&#40;c, PAWN&#41; == 0
        && pos.non_pawn_material&#40;c&#41; - pos.non_pawn_material&#40;opposite_color&#40;c&#41;) <= BishopValueMidgame&#41;
    &#123;
        if (   pos.non_pawn_material&#40;c&#41; == pos.non_pawn_material&#40;opposite_color&#40;c&#41;)
            || pos.non_pawn_material&#40;c&#41; < RookValueMidgame&#41;
            mi->factor&#91;c&#93; = 0;
        else
        &#123;
            switch &#40;pos.piece_count&#40;c, BISHOP&#41;) &#123;
            case 2&#58;
                mi->factor&#91;c&#93; = 32;
                break;
            case 1&#58;
                mi->factor&#91;c&#93; = 12;
                break;
            case 0&#58;
                mi->factor&#91;c&#93; = 6;
                break;
            &#125;
        &#125;
    &#125;

    // Redundancy of major pieces, formula based on Kaufman's paper
    // "The Evaluation of Material Imbalances in Chess"
    // http&#58;//mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
    if &#40;pieceCount&#91;c&#93;&#91;ROOK&#93; >= 1&#41;
        matValue -= sign * (&#40;pieceCount&#91;c&#93;&#91;ROOK&#93; - 1&#41; * RedundantRookPenalty + pieceCount&#91;c&#93;&#91;QUEEN&#93; * RedundantQueenPenalty&#41;;

    them = opposite_color&#40;c&#41;;
	v = 0;

    // Second-degree polynomial material imbalance by Tord Romstad
    //
    // We use PIECE_TYPE_NONE as a place holder for the bishop pair "extended piece",
    // this allow us to be more flexible in defining bishop pair bonuses.
    for &#40;pt1 = PIECE_TYPE_NONE; pt1 <= QUEEN; pt1++)
    &#123;
        pc = pieceCount&#91;c&#93;&#91;pt1&#93;;
        if (!pc&#41;
		&#123;
			for &#40;pt2 = PIECE_TYPE_NONE; pt2 <= QUEEN; pt2++)
				v -=  &#40;3 - pieceCount&#91;c&#93;&#91;pt2&#93;) * MissingPiecetypeCoefficientsSameColor&#91;pt1&#93;&#91;pt2&#93;
			      + &#40;pieceCount&#91;them&#93;&#91;pt2&#93;) * MissingPiecetypeCoefficientsOppositeColor&#91;pt1&#93;&#91;pt2&#93;;
		&#125;

		if &#40;pc&#41;
		&#123;
			vv = LinearCoefficients&#91;pt1&#93;;
			
			for &#40;pt2 = PIECE_TYPE_NONE; pt2 <= pt1; pt2++)
			&#123;
				vv +=  pieceCount&#91;c&#93;&#91;pt2&#93; * QuadraticCoefficientsSameColor&#91;pt1&#93;&#91;pt2&#93;
				+ pieceCount&#91;them&#93;&#91;pt2&#93; * QuadraticCoefficientsOppositeColor&#91;pt1&#93;&#91;pt2&#93;;
			&#125;
			
			v += pc * vv;
		&#125;
    &#125;
    matValue += sign * v;
  &#125;
  mi->value = &#40;int16_t&#41;&#40;matValue / 16&#41;;
  return mi;
&#125;


/// EndgameFunctions member definitions

EndgameFunctions&#58;&#58;EndgameFunctions&#40;) &#123;

  add<EvaluationFunction<KNNK>  >("KNNK");
  add<EvaluationFunction<KPK>   >("KPK");
  add<EvaluationFunction<KBNK>  >("KBNK");
  add<EvaluationFunction<KRKP>  >("KRKP");
  add<EvaluationFunction<KRKB>  >("KRKB");
  add<EvaluationFunction<KRKN>  >("KRKN");
  add<EvaluationFunction<KQKR>  >("KQKR");
  add<EvaluationFunction<KBBKN> >("KBBKN");

  add<ScalingFunction<KNPK>    >("KNPK");
  add<ScalingFunction<KRPKR>   >("KRPKR");
  add<ScalingFunction<KBPKB>   >("KBPKB");
  add<ScalingFunction<KBPPKB>  >("KBPPKB");
  add<ScalingFunction<KBPKN>   >("KBPKN");
  add<ScalingFunction<KRPPKRP> >("KRPPKRP");
&#125;

EndgameFunctions&#58;&#58;~EndgameFunctions&#40;) &#123;

    for &#40;EFMap&#58;&#58;const_iterator it = maps.first.begin&#40;); it != maps.first.end&#40;); ++it&#41;
        delete it->second;

    for &#40;SFMap&#58;&#58;const_iterator it = maps.second.begin&#40;); it != maps.second.end&#40;); ++it&#41;
        delete it->second;
&#125;

Key EndgameFunctions&#58;&#58;buildKey&#40;const string& keyCode&#41; &#123;

    assert&#40;keyCode.length&#40;) > 0 && keyCode.length&#40;) < 8&#41;;
    assert&#40;keyCode&#91;0&#93; == 'K');

    string fen;
    bool upcase = false;

    // Build up a fen string with the given pieces, note that
    // the fen string could be of an illegal position.
    for &#40;size_t i = 0; i < keyCode.length&#40;); i++)
    &#123;
        if &#40;keyCode&#91;i&#93; == 'K')
            upcase = !upcase;

        fen += char&#40;upcase ? toupper&#40;keyCode&#91;i&#93;) &#58; tolower&#40;keyCode&#91;i&#93;));
    &#125;
    fen += char&#40;8 - keyCode.length&#40;) + '0');
    fen += "/8/8/8/8/8/8/8 w - -";
    return Position&#40;fen, false, 0&#41;.get_material_key&#40;);
&#125;

const string EndgameFunctions&#58;&#58;swapColors&#40;const string& keyCode&#41; &#123;

    // Build corresponding key for the opposite color&#58; "KBPKN" -> "KNKBP"
    size_t idx = keyCode.find&#40;'K', 1&#41;;
    return keyCode.substr&#40;idx&#41; + keyCode.substr&#40;0, idx&#41;;
&#125;

template<class T>
void EndgameFunctions&#58;&#58;add&#40;const string& keyCode&#41; &#123;

  typedef typename T&#58;&#58;Base F;
  typedef map<Key, F*> M;

  const_cast<M&>&#40;get<F>()).insert&#40;pair<Key, F*>&#40;buildKey&#40;keyCode&#41;, new T&#40;WHITE&#41;));
  const_cast<M&>&#40;get<F>()).insert&#40;pair<Key, F*>&#40;buildKey&#40;swapColors&#40;keyCode&#41;), new T&#40;BLACK&#41;));
&#125;

template<class T>
T* EndgameFunctions&#58;&#58;get&#40;Key key&#41; const &#123;

  typename map<Key, T*>&#58;&#58;const_iterator it = get<T>().find&#40;key&#41;;
  return it != get<T>().end&#40;) ? it->second &#58; NULL;
&#125;
Changes are:

Code: Select all

  const int MissingPiecetypeCoefficientsSameColor&#91;&#93;&#91;8&#93; = &#123;
  &#123; 3, 1, 1, 1, 1, 1 &#125;, &#123; 1, 3, 1, 1, 1, 1 &#125;, &#123; 1, 1, 3, 1, 1, 1 &#125;,
  &#123; 1, 1, 1, 3, 1, 1 &#125;, &#123; 1, 1, 1, 1, 3, 1 &#125;, &#123; 1, 1, 1, 1, 1, 3 &#125; &#125;;

  const int MissingPiecetypeCoefficientsOppositeColor&#91;&#93;&#91;8&#93; = &#123;
  &#123; 3, 1, 1, 1, 1, 1 &#125;, &#123; 1, 3, 1, 1, 1, 1 &#125;, &#123; 1, 1, 3, 1, 1, 1 &#125;,
  &#123; 1, 1, 1, 3, 1, 1 &#125;, &#123; 1, 1, 1, 1, 3, 1 &#125;, &#123; 1, 1, 1, 1, 1, 3 &#125; &#125;;

Code: Select all

  int32_t v = 0, vv = 0, matValue = 0;
int32_t is probably a bit over the top -to be able to include negative numbers- but I don't think it matters? It does not seem to change much in size of the executable for instance.

Code: Select all

      if (!pc&#41;
		&#123;
			for &#40;pt2 = PIECE_TYPE_NONE; pt2 <= QUEEN; pt2++)
				v -=  &#40;3 - pieceCount&#91;c&#93;&#91;pt2&#93;) * MissingPiecetypeCoefficientsSameColor&#91;pt1&#93;&#91;pt2&#93;
			      + &#40;pieceCount&#91;them&#93;&#91;pt2&#93;) * MissingPiecetypeCoefficientsOppositeColor&#91;pt1&#93;&#91;pt2&#93;;
		&#125;

		if &#40;pc&#41;
		&#123;
			vv = LinearCoefficients&#91;pt1&#93;;
			
			for &#40;pt2 = PIECE_TYPE_NONE; pt2 <= pt1; pt2++)
			&#123;
				vv +=  pieceCount&#91;c&#93;&#91;pt2&#93; * QuadraticCoefficientsSameColor&#91;pt1&#93;&#91;pt2&#93;
				+ pieceCount&#91;them&#93;&#91;pt2&#93; * QuadraticCoefficientsOppositeColor&#91;pt1&#93;&#91;pt2&#93;;
			&#125;
			
			v += pc * vv;
		&#125;
I realize that is is not yet tested, let alone tuned, I will just try to get some idea of its effects as I go along.

Eelco
Debugging is twice as hard as writing the code in the first
place. Therefore, if you write the code as cleverly as possible, you
are, by definition, not smart enough to debug it.
-- Brian W. Kernighan