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