Rev 33 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
33 | pmbaty | 1 | #include "chess.h" |
2 | #include "data.h" |
||
108 | pmbaty | 3 | /* last modified 01/10/16 */ |
33 | pmbaty | 4 | /* |
5 | ******************************************************************************* |
||
6 | * * |
||
7 | * Quiece() is the recursive routine used to implement the quiescence * |
||
8 | * search part of the alpha/beta negamax search. It performs the following * |
||
9 | * functions: * |
||
10 | * * |
||
11 | * (1) It computes a stand-pat score, which gives the side-on-move the * |
||
12 | * choice of standing pat and not playing any move at all and just accepting * |
||
13 | * the current static evaluation, or else it may try captures and/or * |
||
14 | * checking moves to see if it can improve the stand-pat score by making a * |
||
15 | * move that leads to some sort of positional or material gain. * |
||
16 | * * |
||
17 | * (2) The first phase is to generate all possible capture moves and then * |
||
108 | pmbaty | 18 | * sort them into descending using the value of the captured piece and the * |
19 | * complemented value of the capturing piece. This is the classic MVV/LVA * |
||
20 | * ordering approach that removes heavy pieces first in an attempt to reduce * |
||
21 | * the size of the sub-tree these pieces produce. * |
||
33 | pmbaty | 22 | * * |
23 | * (3) When we get ready to actually search each capture, we analyze each * |
||
24 | * move to see if it appears reasonable. Small pieces can capture larger * |
||
108 | pmbaty | 25 | * ones safely, ditto for equal exchanges. For the rest, we use SEE() to * |
33 | pmbaty | 26 | * compute the SEE score. If this is less than zero, we do not search this * |
27 | * move at all to avoid wasting time, since a losing capture rarely helps * |
||
28 | * improve the score in the q-search. The goal here is to find a capture * |
||
29 | * that improves on the stand-pat score and gets us closer to a position * |
||
30 | * that we would describe as "quiet" or "static". * |
||
31 | * * |
||
108 | pmbaty | 32 | * (4) If the parameter "checks" is non-zero, then after searching the * |
33 | pmbaty | 33 | * captures, we generate checking moves and search those. This value also * |
34 | * slightly changes the way captures are searched, since those that are also * |
||
35 | * checks result in calling QuiesceEvasions() which evades checks to see if * |
||
36 | * the check is actually a mate. This means that we have one layer of full- * |
||
37 | * width search to escape checks and do not allow a stand-pat which would * |
||
38 | * hide the effect of the check completely. * |
||
39 | * * |
||
40 | ******************************************************************************* |
||
41 | */ |
||
108 | pmbaty | 42 | int Quiesce(TREE * RESTRICT tree, int ply, int wtm, int alpha, int beta, |
33 | pmbaty | 43 | int checks) { |
108 | pmbaty | 44 | unsigned *next, *movep; |
33 | pmbaty | 45 | int original_alpha = alpha, value; |
46 | |||
47 | /* |
||
48 | ************************************************************ |
||
49 | * * |
||
50 | * Initialize. * |
||
51 | * * |
||
52 | ************************************************************ |
||
53 | */ |
||
54 | if (ply >= MAXPLY - 1) |
||
55 | return beta; |
||
56 | #if defined(NODES) |
||
108 | pmbaty | 57 | if (search_nodes && --temp_search_nodes <= 0) { |
33 | pmbaty | 58 | abort_search = 1; |
59 | return 0; |
||
60 | } |
||
61 | #endif |
||
62 | if (tree->thread_id == 0) |
||
63 | next_time_check--; |
||
64 | /* |
||
65 | ************************************************************ |
||
66 | * * |
||
67 | * Check for draw by repetition, which includes 50 move * |
||
68 | * draws also. This is only done at the first ply of the * |
||
69 | * quiescence search since we are following checking moves * |
||
70 | * as well. The parameter "checks" passed in is "1" for * |
||
71 | * that particular case only (when called from Search()). * |
||
72 | * all other calls (from inside Quiesce()) pass a value of * |
||
73 | * zero so that additional plies of checks are not tried. * |
||
74 | * * |
||
75 | ************************************************************ |
||
76 | */ |
||
77 | if (checks) { |
||
78 | if (Repeat(tree, ply)) { |
||
79 | value = DrawScore(wtm); |
||
80 | if (value < beta) |
||
81 | SavePV(tree, ply, 0); |
||
82 | #if defined(TRACE) |
||
83 | if (ply <= trace_level) |
||
84 | printf("draw by repetition detected, ply=%d.\n", ply); |
||
85 | #endif |
||
86 | return value; |
||
87 | } |
||
88 | } |
||
89 | /* |
||
90 | ************************************************************ |
||
91 | * * |
||
92 | * Now call Evaluate() to produce the "stand-pat" score * |
||
93 | * that will be returned if no capture is acceptable. If * |
||
94 | * this score is > alpha and < beta, then we also have to * |
||
95 | * save the path to this node as it is the PV that leads * |
||
96 | * to this score. * |
||
97 | * * |
||
98 | ************************************************************ |
||
99 | */ |
||
100 | value = Evaluate(tree, ply, wtm, alpha, beta); |
||
101 | #if defined(TRACE) |
||
102 | if (ply <= trace_level) |
||
108 | pmbaty | 103 | Trace(tree, ply, value, wtm, alpha, beta, "Quiesce", serial, EVALUATION, 0); |
33 | pmbaty | 104 | #endif |
105 | if (value > alpha) { |
||
106 | if (value >= beta) |
||
107 | return value; |
||
108 | alpha = value; |
||
109 | tree->pv[ply].pathl = ply; |
||
110 | tree->pv[ply].pathh = 0; |
||
108 | pmbaty | 111 | tree->pv[ply].pathd = iteration; |
33 | pmbaty | 112 | } |
113 | /* |
||
114 | ************************************************************ |
||
115 | * * |
||
116 | * Generate captures and sort them based on simple MVV/LVA * |
||
117 | * order. We simply try to capture the most valuable * |
||
118 | * piece possible, using the least valuable attacker * |
||
119 | * possible, to get rid of heavy pieces quickly and reduce * |
||
120 | * the overall size of the tree. * |
||
121 | * * |
||
122 | * Note that later we use the value of the capturing * |
||
123 | * piece, the value of the captured piece, and possibly * |
||
108 | pmbaty | 124 | * SEE() to exclude captures that appear to lose material, * |
125 | * but we delay expending this effort as long as possible, * |
||
126 | * since beta cutoffs make it unnecessary to search all of * |
||
127 | * these moves anyway. * |
||
33 | pmbaty | 128 | * * |
129 | ************************************************************ |
||
130 | */ |
||
131 | tree->last[ply] = GenerateCaptures(tree, ply, wtm, tree->last[ply - 1]); |
||
132 | for (movep = tree->last[ply - 1]; movep < tree->last[ply]; movep++) { |
||
133 | if (Captured(*movep) == king) |
||
134 | return beta; |
||
108 | pmbaty | 135 | *movep += MVV_LVA[Captured(*movep)][Piece(*movep)]; |
33 | pmbaty | 136 | } |
137 | if (!checks && tree->last[ply] == tree->last[ply - 1]) { |
||
138 | if (alpha != original_alpha) { |
||
139 | tree->pv[ply - 1] = tree->pv[ply]; |
||
140 | tree->pv[ply - 1].path[ply - 1] = tree->curmv[ply - 1]; |
||
141 | } |
||
142 | return value; |
||
143 | } |
||
108 | pmbaty | 144 | NextSort(tree, ply); |
33 | pmbaty | 145 | /* |
146 | ************************************************************ |
||
147 | * * |
||
148 | * Iterate through the move list and search the resulting * |
||
149 | * positions. Now that we are ready to actually search * |
||
150 | * the set of capturing moves, we try three quick tests to * |
||
151 | * see if the move should be excluded because it appears * |
||
108 | pmbaty | 152 | * to lose material. * |
33 | pmbaty | 153 | * * |
154 | * (1) If the capturing piece is not more valuable than * |
||
155 | * the captured piece, then the move can't lose material * |
||
156 | * and should be searched. * |
||
157 | * * |
||
158 | * (2) If the capture removes the last opponent piece, we * |
||
159 | * always search this kind of capture since this can be * |
||
160 | * the move the allows a passed pawn to promote when the * |
||
161 | * opponent has no piece to catch it. * |
||
162 | * * |
||
163 | * (3) Otherwise, If the capturing piece is more valuable * |
||
108 | pmbaty | 164 | * than the captured piece, we use SEE() to determine if * |
33 | pmbaty | 165 | * the capture is losing or not so that we don't search * |
166 | * hopeless moves. * |
||
167 | * * |
||
168 | ************************************************************ |
||
169 | */ |
||
170 | for (next = tree->last[ply - 1]; next < tree->last[ply]; next++) { |
||
108 | pmbaty | 171 | tree->curmv[ply] = Move(*next); |
33 | pmbaty | 172 | if (pcval[Piece(tree->curmv[ply])] > pcval[Captured(tree->curmv[ply])] && |
108 | pmbaty | 173 | TotalPieces(Flip(wtm), occupied) |
33 | pmbaty | 174 | - p_vals[Captured(tree->curmv[ply])] > 0 && |
108 | pmbaty | 175 | SEE(tree, wtm, tree->curmv[ply]) < 0) |
33 | pmbaty | 176 | continue; |
177 | #if defined(TRACE) |
||
178 | if (ply <= trace_level) |
||
108 | pmbaty | 179 | Trace(tree, ply, 0, wtm, alpha, beta, "Quiesce", serial, CAPTURES, |
180 | next - tree->last[ply - 1] + 1); |
||
33 | pmbaty | 181 | #endif |
108 | pmbaty | 182 | MakeMove(tree, ply, wtm, tree->curmv[ply]); |
33 | pmbaty | 183 | tree->nodes_searched++; |
184 | if (!checks) |
||
108 | pmbaty | 185 | value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 0); |
33 | pmbaty | 186 | else if (!Check(wtm)) { |
187 | if (Check(Flip(wtm))) { |
||
188 | tree->qchecks_done++; |
||
108 | pmbaty | 189 | value = -QuiesceEvasions(tree, ply + 1, Flip(wtm), -beta, -alpha); |
33 | pmbaty | 190 | } else |
108 | pmbaty | 191 | value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 0); |
33 | pmbaty | 192 | } |
108 | pmbaty | 193 | UnmakeMove(tree, ply, wtm, tree->curmv[ply]); |
33 | pmbaty | 194 | if (abort_search || tree->stop) |
195 | return 0; |
||
196 | if (value > alpha) { |
||
197 | if (value >= beta) |
||
198 | return value; |
||
199 | alpha = value; |
||
200 | } |
||
201 | } |
||
202 | /* |
||
203 | ************************************************************ |
||
204 | * * |
||
205 | * The next block of code is only executed if the checks * |
||
206 | * parameter is non-zero, otherwise we skip this and exit * |
||
207 | * with no further searching. * |
||
208 | * * |
||
209 | * Generate just the moves (non-captures) that give check * |
||
108 | pmbaty | 210 | * and search the ones that SEE() says are safe. Subtle * |
33 | pmbaty | 211 | * trick: we discard the captures left over from the * |
212 | * above search since we labeled them "losing moves." * |
||
213 | * * |
||
214 | ************************************************************ |
||
215 | */ |
||
216 | if (checks) { |
||
217 | tree->last[ply] = GenerateChecks(tree, wtm, tree->last[ply - 1]); |
||
218 | /* |
||
219 | ************************************************************ |
||
220 | * * |
||
221 | * Iterate through the move list and search the resulting * |
||
222 | * positions. We take them in the normal order that * |
||
223 | * GenerateChecks() provides. * |
||
224 | * * |
||
225 | ************************************************************ |
||
226 | */ |
||
227 | for (next = tree->last[ply - 1]; next < tree->last[ply]; next++) { |
||
108 | pmbaty | 228 | tree->curmv[ply] = Move(*next); |
229 | if (SEE(tree, wtm, tree->curmv[ply]) >= 0) { |
||
33 | pmbaty | 230 | #if defined(TRACE) |
231 | if (ply <= trace_level) |
||
108 | pmbaty | 232 | Trace(tree, ply, 0, wtm, alpha, beta, "Quiesce+checks", serial, |
233 | REMAINING, next - tree->last[ply - 1] + 1); |
||
33 | pmbaty | 234 | #endif |
108 | pmbaty | 235 | MakeMove(tree, ply, wtm, tree->curmv[ply]); |
33 | pmbaty | 236 | tree->nodes_searched++; |
237 | if (!Check(wtm)) { |
||
238 | tree->qchecks_done++; |
||
108 | pmbaty | 239 | value = -QuiesceEvasions(tree, ply + 1, Flip(wtm), -beta, -alpha); |
33 | pmbaty | 240 | } |
108 | pmbaty | 241 | UnmakeMove(tree, ply, wtm, tree->curmv[ply]); |
33 | pmbaty | 242 | if (abort_search || tree->stop) |
243 | return 0; |
||
244 | if (value > alpha) { |
||
245 | if (value >= beta) |
||
246 | return value; |
||
247 | alpha = value; |
||
248 | } |
||
249 | } |
||
250 | } |
||
251 | } |
||
252 | /* |
||
253 | ************************************************************ |
||
254 | * * |
||
255 | * All moves have been searched. Return the search result * |
||
256 | * that was found. If the result is not the original * |
||
257 | * alpha score, then we need to back up the PV that is * |
||
258 | * associated with this score. * |
||
259 | * * |
||
260 | ************************************************************ |
||
261 | */ |
||
262 | if (alpha != original_alpha) { |
||
263 | tree->pv[ply - 1] = tree->pv[ply]; |
||
264 | tree->pv[ply - 1].path[ply - 1] = tree->curmv[ply - 1]; |
||
265 | } |
||
266 | return alpha; |
||
267 | } |
||
268 | |||
108 | pmbaty | 269 | /* last modified 01/10/16 */ |
33 | pmbaty | 270 | /* |
271 | ******************************************************************************* |
||
272 | * * |
||
273 | * QuiesceEvasions() is the recursive routine used to implement the alpha/ * |
||
274 | * beta negamax quiescence search. The primary function here is to escape a * |
||
275 | * check that was delivered by QuiesceChecks() at the previous ply. We do * |
||
276 | * not have the usual "stand pat" option because we have to find a legal * |
||
277 | * move to prove we have not been checkmated. * |
||
278 | * * |
||
279 | * QuiesceEvasions() uses the legal move generator (GenerateCheckEvasions()) * |
||
280 | * to produce only the set of legal moves that escape check. We try those * |
||
281 | * in the the order they are generated since we are going to try them all * |
||
282 | * unless we get a fail-high. * |
||
283 | * * |
||
284 | ******************************************************************************* |
||
285 | */ |
||
108 | pmbaty | 286 | int QuiesceEvasions(TREE * RESTRICT tree, int ply, int wtm, int alpha, |
287 | int beta) { |
||
288 | int original_alpha, value, moves_searched = 0, order; |
||
33 | pmbaty | 289 | |
290 | /* |
||
291 | ************************************************************ |
||
292 | * * |
||
293 | * Initialize. * |
||
294 | * * |
||
295 | ************************************************************ |
||
296 | */ |
||
297 | if (ply >= MAXPLY - 1) |
||
298 | return beta; |
||
299 | #if defined(NODES) |
||
108 | pmbaty | 300 | if (search_nodes && --temp_search_nodes <= 0) { |
33 | pmbaty | 301 | abort_search = 1; |
302 | return 0; |
||
303 | } |
||
304 | if (tree->thread_id == 0) |
||
305 | next_time_check--; |
||
306 | #endif |
||
307 | /* |
||
308 | ************************************************************ |
||
309 | * * |
||
310 | * Check for draw by repetition, which includes 50 move * |
||
311 | * draws also. * |
||
312 | * * |
||
313 | ************************************************************ |
||
314 | */ |
||
315 | if (Repeat(tree, ply)) { |
||
316 | value = DrawScore(wtm); |
||
317 | if (value < beta) |
||
318 | SavePV(tree, ply, 0); |
||
319 | #if defined(TRACE) |
||
320 | if (ply <= trace_level) |
||
321 | printf("draw by repetition detected, ply=%d.\n", ply); |
||
322 | #endif |
||
323 | return value; |
||
324 | } |
||
325 | original_alpha = alpha; |
||
326 | /* |
||
327 | ************************************************************ |
||
328 | * * |
||
329 | * Iterate through the move list and search the resulting * |
||
330 | * positions. These moves are searched in the order that * |
||
331 | * GenerateEvasions() produces them. No hash move is * |
||
332 | * possible since we don't do probes in Quiesce(). We do * |
||
333 | * clear the hash move before we start selecting moves so * |
||
334 | * that we don't search a bogus move from a different * |
||
335 | * position. * |
||
336 | * * |
||
337 | ************************************************************ |
||
338 | */ |
||
339 | tree->hash_move[ply] = 0; |
||
108 | pmbaty | 340 | tree->next_status[ply].phase = HASH; |
341 | while ((order = NextMove(tree, ply, 0, wtm, 1))) { |
||
33 | pmbaty | 342 | #if defined(TRACE) |
343 | if (ply <= trace_level) |
||
108 | pmbaty | 344 | Trace(tree, ply, 0, wtm, alpha, beta, "QuiesceEvasions", serial, |
345 | tree->phase[ply], order); |
||
33 | pmbaty | 346 | #endif |
347 | moves_searched++; |
||
108 | pmbaty | 348 | MakeMove(tree, ply, wtm, tree->curmv[ply]); |
33 | pmbaty | 349 | tree->nodes_searched++; |
108 | pmbaty | 350 | value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 0); |
351 | UnmakeMove(tree, ply, wtm, tree->curmv[ply]); |
||
33 | pmbaty | 352 | if (abort_search || tree->stop) |
353 | return 0; |
||
354 | if (value > alpha) { |
||
355 | if (value >= beta) |
||
356 | return value; |
||
357 | alpha = value; |
||
358 | } |
||
359 | } |
||
360 | /* |
||
361 | ************************************************************ |
||
362 | * * |
||
108 | pmbaty | 363 | * All moves have been searched. If none were legal, it * |
364 | * must be a mate since we have to be in check to reach * |
||
365 | * QuiesceEvasions(). * |
||
33 | pmbaty | 366 | * * |
367 | ************************************************************ |
||
368 | */ |
||
369 | if (moves_searched == 0) { |
||
108 | pmbaty | 370 | value = -(MATE - ply); |
33 | pmbaty | 371 | if (value >= alpha && value < beta) { |
372 | SavePV(tree, ply, 0); |
||
373 | #if defined(TRACE) |
||
374 | if (ply <= trace_level) |
||
375 | printf("Search() no moves! ply=%d\n", ply); |
||
376 | #endif |
||
377 | } |
||
378 | return value; |
||
379 | } else if (alpha != original_alpha) { |
||
380 | tree->pv[ply - 1] = tree->pv[ply]; |
||
381 | tree->pv[ply - 1].path[ply - 1] = tree->curmv[ply - 1]; |
||
382 | } |
||
383 | return alpha; |
||
384 | } |