Subversion Repositories Games.Chess Giants

Rev

Rev 33 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "chess.h"
  2. #include "data.h"
  3. /* last modified 01/10/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.  */
  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;
  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.     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)
  103.     Trace(tree, ply, value, wtm, alpha, beta, "Quiesce", serial, EVALUATION, 0);
  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;
  111.     tree->pv[ply].pathd = iteration;
  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    *
  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.                                     *
  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;
  135.     *movep += MVV_LVA[Captured(*movep)][Piece(*movep)];
  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.   }
  144.   NextSort(tree, ply);
  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   *
  152.  *  to lose material.                                       *
  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  *
  164.  *  than the captured piece, we use SEE() to determine if   *
  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++) {
  171.     tree->curmv[ply] = Move(*next);
  172.     if (pcval[Piece(tree->curmv[ply])] > pcval[Captured(tree->curmv[ply])] &&
  173.         TotalPieces(Flip(wtm), occupied)
  174.         - p_vals[Captured(tree->curmv[ply])] > 0 &&
  175.         SEE(tree, wtm, tree->curmv[ply]) < 0)
  176.       continue;
  177. #if defined(TRACE)
  178.     if (ply <= trace_level)
  179.       Trace(tree, ply, 0, wtm, alpha, beta, "Quiesce", serial, CAPTURES,
  180.           next - tree->last[ply - 1] + 1);
  181. #endif
  182.     MakeMove(tree, ply, wtm, tree->curmv[ply]);
  183.     tree->nodes_searched++;
  184.     if (!checks)
  185.       value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 0);
  186.     else if (!Check(wtm)) {
  187.       if (Check(Flip(wtm))) {
  188.         tree->qchecks_done++;
  189.         value = -QuiesceEvasions(tree, ply + 1, Flip(wtm), -beta, -alpha);
  190.       } else
  191.         value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 0);
  192.     }
  193.     UnmakeMove(tree, ply, wtm, tree->curmv[ply]);
  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  *
  210.  *  and search the ones that SEE() says are safe.  Subtle   *
  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++) {
  228.       tree->curmv[ply] = Move(*next);
  229.       if (SEE(tree, wtm, tree->curmv[ply]) >= 0) {
  230. #if defined(TRACE)
  231.         if (ply <= trace_level)
  232.           Trace(tree, ply, 0, wtm, alpha, beta, "Quiesce+checks", serial,
  233.               REMAINING, next - tree->last[ply - 1] + 1);
  234. #endif
  235.         MakeMove(tree, ply, wtm, tree->curmv[ply]);
  236.         tree->nodes_searched++;
  237.         if (!Check(wtm)) {
  238.           tree->qchecks_done++;
  239.           value = -QuiesceEvasions(tree, ply + 1, Flip(wtm), -beta, -alpha);
  240.         }
  241.         UnmakeMove(tree, ply, wtm, tree->curmv[ply]);
  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.  
  269. /* last modified 01/10/16 */
  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.  */
  286. int QuiesceEvasions(TREE * RESTRICT tree, int ply, int wtm, int alpha,
  287.     int beta) {
  288.   int original_alpha, value, moves_searched = 0, order;
  289.  
  290. /*
  291.  ************************************************************
  292.  *                                                          *
  293.  *  Initialize.                                             *
  294.  *                                                          *
  295.  ************************************************************
  296.  */
  297.   if (ply >= MAXPLY - 1)
  298.     return beta;
  299. #if defined(NODES)
  300.   if (search_nodes && --temp_search_nodes <= 0) {
  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;
  340.   tree->next_status[ply].phase = HASH;
  341.   while ((order = NextMove(tree, ply, 0, wtm, 1))) {
  342. #if defined(TRACE)
  343.     if (ply <= trace_level)
  344.       Trace(tree, ply, 0, wtm, alpha, beta, "QuiesceEvasions", serial,
  345.           tree->phase[ply], order);
  346. #endif
  347.     moves_searched++;
  348.     MakeMove(tree, ply, wtm, tree->curmv[ply]);
  349.     tree->nodes_searched++;
  350.     value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 0);
  351.     UnmakeMove(tree, ply, wtm, tree->curmv[ply]);
  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.  *                                                          *
  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().                                      *
  366.  *                                                          *
  367.  ************************************************************
  368.  */
  369.   if (moves_searched == 0) {
  370.     value = -(MATE - ply);
  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. }
  385.