Add Game::is_dangerous() for Futility Pruning
[purplehaze.git] / src / eval.cpp
blob86573f602ef9b4dd626359854e65439cd1f96a39
1 /* Copyright (C) 2007-2011 Vincent Ollivier
3 * Purple Haze is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * Purple Haze is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include <assert.h>
18 #include <string>
19 #include <iostream>
20 #include <sstream>
21 #include <iomanip>
23 #include "game.h"
24 #include "eval.h"
25 #include "hashtable.h"
27 // PST[Phase][Color][PieceType][Square]
28 static int PST[2][2][NB_PIECE_TYPES][BOARD_SIZE] = { { { { 0 } } } };
30 void Game::init_eval()
32 for (int i = 0; i < 64; ++i) {
33 for (const PieceType& t : PIECE_TYPES) {
34 Square s = board.get_square(i);
36 int opening_score = 0;
37 int ending_score = 0;
39 switch (t) {
40 case PAWN:
41 // Develop central pawns
42 // But not side pawns
43 opening_score = PAWN_FILES_VALUES[board.get_file(s)];
45 // Run for promotion
46 ending_score = 10 * board.get_rank(s);
47 break;
48 case KNIGHT:
49 case BISHOP:
50 // Develop toward center files
51 opening_score = CENTER_BONUS[board.get_file(s)];
52 if (board.is_border(s)) {
53 opening_score = 2 * BORDER_MALUS;
55 // no break
56 default:
57 ending_score = CENTER_BONUS[board.get_file(s)];
58 ending_score += CENTER_BONUS[board.get_rank(s)];
59 break;
62 // Rank bonus
63 int bonus = OPENING_RANKS_BONUS[t][board.get_rank(s)];
64 opening_score += (opening_score * bonus) / 2;
66 PST[OPENING][WHITE][t][s] = opening_score;
67 PST[ENDING][WHITE][t][s] = ending_score;
70 // Special corrections
71 // Urge to develop light pieces during opening
72 PST[OPENING][WHITE][KNIGHT][B1] = -20;
73 PST[OPENING][WHITE][KNIGHT][G1] = -20;
74 PST[OPENING][WHITE][BISHOP][C1] = -15;
75 PST[OPENING][WHITE][BISHOP][F1] = -15;
76 // But others should stay where they are
77 PST[OPENING][WHITE][ROOK][A1] = 5;
78 PST[OPENING][WHITE][ROOK][H1] = 5;
79 PST[OPENING][WHITE][KING][E1] = 5;
80 // Fianchetto
81 PST[OPENING][WHITE][BISHOP][B2] = 3;
82 PST[OPENING][WHITE][BISHOP][G2] = 3;
83 // Protection against bishop attacks
84 PST[OPENING][WHITE][PAWN][A3] += 3;
85 PST[OPENING][WHITE][PAWN][H3] += 3;
87 // Flip scores according to black's side
88 for (int i = 0; i < 2; ++i) {
89 for (int j = 0; j < 64; ++j) {
90 for (const PieceType& t : PIECE_TYPES) {
91 Square ws = board.get_square(j);
92 Square bs = board.flip(ws);
93 PST[i][BLACK][t][bs] = PST[i][WHITE][t][ws];
99 static const int LAZY_EVAL_MARGIN = PIECE_VALUE[ROOK];
101 int Game::eval(int alpha, int beta)
103 // Material evaluation
104 int score = material_eval();
106 // TODO Draws should be caught here
107 // if (score == 0) return 0; // Draw
108 if (score > PIECE_VALUE[KING]) return INF; // Win
109 if (score < -PIECE_VALUE[KING]) return -INF; // Loss
111 // Lazy evaluation
112 if (score + LAZY_EVAL_MARGIN < alpha) return score;
113 if (score - LAZY_EVAL_MARGIN > beta) return score;
115 // TODO Positional evaluation
116 score += position_eval();
118 // TODO Mobility evaluation
119 //score += mobility_eval();
121 return score;
124 int Game::material_eval()
126 int score = 0;
127 Position& pos = current_position();
129 // Lookup position in material hash table
130 bool is_empty = true;
131 int hash_score = material_table.lookup(pos.material_hash(), &is_empty);
132 if (!is_empty) {
133 const Color c = pos.get_turn_color();
134 return (c == WHITE ? hash_score : -hash_score);
137 int material_score[2] = { 0 };
138 int material_bonus[2] = { 0 };
139 for (const Color& c : COLORS) {
140 int nb_pawns = 0;
141 int nb_minors = 0;
142 for (const PieceType& t : PIECE_TYPES) {
143 const int n = pieces.count(c, t);
144 // Pieces' standard values
145 material_score[c] += n * PIECE_VALUE[t];
147 // Bonus values depending on material imbalance
148 int adj;
149 switch (t) {
150 case PAWN:
151 nb_pawns = n;
152 if (n == 0) material_bonus[c] += NO_PAWNS_MALUS;
153 break;
154 case KNIGHT:
155 nb_minors = n;
156 if (n > 1) material_bonus[c] += REDUNDANCY_MALUS;
158 // Value adjusted by the number of pawns on the board
159 adj = PAWNS_ADJUSTEMENT[KNIGHT][nb_pawns];
160 material_bonus[c] += n * adj;
161 break;
162 case BISHOP:
163 nb_minors += n;
164 // Bishop bonus pair (from +40 to +64):
165 // less than half a pawn when most or all the pawns are on
166 // the board, and more than half a pawn when half or more
167 // of the pawns are gone. (Kaufman 1999)
169 // No bonus for two bishops controlling the same color
170 // No bonus for more than two bishops
171 if (n == 2 && !board.is_same_color(
172 pieces.get_position(c, t, 0),
173 pieces.get_position(c, t, 1))) {
174 material_bonus[c] +=
175 BISHOP_PAIR_BONUS + (3 * 8 - nb_pawns);
178 // Value adjusted by the number of pawns on the board
179 adj = PAWNS_ADJUSTEMENT[BISHOP][nb_pawns];
180 material_bonus[c] += n * adj;
181 break;
182 case ROOK:
183 // Principle of the redundancy (Kaufman 1999)
184 if (n > 1) material_bonus[c] += REDUNDANCY_MALUS;
186 // Value adjusted by the number of pawns on the board
187 adj = PAWNS_ADJUSTEMENT[ROOK][nb_pawns];
188 material_bonus[c] += n * adj;
189 break;
190 case QUEEN:
191 if (nb_minors > 1) {
192 // With two or more minor pieces, the queen
193 // equal two rooks. (Kaufman 1999)
194 material_bonus[c] +=
195 (2 * PIECE_VALUE[ROOK]) - (PIECE_VALUE[QUEEN]);
197 // Value adjusted by the number of pawns on the board
198 adj = PAWNS_ADJUSTEMENT[QUEEN][nb_pawns];
199 material_bonus[c] += n * adj;
200 break;
201 default:
202 break;
207 // Draw by insufficient material detection
208 bool is_draw = false;
209 const int K = PIECE_VALUE[KING];
210 const int P = PIECE_VALUE[PAWN];
211 const int N = PIECE_VALUE[KNIGHT];
212 const int B = PIECE_VALUE[BISHOP];
213 for (const Color& c : COLORS) {
214 is_draw = true;
215 // FIDE rules for draw
216 if (material_score[c] == K) {
217 if (material_score[!c] == K) break;
218 if (material_score[!c] == K + B) break;
219 if (material_score[!c] == K + N) break;
220 if (material_score[!c] == K + N + N) break;
222 // TODO is this duplicate with MALUS_NO_PAWNS?
223 const int nb_opponent_pawns = pieces.count(!c, PAWN);
224 if (nb_opponent_pawns == 0 && material_score[!c] < K + 4 * P) {
225 break;
228 is_draw = false; // no break happened
231 const Color c = pos.get_turn_color();
233 if (!is_draw) {
234 score = material_score[c] - material_score[!c];
235 score += material_bonus[c] - material_bonus[!c];
238 // Save score to material hash table
239 hash_score = (c == WHITE ? score : -score);
240 material_table.save(pos.material_hash(), hash_score);
242 return score;
245 int castling_score(const Position& pos, Color c)
247 int score = 0;
248 if (pos.has_castle(c)) {
249 score += CASTLE_BONUS;
250 } else {
251 for (const PieceType& t : SIDE_TYPES) { // for QUEEN and KING side
252 if (!pos.can_castle(c, t)) {
253 score += BREAKING_CASTLE_MALUS;
257 return score;
260 int Game::position_eval()
262 int phase = 0;
263 int position_score[2][2] = { { 0 } };
264 int pawns_files[2][8] = { { 0 } };
265 const Position& pos = current_position();
266 for (const Color& c : COLORS) {
267 for (const PieceType& t : PIECE_TYPES) {
268 const int n = pieces.count(c, t);
269 phase += n * PHASE_COEF[t];
270 for (int j = 0; j < n; ++j) {
271 Square s = pieces.get_position(c, t, j);
272 position_score[OPENING][c] += PST[OPENING][c][t][s];
273 position_score[ENDING][c] += PST[ENDING][c][t][s];
274 if (t == PAWN) pawns_files[c][board.get_file(s)]++;
278 int pawns_score = 0;
279 for (int j = 0; j < 8; ++j) {
280 pawns_score += MULTI_PAWNS_MALUS[pawns_files[c][j]];
282 position_score[OPENING][c] += pawns_score;
284 // Rooks' files bonus
285 int rooks_score = 0;
286 const int nb_rooks = pieces.count(c, ROOK);
287 for (int j = 0; j < nb_rooks; ++j) {
288 Square s = pieces.get_position(c, ROOK, j);
289 if (!pawns_files[!c][board.get_file(s)]) {
290 if (!pawns_files[c][board.get_file(s)]) {
291 rooks_score += OPEN_FILE_BONUS;
293 else rooks_score += HALF_OPEN_FILE_BONUS;
296 position_score[OPENING][c] += rooks_score;
298 // Castling bonus/malus
299 position_score[OPENING][c] += castling_score(pos, c);
303 // Retrieve opening and ending score
304 const Color& c = pos.get_turn_color();
305 const int opening = position_score[OPENING][c] -
306 position_score[OPENING][!c];
307 const int ending = position_score[ENDING][c] -
308 position_score[ENDING][!c];
310 // Tapered Eval (idea from Fruit 2.1)
311 const int max = PHASE_MAX;
312 phase = (phase > max ? max : (phase < 0 ? 0 : phase));
313 return (opening * phase + ending * (max - phase)) / max;