Subversion Repositories Games.Chess Giants

Rev

Rev 108 | 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 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. }
  419.