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.  *   Search() is the recursive routine used to implement the alpha/beta        *
  8.  *   negamax search (similar to minimax but simpler to code.)  Search() is     *
  9.  *   called whenever there is "depth" remaining so that all moves are subject  *
  10.  *   to searching.  Search() recursively calls itself so long as there is at   *
  11.  *   least one ply of depth left, otherwise it calls Quiesce() instead.        *
  12.  *                                                                             *
  13.  *******************************************************************************
  14.  */
  15. int Search(TREE * RESTRICT tree, int ply, int depth, int wtm, int alpha,
  16.     int beta, int in_check, int do_null) {
  17.   int repeat = 0, value = 0, pv_node = alpha != beta - 1, n_depth;
  18.   int searched[256];
  19.  
  20. /*
  21.  ************************************************************
  22.  *                                                          *
  23.  *  Timeout.  Check to see if we have searched enough nodes *
  24.  *  that it is time to peek at how much time has been used, *
  25.  *  or if is time to check for operator keyboard input.     *
  26.  *  This is usually enough nodes to force a time/input      *
  27.  *  check about once per second, except when the target     *
  28.  *  time per move is very small, in which case we try to    *
  29.  *  check the time more frequently.                         *
  30.  *                                                          *
  31.  *  Note that we check input or time-out in thread 0.  This *
  32.  *  makes the code simpler and eliminates some problematic  *
  33.  *  race conditions.                                        *
  34.  *                                                          *
  35.  ************************************************************
  36.  */
  37. #if defined(NODES)
  38.   if (search_nodes && --temp_search_nodes <= 0) {
  39.     abort_search = 1;
  40.     return 0;
  41.   }
  42. #endif
  43.   if (tree->thread_id == 0) {
  44.     if (--next_time_check <= 0) {
  45.       next_time_check = nodes_between_time_checks;
  46.       if (TimeCheck(tree, 1)) {
  47.         abort_search = 1;
  48.         return 0;
  49.       }
  50.       if (CheckInput()) {
  51.         Interrupt(ply);
  52.         if (abort_search)
  53.           return 0;
  54.       }
  55.     }
  56.   }
  57.   if (ply >= MAXPLY - 1)
  58.     return beta;
  59. /*
  60.  ************************************************************
  61.  *                                                          *
  62.  *  Draws.  Check for draw by repetition, which includes    *
  63.  *  50 move draws also.  This is the quickest way to get    *
  64.  *  out of further searching, with minimal effort.  This    *
  65.  *  and the next four steps are skipped for moves at the    *
  66.  *  root (ply = 1).                                         *
  67.  *                                                          *
  68.  ************************************************************
  69.  */
  70.   if (ply > 1) {
  71.     if ((repeat = Repeat(tree, ply))) {
  72.       if (repeat == 1 || !in_check) {
  73.         value = DrawScore(wtm);
  74.         if (value < beta)
  75.           SavePV(tree, ply, 3);
  76. #if defined(TRACE)
  77.         if (ply <= trace_level)
  78.           printf("draw by repetition detected, ply=%d.\n", ply);
  79. #endif
  80.         return value;
  81.       }
  82.     }
  83. /*
  84.  ************************************************************
  85.  *                                                          *
  86.  *  Mate distance pruning.  If we have found a mate, we can *
  87.  *  stop if we are too deep to find a shorter mate.  This   *
  88.  *  only affects the size of the tree in positions where    *
  89.  *  there are forced mates.  It does not change the outcome *
  90.  *  of the search at all, just the time it takes.           *
  91.  *                                                          *
  92.  ************************************************************
  93.  */
  94.     alpha = Max(alpha, -MATE + ply - 1);
  95.     beta = Min(beta, MATE - ply);
  96.     if (alpha >= beta)
  97.       return alpha;
  98. /*
  99.  ************************************************************
  100.  *                                                          *
  101.  *  Trans/Ref.  Check the transposition/refutation (hash)   *
  102.  *  table to see if we have searched this position          *
  103.  *  previously and still have the results available.  We    *
  104.  *  might get a real score, or a bound, or perhaps only a   *
  105.  *  good move to try first.  The possible results are:      *
  106.  *                                                          *
  107.  *  1. HashProbe() returns "HASH_HIT".  This terminates the *
  108.  *  search instantly and we simply return the value found   *
  109.  *  in the hash table.  This value is simply the value we   *
  110.  *  found when we did a real search in this position        *
  111.  *  previously, and ProbeTransRef() verifies that the value *
  112.  *  is useful based on draft and current bounds.            *
  113.  *                                                          *
  114.  *  2. HashProbe() returns "AVOID_NULL_MOVE" which means    *
  115.  *  the hashed score/bound was no good, but it indicated    *
  116.  *  that trying a null-move in this position would be a     *
  117.  *  waste of time since it will likely fail low, not high.  *
  118.  *                                                          *
  119.  *  3. HashProbe() returns "HASH_MISS" when forces us to do *
  120.  *  a normal search to resolve this node.                   *
  121.  *                                                          *
  122.  ************************************************************
  123.  */
  124.     switch (HashProbe(tree, ply, depth, wtm, alpha, beta, &value)) {
  125.       case HASH_HIT:
  126.         return value;
  127.       case AVOID_NULL_MOVE:
  128.         do_null = 0;
  129.       case HASH_MISS:
  130.         break;
  131.     }
  132. /*
  133.  ************************************************************
  134.  *                                                          *
  135.  *  EGTBs.  Now it's time to try a probe into the endgame   *
  136.  *  tablebase files.  This is done if we notice that there  *
  137.  *  are 6 or fewer pieces left on the board AND the move at *
  138.  *  the previous ply was a capture.  If it was not, then we *
  139.  *  would have already probed the EGTBs so if it was a miss *
  140.  *  when we probed then, it will also miss here.  EGTB_use  *
  141.  *  tells us how many pieces to probe on.  Note that this   *
  142.  *  can be zero when trying to swindle the opponent, so     *
  143.  *  that no probes are done since we know it is a draw.     *
  144.  *  This is another way to get out of the search quickly,   *
  145.  *  but not as quickly as the previous steps since this can *
  146.  *  result in an I/O operation.                             *
  147.  *                                                          *
  148.  *  Note that in "swindle mode" this can be turned off by   *
  149.  *  Iterate() setting "EGTB_use = 0" so that we won't probe *
  150.  *  the EGTBs since we are searching only the root moves    *
  151.  *  that lead to a draw and we want to play the move that   *
  152.  *  makes the draw more difficult to reach by the opponent  *
  153.  *  to give him a chance to make a mistake.                 *
  154.  *                                                          *
  155.  *  Another special case is that we slightly fudge the      *
  156.  *  score for draws.  In a normal circumstance, draw=0.00   *
  157.  *  since it is "equal".  However, here we add 0.01 if      *
  158.  *  white has more material, or subtract 0.01 if black has  *
  159.  *  more material, since in a drawn KRP vs KR we would      *
  160.  *  prefer to have the KRP side since the opponent can make *
  161.  *  a mistake and convert the draw to a loss.               *
  162.  *                                                          *
  163.  ************************************************************
  164.  */
  165. #if !defined(NOEGTB)
  166.     if (depth > EGTB_depth && TotalAllPieces <= EGTB_use &&
  167.         !Castle(ply, white) && !Castle(ply, black) &&
  168.         (Captured(tree->curmv[ply - 1]) || ply < 3)) {
  169.       int egtb_value;
  170.  
  171.       tree->egtb_probes++;
  172.       if (EGTBProbe(tree, ply, wtm, &egtb_value)) {
  173.         tree->egtb_hits++;
  174.         alpha = egtb_value;
  175.         if (MateScore(alpha))
  176.           alpha += (alpha > 0) ? -ply + 1 : ply;
  177.         else if (alpha == 0) {
  178.           alpha = DrawScore(wtm);
  179.           if (MaterialSTM(wtm) > 0)
  180.             alpha += 1;
  181.           else if (MaterialSTM(wtm) < 0)
  182.             alpha -= 1;
  183.         }
  184.         if (alpha < beta)
  185.           SavePV(tree, ply, 2);
  186.         return alpha;
  187.       }
  188.     }
  189. #endif
  190. /*
  191.  ************************************************************
  192.  *                                                          *
  193.  *  Null-move.  We now know there is no quick way to get    *
  194.  *  out of here, which leaves one more possibility,         *
  195.  *  although it does require a search, but to a reduced     *
  196.  *  depth.  We try a null move to see if we can get a quick *
  197.  *  cutoff with only a little work.  This operates as       *
  198.  *  follows.  Instead of making a legal move, the side on   *
  199.  *  move passes and does nothing.  The resulting position   *
  200.  *  is searched to a shallower depth than normal (see       *
  201.  *  below).  This will result in a cutoff if our position   *
  202.  *  is very good, but it produces the cutoff much quicker   *
  203.  *  since the search is far shallower than a normal search  *
  204.  *  that would also be likely to fail high.                 *
  205.  *                                                          *
  206.  *  The reduction amount starts off at null_depth (normally *
  207.  *  set to 3 but the user can change this via the crafty    *
  208.  *  personality command) but is then increased as follows:  *
  209.  *                                                          *
  210.  *     R = null_depth + depth / null_divisor                *
  211.  *                                                          *
  212.  *  null_divisor defaults to 6, but this can also be set    *
  213.  *  by the user to try more/less aggressive settings.       *
  214.  *                                                          *
  215.  *  This is skipped for any of the following reasons:       *
  216.  *                                                          *
  217.  *  1. The side on move is in check.  The null move         *
  218.  *     results in an illegal position.                      *
  219.  *  2. No more than one null move can appear in succession  *
  220.  *     as all this does is burn 2x plies of depth.          *
  221.  *  3. The side on move has only pawns left, which makes    *
  222.  *     zugzwang positions more likely.                      *
  223.  *  4. The transposition table probe found an entry that    *
  224.  *     indicates that a null-move search will not fail      *
  225.  *     high, so we avoid the wasted effort.                 *
  226.  *                                                          *
  227.  ************************************************************
  228.  */
  229.  
  230.     tree->last[ply] = tree->last[ply - 1];
  231.     n_depth = (TotalPieces(wtm, occupied) > 9 || n_root_moves > 17 ||
  232.             depth > 3) ? 1 : 3;
  233.     if (do_null && !pv_node && depth > n_depth && !in_check &&
  234.         TotalPieces(wtm, occupied)) {
  235.       uint64_t save_hash_key;
  236.       int R = null_depth + depth / null_divisor;
  237.  
  238.       tree->curmv[ply] = 0;
  239. #if defined(TRACE)
  240.       if (ply <= trace_level)
  241.         Trace(tree, ply, depth, wtm, value - 1, value, "SearchNull", serial,
  242.             NULL_MOVE, 0);
  243. #endif
  244.       tree->status[ply + 1] = tree->status[ply];
  245.       Reversible(ply + 1) = 0;
  246.       save_hash_key = HashKey;
  247.       if (EnPassant(ply + 1)) {
  248.         HashEP(EnPassant(ply + 1));
  249.         EnPassant(ply + 1) = 0;
  250.       }
  251.       tree->null_done[Min(R, 15)]++;
  252.       if (depth - R - 1 > 0)
  253.         value =
  254.             -Search(tree, ply + 1, depth - R - 1, Flip(wtm), -beta, -beta + 1,
  255.             0, NO_NULL);
  256.       else
  257.         value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -beta + 1, 1);
  258.       HashKey = save_hash_key;
  259.       if (abort_search || tree->stop)
  260.         return 0;
  261.       if (value >= beta) {
  262.         HashStore(tree, ply, depth, wtm, LOWER, value, tree->hash_move[ply]);
  263.         return value;
  264.       }
  265.     }
  266. /*
  267.  ************************************************************
  268.  *                                                          *
  269.  *  IID.  This step is rarely executed.  It is used when    *
  270.  *  there is no best move from the hash table, and this is  *
  271.  *  a PV node, since we need a good move to search first.   *
  272.  *  While killers moves are good, they are not quite good   *
  273.  *  enough.  the simplest solution is to try a shallow      *
  274.  *  search (depth-2) to get a move.  note that when we call *
  275.  *  Search() with depth-2, it, too, will not have a hash    *
  276.  *  move, and will therefore recursively continue this      *
  277.  *  process, hence the name "internal iterative deepening." *
  278.  *                                                          *
  279.  ************************************************************
  280.  */
  281.     tree->next_status[ply].phase = HASH;
  282.     if (!tree->hash_move[ply] && depth >= 6 && do_null && ply > 1 && pv_node) {
  283.       tree->curmv[ply] = 0;
  284.       if (depth - 2 > 0)
  285.         value =
  286.             Search(tree, ply, depth - 2, wtm, alpha, beta, in_check, DO_NULL);
  287.       else
  288.         value = Quiesce(tree, ply, wtm, alpha, beta, 1);
  289.       if (abort_search || tree->stop)
  290.         return 0;
  291.       if (value > alpha) {
  292.         if (value < beta) {
  293.           if ((int) tree->pv[ply - 1].pathl > ply)
  294.             tree->hash_move[ply] = tree->pv[ply - 1].path[ply];
  295.         } else
  296.           tree->hash_move[ply] = tree->curmv[ply];
  297.         tree->last[ply] = tree->last[ply - 1];
  298.         tree->next_status[ply].phase = HASH;
  299.       }
  300.     }
  301.   }
  302. /*
  303.  ************************************************************
  304.  *                                                          *
  305.  *  Search moves.  Now we call SearchMoveList() to interate *
  306.  *  through the move list and search each new position.     *
  307.  *  Note that this is done in a separate procedure because  *
  308.  *  this is also the code that is used to do the parallel   *
  309.  *  search.                                                 *
  310.  *                                                          *
  311.  ************************************************************
  312.  */
  313.   searched[0] = 0;
  314.   value =
  315.       SearchMoveList(tree, ply, depth, wtm, alpha, beta, searched, in_check,
  316.       repeat, serial);
  317.   return value;
  318. }
  319.  
  320. /* last modified 09/28/15 */
  321. /*
  322.  *******************************************************************************
  323.  *                                                                             *
  324.  *   SearchMoveList() is the recursive routine used to implement the main      *
  325.  *   search loop.  This code is responsible for iterating through the move     *
  326.  *   list until it encounters a condition that ends the search at this ply.    *
  327.  *   At that point it simply returns the current negamax value to the caller   *
  328.  *   to handle as necessary.                                                   *
  329.  *                                                                             *
  330.  *   The "mode" flag indicates which of the following conditions apply here    *
  331.  *   which directly controls parts of the search.                              *
  332.  *                                                                             *
  333.  *      mode = serial   ->  this is a serial search.                           *
  334.  *                                                                             *
  335.  *      mode = parallel ->  this is a parallel search, which implies that this *
  336.  *                          is a partial search which means we do NOT want to  *
  337.  *                          do any trans/ref updating and we also need to take *
  338.  *                          care about locking things that are being updated   *
  339.  *                          by more than one thread in parallel.               *
  340.  *                                                                             *
  341.  *   When mode = parallel, this code performs the same function as the old     *
  342.  *   SearchParallel() code, except that it is the main search loop for the     *
  343.  *   program, there is no longer any duplicated code.  This is called by the   *
  344.  *   normal Search() function and by ThreadWait() where idle processes wait    *
  345.  *   for work and then call this procedure to search a subset of the moves at  *
  346.  *   this ply (in parallel with other threads).                                *
  347.  *                                                                             *
  348.  *******************************************************************************
  349.  */
  350. int SearchMoveList(TREE * RESTRICT tree, int ply, int depth, int wtm,
  351.     int alpha, int beta, int searched[], int in_check, int repeat, int mode) {
  352.   TREE *current;
  353.   int extend, reduce, check, original_alpha = alpha, t_beta;
  354.   int i, value = 0, pv_node = alpha != beta - 1, status, order;
  355.   int moves_done = 0, phase, bestmove, type;
  356.  
  357. /*
  358.  ************************************************************
  359.  *                                                          *
  360.  *  Basic initialization before we begin the loop through   *
  361.  *  the move list.  If this is a parallel search, we have   *
  362.  *  already searched one move, so we set t_beta to alpha+1  *
  363.  *  to set up for a normal PVS search (for moves 2-n)       *
  364.  *  instead of using alpha,beta for the first move as we do *
  365.  *  in a normal search.  Also, if this is a serial search,  *
  366.  *  we are fixing to search the first move so we set the    *
  367.  *  searched move counter to zero, where in a parallel      *
  368.  *  search this has already been done and we leave it alone *
  369.  *  here.                                                   *
  370.  *                                                          *
  371.  *  We also set <current> to tree for a serial search, and  *
  372.  *  to tree->parent for a parallel search since we need to  *
  373.  *  share the move list at split nodes.                     *
  374.  *                                                          *
  375.  ************************************************************
  376.  */
  377.   tree->next_status[ply].phase = HASH;
  378.   if (mode == parallel) {
  379.     current = tree->parent;
  380.     t_beta = alpha + 1;
  381.   } else {
  382.     current = tree;
  383.     t_beta = beta;
  384.   }
  385. /*
  386.  ************************************************************
  387.  *                                                          *
  388.  *  Iterate.  Now iterate through the move list and search  *
  389.  *  the resulting positions.  Note that Search() culls any  *
  390.  *  move that is not legal by using Check().  The special   *
  391.  *  case is that we must find one legal move to search to   *
  392.  *  confirm that it's not a mate or draw.                   *
  393.  *                                                          *
  394.  *  We call NextMove() which will generate moves in the     *
  395.  *  normal way (captures, killers, etc) or it will use the  *
  396.  *  GenerateEvasions() generator if we are in check.  For   *
  397.  *  the special case of ply=1, we use NextRootMove() since  *
  398.  *  the ply=1 move list has been generated and the order is *
  399.  *  updated as each search iteration is executed.           *
  400.  *                                                          *
  401.  ************************************************************
  402.  */
  403.   while (1) {
  404.     if (ply == 1 && moves_done == 1 && alpha == original_alpha &&
  405.         mode == serial)
  406.       break;
  407.     if (mode == parallel)
  408.       Lock(current->lock);
  409.     order = (ply > 1) ? NextMove(current, ply, depth, wtm, in_check) :
  410.       NextRootMove(current, tree, wtm);
  411.     phase = current->phase[ply];
  412.     if (mode == parallel) {
  413.       tree->curmv[ply] = tree->parent->curmv[ply];
  414.       Unlock(current->lock);
  415.     }
  416.     if (!order)
  417.       break;
  418. #if defined(TRACE)
  419.     if (ply <= trace_level)
  420.       Trace(tree, ply, depth, wtm, alpha, beta, "SearchMoveList", mode, phase,
  421.           order);
  422. #endif
  423.     MakeMove(tree, ply, wtm, tree->curmv[ply]);
  424.     tree->nodes_searched++;
  425.     status = ILLEGAL;
  426.     if (in_check || !Check(wtm))
  427.       do {
  428.         searched[0]++;
  429.         moves_done++;
  430.         status = LEGAL;
  431.         searched[searched[0]] = tree->curmv[ply];
  432. /*
  433.  ************************************************************
  434.  *                                                          *
  435.  *  Check.  If the move to be made checks the opponent,     *
  436.  *  then we need to remember that he's in check and also    *
  437.  *  extend the depth by one ply for him to get out.         *
  438.  *                                                          *
  439.  *  We do not extend unsafe checking moves (as indicated by *
  440.  *  the SEE algorithm), since these are usually a waste of  *
  441.  *  time and simply blow up the tree search space.          *
  442.  *                                                          *
  443.  *  Note that extending here disables any potential foward  *
  444.  *  pruning or reductions for this move.                    *
  445.  *                                                          *
  446.  ************************************************************
  447.  */
  448.         extend = 0;
  449.         reduce = 0;
  450.         if (Check(Flip(wtm))) {
  451.           check = 1;
  452.           if (SEEO(tree, wtm,
  453.                   tree->curmv[ply]) - pcval[Captured(tree->curmv[ply])] <=
  454.               0) {
  455.             extend = check_depth;
  456.             tree->extensions_done++;
  457.           }
  458.         } else
  459.           check = 0;
  460. /*
  461.  ************************************************************
  462.  *                                                          *
  463.  *  Futility.  First attempt at forward pruning based on    *
  464.  *  the futility idea.                                      *
  465.  *                                                          *
  466.  *  We have an array of pruning margin values that are      *
  467.  *  indexed by depth (remaining plies left until we drop    *
  468.  *  into the quiescence search) and which increase with     *
  469.  *  depth since more depth means a greater chance of        *
  470.  *  bringing the score back up to alpha or beyond.  If the  *
  471.  *  current material + the bonus is less than alpha, we     *
  472.  *  simply avoid searching this move at all, and skip to    *
  473.  *  the next move without expending any more effort.  Note  *
  474.  *  that this is classic forward-pruning and can certainly  *
  475.  *  introduce errors into the search.  However, cluster     *
  476.  *  testing has shown that this improves play in real       *
  477.  *  games.  The current implementation only prunes in the   *
  478.  *  last 6 plies before quiescence, although this can be    *
  479.  *  tuned with the "eval" command changing the "pruning     *
  480.  *  depth" value to something other than 7 (test is for     *
  481.  *  depth < pruning depth, current value is 7 which prunes  *
  482.  *  in last 6 plies only).  Testing shows no benefit in     *
  483.  *  larger values than 7, although this might change in     *
  484.  *  future versions as other things are modified.           *
  485.  *                                                          *
  486.  *  Exception:                                              *
  487.  *                                                          *
  488.  *    We do not prune if we are safely pushing a passed     *
  489.  *    pawn to the 6th rank, where it becomes very dangerous *
  490.  *    since it can promote in two more moves.               *
  491.  *                                                          *
  492.  *  All pruning and reduction code is skipped if any of the *
  493.  *  following are true:                                     *
  494.  *                                                          *
  495.  *  (1) side on move is in check.                           *
  496.  *                                                          *
  497.  *  (2) move has not already been flagged for a search      *
  498.  *      extension.                                          *
  499.  *                                                          *
  500.  *  (3) this is not the first move at this ply.             *
  501.  *                                                          *
  502.  *  (4) we are in the REMAINING phase, which means that a   *
  503.  *      cutoff is not very likely.                          *
  504.  *                                                          *
  505.  ************************************************************
  506.  */
  507.         if (!in_check && !extend && order > 1 && phase >= HISTORY &&
  508.             !(PawnPush(wtm, tree->curmv[ply]))) {
  509.           if (!pv_node && depth < pruning_depth &&
  510.               MaterialSTM(wtm) + pruning_margin[depth] <= alpha) {
  511.             tree->moves_fpruned++;
  512.             break;
  513.           }
  514. /*
  515.  ************************************************************
  516.  *                                                          *
  517.  *  LMP.  Final attempt at forward pruning based on the     *
  518.  *  "late move pruning" idea (a take-off on LMR).           *
  519.  *                                                          *
  520.  *  The basic idea here is that once we have searched a     *
  521.  *  significant number of moves at a ply, it becomes less   *
  522.  *  and less likely that any of the moves left will produce *
  523.  *  a cutoff.  If the move appears to be simple (not a      *
  524.  *  check, etc) then we simply skip it, once the move count *
  525.  *  has been satisfied.  At present, we only do this in the *
  526.  *  last two plies although this might be changed in the    *
  527.  *  future.                                                 *
  528.  *                                                          *
  529.  ************************************************************
  530.  */
  531.           if (!pv_node && alpha > -MATE + 300 && depth < movecnt_depth &&
  532.               !CaptureOrPromote(tree->curmv[ply]) &&
  533.               order > movecnt_pruning[depth]) {
  534.             tree->moves_mpruned++;
  535.             break;
  536.           }
  537. /*
  538.  ************************************************************
  539.  *                                                          *
  540.  *  LMR.  Now it's time to try to reduce the search depth   *
  541.  *  if the move appears to be "poor" because it appears     *
  542.  *  later in the move list.                                 *
  543.  *                                                          *
  544.  *  The reduction is variable and is done via a table look- *
  545.  *  up that uses a function based on remaining depth (more  *
  546.  *  depth remaining, the larger the reduction) and the      *
  547.  *  number of moves searched (more moves searched, the      *
  548.  *  larger the reduction).  The "shape" of this reduction   *
  549.  *  formula is user-settable via the "lmr" command.         *
  550.  *                                                          *
  551.  ************************************************************
  552.  */
  553.           reduce = LMR[Min(depth, 31)][Min(order, 63)];
  554.           tree->LMR_done[reduce]++;
  555.         }
  556. /*
  557.  ************************************************************
  558.  *                                                          *
  559.  *  Now do the PVS search on the current move.              *
  560.  *                                                          *
  561.  *  Note that we do the usual alpha/beta cutoff tests here  *
  562.  *  but we only set an indicator that is used after we have *
  563.  *  called Unmake().  This cleaned up the exit from search  *
  564.  *  and makes it easier to understand when there is only    *
  565.  *  one point where this is done, without needing multiple  *
  566.  *  Unmake() calls when there are different exit points.    *
  567.  *                                                          *
  568.  ************************************************************
  569.  */
  570.         value =
  571.             SearchMove(tree, ply, depth, wtm, alpha, t_beta, beta, extend,
  572.             reduce, check);
  573.         if (value > alpha) {
  574.           status = IN_WINDOW;
  575.           if (value >= beta)
  576.             status = FAIL_HIGH;
  577.           if (mode == parallel && ply == 1)
  578.             status = FAIL_HIGH;
  579.         }
  580.       } while (0);
  581.     UnmakeMove(tree, ply, wtm, tree->curmv[ply]);
  582.     if (abort_search || tree->stop)
  583.       break;
  584. /*
  585.  ************************************************************
  586.  *                                                          *
  587.  *  Test 1.  When we get here, we have made a move,         *
  588.  *  searched it (and re-searched if necessary/appropriate), *
  589.  *  and the move has been unmade so that the board is in a  *
  590.  *  correct state.                                          *
  591.  *                                                          *
  592.  *  If status = FAIL_HIGH, the search failed high.  The     *
  593.  *  first thing to handle is the case where we are at       *
  594.  *  ply=1, which is a special case.  If we are going to     *
  595.  *  fail high here and terminate the search immediately, we *
  596.  *  need to build the fail-high PV to back up to Iterate()  *
  597.  *  so it will produce the correct output and widen the     *
  598.  *  alpha/beta window.                                      *
  599.  *                                                          *
  600.  *  We then check to see if this is a parallel search.  If  *
  601.  *  so then we are done here, but we need to tell all of    *
  602.  *  the siblings that are helping at this split point that  *
  603.  *  they should immediately stop searching here since we    *
  604.  *  don't need their results.                               *
  605.  *                                                          *
  606.  *  Otherwise we update the killer moves and history        *
  607.  *  counters and store the fail-high information in the     *
  608.  *  trans/ref table for future use if we happen to reach    *
  609.  *  this position again.                                    *
  610.  *                                                          *
  611.  ************************************************************
  612.  */
  613.     if (status == FAIL_HIGH) {
  614.       if (ply == 1) {
  615.         if (!tree->stop) {
  616.           tree->pv[1].path[1] = tree->curmv[1];
  617.           tree->pv[1].pathl = 2;
  618.           tree->pv[1].pathh = 0;
  619.           tree->pv[1].pathd = iteration;
  620.           tree->pv[0] = tree->pv[1];
  621.         }
  622.       }
  623. #if (CPUS > 1)
  624.       if (mode == parallel) {
  625.         Lock(lock_smp);
  626.         Lock(tree->parent->lock);
  627.         if (!tree->stop) {
  628.           int proc;
  629.  
  630.           parallel_aborts++;
  631.           for (proc = 0; proc < (int) smp_max_threads; proc++) // Pierre-Marie Baty -- added type cast
  632.             if (tree->parent->siblings[proc] && proc != tree->thread_id)
  633.               ThreadStop(tree->parent->siblings[proc]);
  634.         }
  635.         Unlock(tree->parent->lock);
  636.         Unlock(lock_smp);
  637.         return value;
  638.       }
  639. #endif
  640.       tree->fail_highs++;
  641.       if (order == 1)
  642.         tree->fail_high_first_move++;
  643.       HashStore(tree, ply, depth, wtm, LOWER, value, tree->curmv[ply]);
  644.       History(tree, ply, depth, wtm, tree->curmv[ply], searched);
  645.       return beta;
  646. /*
  647.  ************************************************************
  648.  *                                                          *
  649.  *  Test 2.  If status = IN_WINDOW, this is a search that   *
  650.  *  improved alpha without failing high.  We simply update  *
  651.  *  alpha and continue searching moves.                     *
  652.  *                                                          *
  653.  *  Special case:  If ply = 1 in a normal search, we have   *
  654.  *  a best move and score that just changed.  We need to    *
  655.  *  update the root move list by adding the PV and the      *
  656.  *  score, and then we look to make sure this new "best     *
  657.  *  move" is not actually worse than the best we have found *
  658.  *  so far this iteration.  If it is worse, we restore the  *
  659.  *  best move and score from the real best move so our      *
  660.  *  search window won't be out of whack, which would let    *
  661.  *  moves with scores in between this bad move and the best *
  662.  *  move fail high, cause re-searches, and waste time.      *
  663.  *                                                          *
  664.  *  If this is ply = 1, we display the PV to keep the user  *
  665.  *  informed.                                               *
  666.  *                                                          *
  667.  ************************************************************
  668.  */
  669.     } else if (status == IN_WINDOW) {
  670.       alpha = value;
  671.       if (ply == 1 && mode == serial) {
  672.         tree->pv[1].pathv = value;
  673.         tree->pv[0] = tree->pv[1];
  674.         for (i = 0; i < n_root_moves; i++)
  675.           if (root_moves[i].move == tree->pv[1].path[1]) {
  676.             root_moves[i].path = tree->pv[1];
  677.             root_moves[i].path.pathv = alpha;
  678.           }
  679.         for (i = 0; i < n_root_moves; i++)
  680.           if (value < root_moves[i].path.pathv) {
  681.             value = root_moves[i].path.pathv;
  682.             alpha = value;
  683.             tree->pv[0] = root_moves[i].path;
  684.             tree->pv[1] = tree->pv[0];
  685.           }
  686.         Output(tree);
  687.         failhi_delta = 16;
  688.         faillo_delta = 16;
  689.       }
  690.     }
  691. /*
  692.  ************************************************************
  693.  *                                                          *
  694.  *  Test 3.  If status = ILLEGAL, this search was given an  *
  695.  *  illegal move and no search was done, we skip any        *
  696.  *  updating and simply select the next move to search.     *
  697.  *                                                          *
  698.  ************************************************************
  699.  */
  700.     else if (status == ILLEGAL)
  701.       continue;
  702.     t_beta = alpha + 1;
  703. /*
  704.  ************************************************************
  705.  *                                                          *
  706.  *  SMP.  If are doing an SMP search, and we have idle      *
  707.  *  processors, now is the time to get them involved.  We   *
  708.  *  have now satisfied the "young brothers wait" condition  *
  709.  *  since we have searched at least one move.  All that is  *
  710.  *  left is to check the split constraints to see if this   *
  711.  *  is an acceptable split point.                           *
  712.  *                                                          *
  713.  *    (1) We can't split within N plies of the frontier     *
  714.  *        nodes to avoid excessive split overhead.          *
  715.  *                                                          *
  716.  *    (2) We can't split until at least N nodes have been   *
  717.  *        searched since this thread was last split, to     *
  718.  *        avoid splitting too often, mainly in endgames.    *
  719.  *                                                          *
  720.  *    (3) We have to have searched one legal move to avoid  *
  721.  *        splitting at a node where we have no legal moves  *
  722.  *        (the first move tried might have been illegal as  *
  723.  *        in when we encounter a stalemate).                *
  724.  *                                                          *
  725.  *    (4) If we are at ply=1, we can't split unless the     *
  726.  *        smp_split_at_root flag is set to 1, AND the next  *
  727.  *        move in the ply=1 move list is not flagged as     *
  728.  *        "do not search in parallel" which happens when    *
  729.  *        this move was a best move in the last couple of   *
  730.  *        searches and we want all processors on it at once *
  731.  *        to get a score back quicker.                      *
  732.  *                                                          *
  733.  *    (5) if the variable smp_split is != 0, we have idle   *
  734.  *        threads that can help, which means we want to get *
  735.  *        them involved quickly, OR if this node is an      *
  736.  *        acceptable "gratuitous-split" point by being far  *
  737.  *        enough from the tips of the tree to avoid         *
  738.  *        excessive overhead.                               *
  739.  *                                                          *
  740.  *  We use this code recursively to perform a parallel      *
  741.  *  search at this ply.  But when we finish a partial piece *
  742.  *  of the search in parallel, we don't need to update any  *
  743.  *  search data structures, we will defer that until all of *
  744.  *  parallel threads complete and return back into this     *
  745.  *  code after the parallel search has been collapsed back  *
  746.  *  to one instance of search at this ply.                  *
  747.  *                                                          *
  748.  *  Special case:  we do not split if we are at ply=1 and   *
  749.  *  alpha == original_alpha.  That means the first move     *
  750.  *  failed low, and we are going to exit search and return  *
  751.  *  to Iterate() to report this.                            *
  752.  *                                                          *
  753.  *  In Generation II, multiple threads can reach this point *
  754.  *  at the same time.  We allow multiple threads to split   *
  755.  *  at the same time, but then the idle threads will choose *
  756.  *  to join the thread with the most attractive split point *
  757.  *  rather than just taking pot-luck.  The only limitation  *
  758.  *  on a thread adding a split point here is that if the    *
  759.  *  thread already has enough joinable split points have    *
  760.  *  not been joined yet, we do not incur the overhead of    *
  761.  *  creating another split point until the existing split   *
  762.  *  point is completed or a thread joins at at that point.  *
  763.  *                                                          *
  764.  *  We do not lock anything here, as the split operation    *
  765.  *  only affects thread-local data.  When the split is done *
  766.  *  then the ThreadJoin() function will acquire the lock    *
  767.  *  needed to avoid race conditions during the join op-     *
  768.  *  eration.                                                *
  769.  *                                                          *
  770.  ************************************************************
  771.  */
  772. #if (CPUS > 1)
  773.     if (mode == serial && moves_done && smp_threads &&
  774.         ThreadSplit(tree, ply, depth, alpha, original_alpha, moves_done))
  775.       do {
  776.         tree->alpha = alpha;
  777.         tree->beta = beta;
  778.         tree->value = alpha;
  779.         tree->wtm = wtm;
  780.         tree->ply = ply;
  781.         tree->depth = depth;
  782.         tree->in_check = in_check;
  783.         tree->searched = searched;
  784.         if (Split(tree)) {
  785.           if (abort_search || tree->stop)
  786.             return 0;
  787.           value = tree->value;
  788.           if (value > alpha) {
  789.             if (ply == 1)
  790.               tree->pv[0] = tree->pv[1];
  791.             if (value >= beta) {
  792.               HashStore(tree, ply, depth, wtm, LOWER, value, tree->cutmove);
  793.               return value;
  794.             }
  795.             alpha = value;
  796.             break;
  797.           }
  798.         }
  799.       } while (0);
  800. #endif
  801.   }
  802. /*
  803.  ************************************************************
  804.  *                                                          *
  805.  *  SMP Cleanup.  If we are doing an SMP search, there are  *
  806.  *  no "end-of-search" things to do.  We have searched all  *
  807.  *  the remaining moves at this ply in parallel, and now    *
  808.  *  return and let the original search that started this    *
  809.  *  sub-tree clean up, do the tests for mate/stalemate,     *
  810.  *  update the hash table, etc.                             *
  811.  *                                                          *
  812.  *  As we return, we end back up in Thread() where we       *
  813.  *  started, which then copies the best score/etc back to   *
  814.  *  the parent thread.                                      *
  815.  *                                                          *
  816.  ************************************************************
  817.  */
  818.   if (abort_search || tree->stop || mode == parallel)
  819.     return alpha;
  820. /*
  821.  ************************************************************
  822.  *                                                          *
  823.  *  Search completed.  All moves have been searched.  If    *
  824.  *  none were legal, return either MATE or DRAW depending   *
  825.  *  on whether the side to move is in check or not.         *
  826.  *                                                          *
  827.  ************************************************************
  828.  */
  829.   if (moves_done == 0) {
  830.     value = (Check(wtm)) ? -(MATE - ply) : DrawScore(wtm);
  831.     if (value >= alpha && value < beta) {
  832.       SavePV(tree, ply, 0);
  833. #if defined(TRACE)
  834.       if (ply <= trace_level)
  835.         printf("Search() no moves!  ply=%d\n", ply);
  836. #endif
  837.     }
  838.     return value;
  839.   } else {
  840.     bestmove =
  841.         (alpha ==
  842.         original_alpha) ? tree->hash_move[ply] : tree->pv[ply].path[ply];
  843.     type = (alpha == original_alpha) ? UPPER : EXACT;
  844.     if (repeat == 2 && alpha != -(MATE - ply - 1)) {
  845.       value = DrawScore(wtm);
  846.       if (value < beta)
  847.         SavePV(tree, ply, 4);
  848. #if defined(TRACE)
  849.       if (ply <= trace_level)
  850.         printf("draw by 50 move rule detected, ply=%d.\n", ply);
  851. #endif
  852.       return value;
  853.     } else if (alpha != original_alpha) {
  854.       tree->pv[ply - 1] = tree->pv[ply];
  855.       tree->pv[ply - 1].path[ply - 1] = tree->curmv[ply - 1];
  856.     }
  857.     HashStore(tree, ply, depth, wtm, type, alpha, bestmove);
  858.     return alpha;
  859.   }
  860. }
  861.  
  862. /* last modified 07/01/15 */
  863. /*
  864.  *******************************************************************************
  865.  *                                                                             *
  866.  *   SearchMove() implements the PVS search and returns the value.  We do a    *
  867.  *   null-window search with the window (alpha, t_beta) and if that fails high *
  868.  *   we repeat the search with the window {alpha, beta} assuming that beta !=  *
  869.  *   t_beta.                                                                   *
  870.  *                                                                             *
  871.  *******************************************************************************
  872.  */
  873. int SearchMove(TREE * RESTRICT tree, int ply, int depth, int wtm, int alpha,
  874.     int t_beta, int beta, int extend, int reduce, int check) {
  875.   int value;
  876. /*
  877.  ************************************************************
  878.  *                                                          *
  879.  *  PVS search.  We have determined whether the depth is to *
  880.  *  be changed by an extension or a reduction.  If we get   *
  881.  *  to this point, then the move is not being pruned.  So   *
  882.  *  off we go to a recursive search/quiescence call to work *
  883.  *  our way toward a terminal node.                         *
  884.  *                                                          *
  885.  *  There is one special-case to handle.  If the depth was  *
  886.  *  reduced, and Search() returns a value >= beta then      *
  887.  *  accepting that is risky (we reduced the move as we      *
  888.  *  thought it was bad and expected it to fail low) so we   *
  889.  *  repeat the search using the original (non-reduced)      *
  890.  *  depth to see if the fail-high happens again.            *
  891.  *                                                          *
  892.  ************************************************************
  893.  */
  894.   if (depth + extend - reduce - 1 > 0) {
  895.     value =
  896.         -Search(tree, ply + 1, depth + extend - reduce - 1, Flip(wtm),
  897.         -t_beta, -alpha, check, DO_NULL);
  898.     if (value > alpha && reduce) {
  899.       value =
  900.           -Search(tree, ply + 1, depth - 1, Flip(wtm), -t_beta, -alpha, check,
  901.           DO_NULL);
  902.     }
  903.   } else
  904.     value = -Quiesce(tree, ply + 1, Flip(wtm), -t_beta, -alpha, 1);
  905.   if (abort_search || tree->stop)
  906.     return 0;
  907. /*
  908.  ************************************************************
  909.  *                                                          *
  910.  *  PVS re-search.  This is the PVS re-search code.  If we  *
  911.  *  reach this point and value > alpha and value < beta,    *
  912.  *  then this can not be a null-window search.  We have to  *
  913.  *  re-search the position with the original beta value     *
  914.  *  to see if it still fails high before we treat this as a *
  915.  *  real fail-high and back up the value to the previous    *
  916.  *  ply.                                                    *
  917.  *                                                          *
  918.  ************************************************************
  919.  */
  920.   if (value > alpha && value < beta && t_beta < beta) {
  921.     if (ply == 1)
  922.       return beta;
  923.     if (depth + extend - 1 > 0)
  924.       value =
  925.           -Search(tree, ply + 1, depth + extend - 1, Flip(wtm), -beta, -alpha,
  926.           check, DO_NULL);
  927.     else
  928.       value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 1);
  929.     if (abort_search || tree->stop)
  930.       return 0;
  931.   }
  932.   return value;
  933. }
  934.