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/>.
27 inline bool Game::is_dangerous(Move m
)
29 // current_position() is used assuming
30 // that the move as already been made.
32 if (board
[m
.dest()].type() == PAWN
) {
33 const Color c
= !current_position().get_turn_color();
34 if (m
.dest_rank() + RANK_6
* c
== RANK_7
) return true;
39 const Piece capture = current_position().get_capture();
40 if (capture.type() != PAWN) return true;
43 return m.is_promotion();
46 return m
.is_capture() || m
.is_promotion();
49 unsigned long long int Game::perft(unsigned int depth
)
51 if (depth
== 0) return 1;
52 unsigned long long int nodes
= 0;
53 Color c
= current_position().get_turn_color();
54 bool use_lazy
= false; // Lazy moves generation is not usefull in perft()
55 Moves
moves(board
, pieces
, current_position(), search_moves
, use_lazy
);
57 while (!(move
= moves
.next()).is_null()) {
60 if (is_legal(move
)) ++nodes
; // TODO: Finish is_legal()
64 if (!is_check(c
)) nodes
+= perft(depth
- 1);
73 int Game::quiescence(int alpha
, int beta
, int depth
, const int ply
)
75 if (time
.poll(nodes_count
)) return 0;
77 const int stand_pat
= eval(alpha
, beta
);
78 if (ply
>= MAX_PLY
) return stand_pat
;
80 if (stand_pat
>= beta
) return stand_pat
; // Beta cut-off
83 const int delta
= PIECE_VALUE
[QUEEN
]; // TODO: Switch off in late endgame
84 if (stand_pat
< alpha
- delta
) return alpha
;
86 if (alpha
< stand_pat
) alpha
= stand_pat
; // New alpha
88 Color player
= current_position().get_turn_color();
90 Moves
moves(board
, pieces
, current_position(), search_moves
);
92 while (!(move
= moves
.next()).is_null()) {
93 if (moves
.state() > GOOD_CAPTURES
) break; // Skip bad captures
96 if (is_check(player
)) { // Illegal move
101 const int score
= -quiescence(-beta
, -alpha
, depth
- 1, ply
+ 1);
103 if (time
.poll(nodes_count
)) return 0;
111 if (time
.poll(nodes_count
)) return 0;
115 template<NodeType node_type
>
116 int Game::search(int alpha
, int beta
, int depth
, const int ply
)
118 if (time
.poll(nodes_count
)) return 0;
119 if (depth
<= 0) return quiescence(alpha
, beta
, 0, ply
+ 1); // Quiescence
120 if (tree
.has_repetition_draw()) return 0; // Repetition draw rules
123 const int old_alpha
= alpha
;
124 Position pos
= current_position();
125 int best_score
= -INF
;
127 const bool is_pv
= (node_type
== PV
);
129 // Lookup in Transposition Table
131 Transposition trans
= tt
.lookup(pos
.hash(), &is_empty
);
133 // FIXME Avoid a potential bug with tt.lookup()
134 const bool discard
= pos
.hash() == 0 &&
135 trans
.get_bound() == UNDEF_BOUND
;
137 if (/*!is_pv &&*/ depth
<= trans
.get_depth() && !discard
) {
138 const int tr_score
= trans
.get_value();
139 switch (trans
.get_bound()) {
140 case EXACT
: return tr_score
; // Already searched node
141 case UPPER
: if (tr_score
< beta
) beta
= tr_score
; break;
142 case LOWER
: if (tr_score
> alpha
) alpha
= tr_score
; break;
143 default: assert(false);
145 if (alpha
>= beta
) return tr_score
; // TT cause a cut-off
148 // If the transposition does not contain the best move,
149 // best_move.is_null() will be true.
150 best_move
= trans
.get_best_move();
153 const Color player
= pos
.get_turn_color();
154 const bool is_in_check
= is_check(player
);
155 const bool is_null_move
= !pos
.get_null_move_right(); // No more than one
159 if (is_in_check
) ++depth
;
164 const int nb_pieces
= pieces
.count(player
);
165 const int nb_pawns
= pieces
.count(player
, PAWN
);
166 const bool is_pawn_ending
= nb_pieces
== nb_pawns
+ 1; // Pawns + king
168 const bool nmp_allowed
=
174 if (nmp_allowed
&& depth
> NMP_DEPTH
&& nb_pieces
> 3) {
176 make_move(null_move
);
177 current_position().set_null_move_right(false); // No consecutive NM
178 const int r
= R_ADAPT(depth
, nb_pieces
);
179 score
= -search
<node_type
>(-beta
, -beta
+ 1, depth
- r
- 1, ply
+ 1);
180 undo_move(null_move
);
184 } else if (is_null_move
) {
185 // Next move we will again have the right to do another null-move
186 pos
.set_null_move_right(true);
191 // Internal Iterative Deepening
192 const bool iid_allowed
= !is_null_move
&& is_pv
;
193 if (iid_allowed
&& depth
> IID_DEPTH
&& best_move
.is_null()) {
194 search
<PV
>(alpha
, beta
, depth
/ 2, ply
);
195 best_move
= tt
.lookup(pos
.hash(), &is_empty
).get_best_move();
199 bool legal_move_found
= false;
200 bool is_principal_variation
= true;
202 Moves
moves(board
, pieces
, current_position(), search_moves
);
203 moves
.add(best_move
, BEST
);
205 // Killer moves need pseudo legality checking before we can use them,
206 // but they can cause a cut-off and dispense to generate quiet moves
208 for (int i
= 0; i
< MAX_KILLERS
; ++i
) {
209 Move killer
= get_killer_move(depth
, i
);
210 if (is_legal(killer
)) {
211 moves
.add(killer
, KILLERS
);
216 while (!(move
= moves
.next()).is_null()) {
217 if (move
.is_capture()) {
218 if (board
[move
.dest()].type() == KING
) {
219 return INF
- ply
; // Checkmate
223 if (is_check(player
)) { // Skip illegal move
227 legal_move_found
= true;
229 // PVS code from http://www.talkchess.com/forum/viewtopic.php?t=26974
230 if (is_principal_variation
) {
231 best_score
= -search
<PV
>(-beta
, -alpha
, depth
- 1, ply
+ 1);
234 if (best_score
> alpha
) {
235 if (best_score
>= beta
) { // Beta cut-off
236 // Update killer moves
237 if (!move
.is_capture()) {
238 set_killer_move(depth
, move
);
246 is_principal_variation
= false;
248 const bool is_giving_check
= is_check(!player
);
252 const bool fp_allowed
=
255 !is_killer_move(depth
, move
) &&
256 !is_dangerous(move
) &&
259 best_score
< INF
- MAX_PLY
&&
260 pieces
.count(!player
) > 3;
262 if (fp_allowed
&& depth
<= FUTILITY_DEPTH
) {
263 // Using an array of margins is an idea from Crafty
264 score
= material_eval() + FUTILITY_MARGINS
[depth
];
272 int r
= 0; // Depth reduction
275 // Late Move Reduction
276 const bool lmr_allowed
=
279 !is_killer_move(depth
, move
) &&
280 !move
.is_capture() &&
281 !move
.is_promotion();
283 if (lmr_allowed
&& depth
> LMR_DEPTH
) {
284 ++r
; // Do the search at a reduced depth
289 score
= -search
<ALL
>(-alpha
- 1, -alpha
, depth
- r
- 1, ply
+ 1);
292 if (alpha
< score
&& score
< beta
) {
293 score
= -search
<ALL
>(-beta
, -alpha
, depth
- 1, ply
+ 1);
300 if (time
.poll(nodes_count
)) return 0;
301 if (score
> best_score
) { // Found a new best move
304 if (score
>= beta
) { // Sufficient to cause a cut-off?
305 if (!move
.is_capture()) {
306 set_killer_move(depth
, move
); // Update killer moves
314 if (time
.poll(nodes_count
)) return 0;
315 if (!legal_move_found
) { // End of game?
316 if (is_in_check
) return -INF
+ ply
; // Checkmate
317 return 0; // Stalemate
321 // Store the search to Transposition Table
322 //assert(!best_move.is_null());
323 if (depth
>= trans
.get_depth() /*&& !is_null_move*/) {
324 const int value
= best_score
;
325 const Bound bound
= (best_score
>= beta
? LOWER
:
326 (best_score
<= old_alpha
? UPPER
: EXACT
));
327 if (bound
== UPPER
) best_move
= Move(); // Don't store best move
328 tt
.save(pos
.hash(), value
, bound
, depth
, best_move
);
334 Move
Game::root(int max_depth
)
336 time
.start_thinking(tree
.get_ply());
337 Color player
= current_position().get_turn_color();
338 print_thinking_header();
342 assert(max_depth
<= MAX_PLY
);
343 int it
; // Iteration of Depth
344 int best_scores
[MAX_PLY
];
345 for (it
= 1; it
< max_depth
; ++it
) { // Iterative Deepening
349 if (time
.is_out_of_time()) break; // Do not start this ply if no time
350 if (time
.get_allocated_time() - time
.get_elapsed_time() < 100) {
351 // Decrease polling interval if <1s left
352 time
.set_polling_interval(100000);
357 for (int i
= 1; i
< 4; ++i
) {
358 int val
= best_scores
[it
- i
];
359 if (-INF
+ MAX_PLY
< val
&& val
< INF
- MAX_PLY
) {
363 if (is_mate
) break; // The position was mate in the 3 previous ply
366 Moves
moves(board
, pieces
, current_position(), search_moves
);
367 moves
.add(best_move
, BEST
);
370 for (nb_moves
= 1; !(move
= moves
.next()).is_null(); ++nb_moves
) {
372 if (is_check(player
)) { // Skip illegal move
378 //NodeType node_type = (nb_moves ? PV : ALL);
379 //score = -search<node_type>(-beta, -alpha, it - 1, 1);
381 score
= -search
<PV
>(-beta
, -alpha
, it
- 1, 1);
383 score
= -search
<ALL
>(-beta
, -alpha
, it
- 1, 1);
386 if (time
.is_out_of_time()) break; // Discard this move
391 if (nodes_count
> 200000) { // Save CPU time at the beginning
392 print_thinking(it
, alpha
, best_move
);
396 if (time
.is_out_of_time()) {
397 // TODO: restore best_move and best_score from previous ply?
398 break; // Discard this ply
400 if (!best_move
.is_null()) {
401 tt
.save(current_position().hash(), alpha
, EXACT
, it
, best_move
);
403 best_scores
[it
] = best_score
;
405 // If there is only one legal move, no iterative deepening needed
406 if (nb_moves
== 1) break;
408 if (!best_move
.is_null()) {
409 print_thinking(it
, best_score
, best_move
);