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