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 Marco Costalba
6 Stockfish is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 Stockfish is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 //// Local definitions
39 const Value BishopPairMidgameBonus = Value(109);
40 const Value BishopPairEndgameBonus = Value(97);
42 Key KNNKMaterialKey, KKNNMaterialKey;
51 /// See header for a class description. It is declared here to avoid
52 /// to include <map> in the header file.
54 class EndgameFunctions {
58 EndgameEvaluationFunctionBase* getEEF(Key key) const;
59 EndgameScalingFunctionBase* getESF(Key key, Color* c) const;
62 void add(const std::string& keyCode, EndgameEvaluationFunctionBase* f);
63 void add(const std::string& keyCode, Color c, EndgameScalingFunctionBase* f);
64 Key buildKey(const std::string& keyCode);
69 EndgameScalingFunctionBase* fun;
72 std::map<Key, EndgameEvaluationFunctionBase*> EEFmap;
73 std::map<Key, ScalingInfo> ESFmap;
82 /// Constructor for the MaterialInfoTable class
84 MaterialInfoTable::MaterialInfoTable(unsigned int numOfEntries) {
87 entries = new MaterialInfo[size];
88 funcs = new EndgameFunctions();
89 if (!entries || !funcs)
91 std::cerr << "Failed to allocate " << (numOfEntries * sizeof(MaterialInfo))
92 << " bytes for material hash table." << std::endl;
99 /// Destructor for the MaterialInfoTable class
101 MaterialInfoTable::~MaterialInfoTable() {
108 /// MaterialInfoTable::clear() clears a material hash table by setting
109 /// all entries to 0.
111 void MaterialInfoTable::clear() {
113 memset(entries, 0, size * sizeof(MaterialInfo));
117 /// MaterialInfoTable::get_material_info() takes a position object as input,
118 /// computes or looks up a MaterialInfo object, and returns a pointer to it.
119 /// If the material configuration is not already present in the table, it
120 /// is stored there, so we don't have to recompute everything when the
121 /// same material configuration occurs again.
123 MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
125 Key key = pos.get_material_key();
126 int index = key & (size - 1);
127 MaterialInfo* mi = entries + index;
129 // If mi->key matches the position's material hash key, it means that we
130 // have analysed this material configuration before, and we can simply
131 // return the information we found the last time instead of recomputing it.
135 // Clear the MaterialInfo object, and set its key
139 // A special case before looking for a specialized evaluation function
140 // KNN vs K is a draw.
141 if (key == KNNKMaterialKey || key == KKNNMaterialKey)
143 mi->factor[WHITE] = mi->factor[BLACK] = 0;
147 // Let's look if we have a specialized evaluation function for this
148 // particular material configuration. First we look for a fixed
149 // configuration one, then a generic one if previous search failed.
150 if ((mi->evaluationFunction = funcs->getEEF(key)) != NULL)
153 else if ( pos.non_pawn_material(BLACK) == Value(0)
154 && pos.piece_count(BLACK, PAWN) == 0
155 && pos.non_pawn_material(WHITE) >= RookValueEndgame)
157 mi->evaluationFunction = &EvaluateKXK;
160 else if ( pos.non_pawn_material(WHITE) == Value(0)
161 && pos.piece_count(WHITE, PAWN) == 0
162 && pos.non_pawn_material(BLACK) >= RookValueEndgame)
164 mi->evaluationFunction = &EvaluateKKX;
167 else if ( pos.pawns() == EmptyBoardBB
168 && pos.rooks() == EmptyBoardBB
169 && pos.queens() == EmptyBoardBB)
171 // Minor piece endgame with at least one minor piece per side,
173 assert(pos.knights(WHITE) | pos.bishops(WHITE));
174 assert(pos.knights(BLACK) | pos.bishops(BLACK));
176 if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
177 && pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
179 mi->evaluationFunction = &EvaluateKmmKm;
184 // OK, we didn't find any special evaluation function for the current
185 // material configuration. Is there a suitable scaling function?
187 // The code below is rather messy, and it could easily get worse later,
188 // if we decide to add more special cases. We face problems when there
189 // are several conflicting applicable scaling functions and we need to
190 // decide which one to use.
192 EndgameScalingFunctionBase* sf;
194 if ((sf = funcs->getESF(key, &c)) != NULL)
196 mi->scalingFunction[c] = sf;
200 if ( pos.non_pawn_material(WHITE) == BishopValueMidgame
201 && pos.piece_count(WHITE, BISHOP) == 1
202 && pos.piece_count(WHITE, PAWN) >= 1)
203 mi->scalingFunction[WHITE] = &ScaleKBPK;
205 if ( pos.non_pawn_material(BLACK) == BishopValueMidgame
206 && pos.piece_count(BLACK, BISHOP) == 1
207 && pos.piece_count(BLACK, PAWN) >= 1)
208 mi->scalingFunction[BLACK] = &ScaleKKBP;
210 if ( pos.piece_count(WHITE, PAWN) == 0
211 && pos.non_pawn_material(WHITE) == QueenValueMidgame
212 && pos.piece_count(WHITE, QUEEN) == 1
213 && pos.piece_count(BLACK, ROOK) == 1
214 && pos.piece_count(BLACK, PAWN) >= 1)
215 mi->scalingFunction[WHITE] = &ScaleKQKRP;
217 else if ( pos.piece_count(BLACK, PAWN) == 0
218 && pos.non_pawn_material(BLACK) == QueenValueMidgame
219 && pos.piece_count(BLACK, QUEEN) == 1
220 && pos.piece_count(WHITE, ROOK) == 1
221 && pos.piece_count(WHITE, PAWN) >= 1)
222 mi->scalingFunction[BLACK] = &ScaleKRPKQ;
224 if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0))
226 if (pos.piece_count(BLACK, PAWN) == 0)
228 assert(pos.piece_count(WHITE, PAWN) >= 2);
229 mi->scalingFunction[WHITE] = &ScaleKPsK;
231 else if (pos.piece_count(WHITE, PAWN) == 0)
233 assert(pos.piece_count(BLACK, PAWN) >= 2);
234 mi->scalingFunction[BLACK] = &ScaleKKPs;
236 else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
238 mi->scalingFunction[WHITE] = &ScaleKPKPw;
239 mi->scalingFunction[BLACK] = &ScaleKPKPb;
243 // Compute the space weight
244 if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >=
245 2*QueenValueMidgame + 4*RookValueMidgame + 2*KnightValueMidgame)
247 int minorPieceCount = pos.piece_count(WHITE, KNIGHT)
248 + pos.piece_count(BLACK, KNIGHT)
249 + pos.piece_count(WHITE, BISHOP)
250 + pos.piece_count(BLACK, BISHOP);
252 mi->spaceWeight = minorPieceCount * minorPieceCount;
255 // Evaluate the material balance
258 Value egValue = Value(0);
259 Value mgValue = Value(0);
261 for (c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign)
263 // No pawns makes it difficult to win, even with a material advantage
264 if ( pos.piece_count(c, PAWN) == 0
265 && pos.non_pawn_material(c) - pos.non_pawn_material(opposite_color(c)) <= BishopValueMidgame)
267 if ( pos.non_pawn_material(c) == pos.non_pawn_material(opposite_color(c))
268 || pos.non_pawn_material(c) < RookValueMidgame)
272 switch (pos.piece_count(c, BISHOP)) {
287 if (pos.piece_count(c, BISHOP) >= 2)
289 mgValue += sign * BishopPairMidgameBonus;
290 egValue += sign * BishopPairEndgameBonus;
293 // Knights are stronger when there are many pawns on the board. The
294 // formula is taken from Larry Kaufman's paper "The Evaluation of Material
295 // Imbalances in Chess":
296 // http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
297 mgValue += sign * Value(pos.piece_count(c, KNIGHT)*(pos.piece_count(c, PAWN)-5)*16);
298 egValue += sign * Value(pos.piece_count(c, KNIGHT)*(pos.piece_count(c, PAWN)-5)*16);
300 // Redundancy of major pieces, again based on Kaufman's paper:
301 if (pos.piece_count(c, ROOK) >= 1)
303 Value v = Value((pos.piece_count(c, ROOK) - 1) * 32 + pos.piece_count(c, QUEEN) * 16);
308 mi->mgValue = int16_t(mgValue);
309 mi->egValue = int16_t(egValue);
314 /// EndgameFunctions member definitions. This class is used to store the maps
315 /// of end game and scaling functions that MaterialInfoTable will query for
316 /// each key. The maps are constant and are populated only at construction,
317 /// but are per-thread instead of globals to avoid expensive locks.
319 EndgameFunctions::EndgameFunctions() {
321 KNNKMaterialKey = buildKey("KNNK");
322 KKNNMaterialKey = buildKey("KKNN");
324 add("KPK", &EvaluateKPK);
325 add("KKP", &EvaluateKKP);
326 add("KBNK", &EvaluateKBNK);
327 add("KKBN", &EvaluateKKBN);
328 add("KRKP", &EvaluateKRKP);
329 add("KPKR", &EvaluateKPKR);
330 add("KRKB", &EvaluateKRKB);
331 add("KBKR", &EvaluateKBKR);
332 add("KRKN", &EvaluateKRKN);
333 add("KNKR", &EvaluateKNKR);
334 add("KQKR", &EvaluateKQKR);
335 add("KRKQ", &EvaluateKRKQ);
336 add("KBBKN", &EvaluateKBBKN);
337 add("KNKBB", &EvaluateKNKBB);
339 add("KNPK", WHITE, &ScaleKNPK);
340 add("KKNP", BLACK, &ScaleKKNP);
341 add("KRPKR", WHITE, &ScaleKRPKR);
342 add("KRKRP", BLACK, &ScaleKRKRP);
343 add("KBPKB", WHITE, &ScaleKBPKB);
344 add("KBKBP", BLACK, &ScaleKBKBP);
345 add("KBPPKB", WHITE, &ScaleKBPPKB);
346 add("KBKBPP", BLACK, &ScaleKBKBPP);
347 add("KBPKN", WHITE, &ScaleKBPKN);
348 add("KNKBP", BLACK, &ScaleKNKBP);
349 add("KRPPKRP", WHITE, &ScaleKRPPKRP);
350 add("KRPKRPP", BLACK, &ScaleKRPKRPP);
351 add("KRPPKRP", WHITE, &ScaleKRPPKRP);
352 add("KRPKRPP", BLACK, &ScaleKRPKRPP);
355 Key EndgameFunctions::buildKey(const std::string& keyCode) {
357 assert(keyCode.length() > 0 && keyCode[0] == 'K');
358 assert(keyCode.length() < 8);
363 // Build up a fen substring with the given pieces, note
364 // that the fen string could be of an illegal position.
365 for (size_t i = 0; i < keyCode.length(); i++)
367 if (keyCode[i] == 'K')
370 s << char(upcase? toupper(keyCode[i]) : tolower(keyCode[i]));
372 s << 8 - keyCode.length() << "/8/8/8/8/8/8/8 w -";
373 return Position(s.str()).get_material_key();
376 void EndgameFunctions::add(const std::string& keyCode, EndgameEvaluationFunctionBase* f) {
378 EEFmap.insert(std::pair<Key, EndgameEvaluationFunctionBase*>(buildKey(keyCode), f));
381 void EndgameFunctions::add(const std::string& keyCode, Color c, EndgameScalingFunctionBase* f) {
383 ScalingInfo s = {c, f};
384 ESFmap.insert(std::pair<Key, ScalingInfo>(buildKey(keyCode), s));
387 EndgameEvaluationFunctionBase* EndgameFunctions::getEEF(Key key) const {
389 std::map<Key, EndgameEvaluationFunctionBase*>::const_iterator it(EEFmap.find(key));
390 return (it != EEFmap.end() ? it->second : NULL);
393 EndgameScalingFunctionBase* EndgameFunctions::getESF(Key key, Color* c) const {
395 std::map<Key, ScalingInfo>::const_iterator it(ESFmap.find(key));
396 if (it == ESFmap.end())
400 return it->second.fun;