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 | } |