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/>.
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;
41 // Develop central pawns
43 opening_score
= PAWN_FILES_VALUES
[board
.get_file(s
)];
46 ending_score
= 10 * board
.get_rank(s
);
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
;
57 ending_score
= CENTER_BONUS
[board
.get_file(s
)];
58 ending_score
+= CENTER_BONUS
[board
.get_rank(s
)];
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;
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
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();
124 int Game::material_eval()
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
);
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
) {
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
152 if (n
== 0) material_bonus
[c
] += NO_PAWNS_MALUS
;
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
;
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))) {
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
;
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
;
192 // With two or more minor pieces, the queen
193 // equal two rooks. (Kaufman 1999)
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
;
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
) {
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
) {
228 is_draw
= false; // no break happened
231 const Color c
= pos
.get_turn_color();
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
);
245 int castling_score(const Position
& pos
, Color c
)
248 if (pos
.has_castle(c
)) {
249 score
+= CASTLE_BONUS
;
251 for (const PieceType
& t
: SIDE_TYPES
) { // for QUEEN and KING side
252 if (!pos
.can_castle(c
, t
)) {
253 score
+= BREAKING_CASTLE_MALUS
;
260 int Game::position_eval()
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
)]++;
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
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
;