Add simple debug hit rate counter
[stockfish] / src / material.cpp
1 /*
2   Glaurung, a UCI chess playing engine.
3   Copyright (C) 2004-2008 Tord Romstad
4
5   Glaurung is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9   
10   Glaurung is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14   
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20 ////
21 //// Includes
22 ////
23
24 #include <cassert>
25
26 #include "material.h"
27
28
29 ////
30 //// Local definitions
31 ////
32
33 namespace {
34
35   const Value BishopPairMidgameBonus = Value(100);
36   const Value BishopPairEndgameBonus = Value(100);
37
38   Key KPKMaterialKey, KKPMaterialKey;
39   Key KBNKMaterialKey, KKBNMaterialKey;
40   Key KRKPMaterialKey, KPKRMaterialKey;
41   Key KRKBMaterialKey, KBKRMaterialKey;
42   Key KRKNMaterialKey, KNKRMaterialKey;
43   Key KQKRMaterialKey, KRKQMaterialKey;
44   Key KRPKRMaterialKey, KRKRPMaterialKey;
45   Key KRPPKRPMaterialKey, KRPKRPPMaterialKey;
46   Key KNNKMaterialKey, KKNNMaterialKey;
47   Key KBPKBMaterialKey, KBKBPMaterialKey;
48   Key KBPKNMaterialKey, KNKBPMaterialKey;
49   Key KNPKMaterialKey, KKNPMaterialKey;
50   Key KPKPMaterialKey;
51
52 };
53
54
55 ////
56 //// Functions
57 ////
58
59 /// MaterialInfo::init() is called during program initialization.  It 
60 /// precomputes material hash keys for a few basic endgames, in order
61 /// to make it easy to recognize such endgames when they occur.
62
63 void MaterialInfo::init() {
64   KPKMaterialKey = Position::zobMaterial[WHITE][PAWN][1];
65   KKPMaterialKey = Position::zobMaterial[BLACK][PAWN][1];
66   KBNKMaterialKey =
67     Position::zobMaterial[WHITE][BISHOP][1] ^
68     Position::zobMaterial[WHITE][KNIGHT][1];
69   KKBNMaterialKey =
70     Position::zobMaterial[BLACK][BISHOP][1] ^
71     Position::zobMaterial[BLACK][KNIGHT][1];
72   KRKPMaterialKey =
73     Position::zobMaterial[WHITE][ROOK][1] ^
74     Position::zobMaterial[BLACK][PAWN][1];
75   KPKRMaterialKey =
76     Position::zobMaterial[WHITE][PAWN][1] ^
77     Position::zobMaterial[BLACK][ROOK][1];
78   KRKBMaterialKey =
79     Position::zobMaterial[WHITE][ROOK][1] ^
80     Position::zobMaterial[BLACK][BISHOP][1];
81   KBKRMaterialKey =
82     Position::zobMaterial[WHITE][BISHOP][1] ^
83     Position::zobMaterial[BLACK][ROOK][1];
84   KRKNMaterialKey =
85     Position::zobMaterial[WHITE][ROOK][1] ^
86     Position::zobMaterial[BLACK][KNIGHT][1];
87   KNKRMaterialKey =
88     Position::zobMaterial[WHITE][KNIGHT][1] ^
89     Position::zobMaterial[BLACK][ROOK][1];
90   KQKRMaterialKey =
91     Position::zobMaterial[WHITE][QUEEN][1] ^
92     Position::zobMaterial[BLACK][ROOK][1];
93   KRKQMaterialKey =
94     Position::zobMaterial[WHITE][ROOK][1] ^
95     Position::zobMaterial[BLACK][QUEEN][1];
96   KRPKRMaterialKey =
97     Position::zobMaterial[WHITE][ROOK][1] ^
98     Position::zobMaterial[WHITE][PAWN][1] ^
99     Position::zobMaterial[BLACK][ROOK][1];
100   KRKRPMaterialKey =
101     Position::zobMaterial[WHITE][ROOK][1] ^
102     Position::zobMaterial[BLACK][ROOK][1] ^
103     Position::zobMaterial[BLACK][PAWN][1];
104   KRPPKRPMaterialKey =
105     Position::zobMaterial[WHITE][ROOK][1] ^
106     Position::zobMaterial[WHITE][PAWN][1] ^
107     Position::zobMaterial[WHITE][PAWN][2] ^
108     Position::zobMaterial[BLACK][ROOK][1] ^
109     Position::zobMaterial[BLACK][PAWN][1];
110   KRPKRPPMaterialKey =
111     Position::zobMaterial[WHITE][ROOK][1] ^
112     Position::zobMaterial[WHITE][PAWN][1] ^
113     Position::zobMaterial[BLACK][ROOK][1] ^
114     Position::zobMaterial[BLACK][PAWN][1] ^
115     Position::zobMaterial[BLACK][PAWN][2];
116   KNNKMaterialKey =
117     Position::zobMaterial[WHITE][KNIGHT][1] ^
118     Position::zobMaterial[WHITE][KNIGHT][2];
119   KKNNMaterialKey =
120     Position::zobMaterial[BLACK][KNIGHT][1] ^
121     Position::zobMaterial[BLACK][KNIGHT][2];
122   KBPKBMaterialKey =
123     Position::zobMaterial[WHITE][BISHOP][1] ^
124     Position::zobMaterial[WHITE][PAWN][1] ^
125     Position::zobMaterial[BLACK][BISHOP][1];
126   KBKBPMaterialKey =
127     Position::zobMaterial[WHITE][BISHOP][1] ^
128     Position::zobMaterial[BLACK][BISHOP][1] ^
129     Position::zobMaterial[BLACK][PAWN][1];
130   KBPKNMaterialKey =
131     Position::zobMaterial[WHITE][BISHOP][1] ^
132     Position::zobMaterial[WHITE][PAWN][1] ^
133     Position::zobMaterial[BLACK][KNIGHT][1];
134   KNKBPMaterialKey =
135     Position::zobMaterial[WHITE][KNIGHT][1] ^
136     Position::zobMaterial[BLACK][BISHOP][1] ^
137     Position::zobMaterial[BLACK][PAWN][1];
138   KNPKMaterialKey =
139     Position::zobMaterial[WHITE][KNIGHT][1] ^
140     Position::zobMaterial[WHITE][PAWN][1];
141   KKNPMaterialKey =
142     Position::zobMaterial[BLACK][KNIGHT][1] ^
143     Position::zobMaterial[BLACK][PAWN][1];
144   KPKPMaterialKey =
145     Position::zobMaterial[WHITE][PAWN][1] ^
146     Position::zobMaterial[BLACK][PAWN][1];
147 }
148
149
150 /// Constructor for the MaterialInfoTable class.
151
152 MaterialInfoTable::MaterialInfoTable(unsigned numOfEntries) {
153   size = numOfEntries;
154   entries = new MaterialInfo[size];
155   if(entries == NULL) {
156     std::cerr << "Failed to allocate " << (numOfEntries * sizeof(MaterialInfo))
157               << " bytes for material hash table." << std::endl;
158     exit(EXIT_FAILURE);
159   }
160   this->clear();
161 }
162
163
164 /// Destructor for the MaterialInfoTable class.
165
166 MaterialInfoTable::~MaterialInfoTable() {
167   delete [] entries;
168 }
169
170
171 /// MaterialInfoTable::clear() clears a material hash table by setting
172 /// all entries to 0.
173
174 void MaterialInfoTable::clear() {
175   memset(entries, 0, size * sizeof(MaterialInfo));
176 }
177
178
179 /// MaterialInfoTable::get_material_info() takes a position object as input,
180 /// computes or looks up a MaterialInfo object, and returns a pointer to it.
181 /// If the material configuration is not already present in the table, it
182 /// is stored there, so we don't have to recompute everything when the 
183 /// same material configuration occurs again.
184
185 MaterialInfo *MaterialInfoTable::get_material_info(const Position &pos) {
186   Key key = pos.get_material_key();
187   int index = key & (size - 1);
188   MaterialInfo *mi = entries + index;
189
190   // If mi->key matches the position's material hash key, it means that we 
191   // have analysed this material configuration before, and we can simply
192   // return the information we found the last time instead of recomputing it:
193   if(mi->key == key)
194     return mi;
195
196   // Clear the MaterialInfo object, and set its key:
197   mi->clear();
198   mi->key = key;
199
200   // A special case before looking for a specialized evaluation function:
201   // KNN vs K is a draw:
202   if(key == KNNKMaterialKey || key == KKNNMaterialKey) {
203     mi->factor[WHITE] = mi->factor[BLACK] = 0;
204     return mi;
205   }
206
207   // Let's look if we have a specialized evaluation function for this
208   // particular material configuration:
209   if(key == KPKMaterialKey) {
210     mi->evaluationFunction = &EvaluateKPK;
211     return mi;
212   }
213   else if(key == KKPMaterialKey) {
214     mi->evaluationFunction = &EvaluateKKP;
215     return mi;
216   }
217   else if(key == KBNKMaterialKey) {
218     mi->evaluationFunction = &EvaluateKBNK;
219     return mi;
220   }
221   else if(key == KKBNMaterialKey) {
222     mi->evaluationFunction = &EvaluateKKBN;
223     return mi;
224   }
225   else if(key == KRKPMaterialKey) {
226     mi->evaluationFunction = &EvaluateKRKP;
227     return mi;
228   }
229   else if(key == KPKRMaterialKey) {
230     mi->evaluationFunction = &EvaluateKPKR;
231     return mi;
232   }
233   else if(key == KRKBMaterialKey) {
234     mi->evaluationFunction = &EvaluateKRKB;
235     return mi;
236   }
237   else if(key == KBKRMaterialKey) {
238     mi->evaluationFunction = &EvaluateKBKR;
239     return mi;
240   }
241   else if(key == KRKNMaterialKey) {
242     mi->evaluationFunction = &EvaluateKRKN;
243     return mi;
244   }
245   else if(key == KNKRMaterialKey) {
246     mi->evaluationFunction = &EvaluateKNKR;
247     return mi;
248   }
249   else if(key == KQKRMaterialKey) {
250     mi->evaluationFunction = &EvaluateKQKR;
251     return mi;
252   }
253   else if(key == KRKQMaterialKey) {
254     mi->evaluationFunction = &EvaluateKRKQ;
255     return mi;
256   }
257   else if(pos.non_pawn_material(BLACK) == Value(0) &&
258           pos.pawn_count(BLACK) == 0 &&
259           pos.non_pawn_material(WHITE) >= RookValueEndgame) {
260     mi->evaluationFunction = &EvaluateKXK;
261     return mi;
262   }
263   else if(pos.non_pawn_material(WHITE) == Value(0) &&
264           pos.pawn_count(WHITE) == 0 &&
265           pos.non_pawn_material(BLACK) >= RookValueEndgame) {
266     mi->evaluationFunction = &EvaluateKKX;
267     return mi;
268   }
269
270   // OK, we didn't find any special evaluation function for the current
271   // material configuration.  Is there a suitable scaling function?
272   //
273   // The code below is rather messy, and it could easily get worse later,
274   // if we decide to add more special cases.  We face problems when there
275   // are several conflicting applicable scaling functions and we need to
276   // decide which one to use.
277
278   if(key == KRPKRMaterialKey) {
279     mi->scalingFunction[WHITE] = &ScaleKRPKR;
280     return mi;
281   }
282   if(key == KRKRPMaterialKey) {
283     mi->scalingFunction[BLACK] = &ScaleKRKRP;
284     return mi;
285   }
286   if(key == KRPPKRPMaterialKey) {
287     mi->scalingFunction[WHITE] = &ScaleKRPPKRP;
288     return mi;
289   }
290   else if(key == KRPKRPPMaterialKey) {
291     mi->scalingFunction[BLACK] = &ScaleKRPKRPP;
292     return mi;
293   }
294   if(key == KBPKBMaterialKey) {
295     mi->scalingFunction[WHITE] = &ScaleKBPKB;
296     return mi;
297   }
298   if(key == KBKBPMaterialKey) {
299     mi->scalingFunction[BLACK] = &ScaleKBKBP;
300     return mi;
301   }
302   if(key == KBPKNMaterialKey) {
303     mi->scalingFunction[WHITE] = &ScaleKBPKN;
304     return mi;
305   }
306   if(key == KNKBPMaterialKey) {
307     mi->scalingFunction[BLACK] = &ScaleKNKBP;
308     return mi;
309   }
310   if(key == KNPKMaterialKey) {
311     mi->scalingFunction[WHITE] = &ScaleKNPK;
312     return mi;
313   }
314   if(key == KKNPMaterialKey) {
315     mi->scalingFunction[BLACK] = &ScaleKKNP;
316     return mi;
317   }
318
319   if(pos.non_pawn_material(WHITE) == BishopValueMidgame &&
320      pos.bishop_count(WHITE) == 1 && pos.pawn_count(WHITE) >= 1)
321     mi->scalingFunction[WHITE] = &ScaleKBPK;
322   if(pos.non_pawn_material(BLACK) == BishopValueMidgame &&
323      pos.bishop_count(BLACK) == 1 && pos.pawn_count(BLACK) >= 1)
324     mi->scalingFunction[BLACK] = &ScaleKKBP;
325
326   if(pos.pawn_count(WHITE) == 0 &&
327      pos.non_pawn_material(WHITE) == QueenValueMidgame &&
328      pos.queen_count(WHITE) == 1 &&
329      pos.rook_count(BLACK) == 1 && pos.pawn_count(BLACK) >= 1)
330     mi->scalingFunction[WHITE] = &ScaleKQKRP;
331   else if(pos.pawn_count(BLACK) == 0 &&
332           pos.non_pawn_material(BLACK) == QueenValueMidgame &&
333           pos.queen_count(BLACK) == 1 &&
334           pos.rook_count(WHITE) == 1 && pos.pawn_count(WHITE) >= 1)
335     mi->scalingFunction[BLACK] = &ScaleKRPKQ;
336
337   if(pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0)) {
338     if(pos.pawn_count(BLACK) == 0) {
339       assert(pos.pawn_count(WHITE) >= 2);
340       mi->scalingFunction[WHITE] = &ScaleKPsK;
341     }
342     else if(pos.pawn_count(WHITE) == 0) {
343       assert(pos.pawn_count(BLACK) >= 2);
344       mi->scalingFunction[BLACK] = &ScaleKKPs;
345     }
346     else if(pos.pawn_count(WHITE) == 1 && pos.pawn_count(BLACK) == 1) {
347       mi->scalingFunction[WHITE] = &ScaleKPKPw;
348       mi->scalingFunction[BLACK] = &ScaleKPKPb;
349     }
350   }
351
352   // Evaluate the material balance.
353   
354   Color c;
355   int sign;
356   Value egValue = Value(0), mgValue = Value(0);
357   
358   for(c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign) {
359
360     // No pawns makes it difficult to win, even with a material advantage:
361     if(pos.pawn_count(c) == 0 &&
362        pos.non_pawn_material(c) - pos.non_pawn_material(opposite_color(c))
363        <= BishopValueMidgame) {
364       if(pos.non_pawn_material(c) == pos.non_pawn_material(opposite_color(c)))
365         mi->factor[c] = 0;
366       else if(pos.non_pawn_material(c) < RookValueMidgame)
367         mi->factor[c] = 0;
368       else {
369         switch(pos.bishop_count(c)) {
370         case 2:
371           mi->factor[c] = 32; break;
372         case 1:
373           mi->factor[c] = 12; break;
374         case 0:
375           mi->factor[c] = 6; break;
376         }
377       }
378     }
379     
380     // Bishop pair:
381     if(pos.bishop_count(c) >= 2) {
382       mgValue += sign * BishopPairMidgameBonus;
383       egValue += sign * BishopPairEndgameBonus;
384     }
385
386     // Knights are stronger when there are many pawns on the board.  The 
387     // formula is taken from Larry Kaufman's paper "The Evaluation of Material
388     // Imbalances in Chess": 
389     // http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
390     mgValue += sign * Value(pos.knight_count(c)*(pos.pawn_count(c)-5)*16);
391     egValue += sign * Value(pos.knight_count(c)*(pos.pawn_count(c)-5)*16);
392
393     // Redundancy of major pieces, again based on Kaufman's paper:
394     if(pos.rook_count(c) >= 1) {
395       Value v = Value((pos.rook_count(c) - 1) * 32 + pos.queen_count(c) * 16);
396       mgValue -= sign * v;
397       egValue -= sign * v;
398     }
399       
400   }
401
402   mi->mgValue = int16_t(mgValue);
403   mi->egValue = int16_t(egValue);
404
405   return mi;
406 }