Exemplo n.º 1
0
static int negascout(struct SearchData *sd,
                     int alpha,
                     int beta,
                     const int depth,
                     int node_type
#if MP
                     ,int exclusiveP
#endif /* MP  */
                    ) {
    struct Position *p = sd->position;
    struct SearchStatus *st;
    int best = -INF;
    int bestm = M_NONE;
    int tmp;
    int talpha;
    int incheck;
    int lmove;
    int move;
    int extend = 0;
    int threat = FALSE;
    int reduce_extensions;
    int next_type;
    int was_futile = FALSE;
#if FUTILITY
    int is_futile;
    int optimistic = 0;
#endif

#if MP
    int deferred_cnt = 0;
    int deferred_list[MAX_DEFERRED];
    int deferred_depth[MAX_DEFERRED];
#endif

    EnterNode(sd);

    Nodes++;

    /* check for search termination */
    if (sd->master && TerminateSearch(sd)) {
        AbortSearch = TRUE;
        goto EXIT;
    }

    /* max search depth reached */
    if (sd->ply >= MaxDepth) goto EXIT;

    /*
     * Check for insufficent material or theoretical draw.
     */

    if ( /* InsufMat(p) || CheckDraw(p) || */  Repeated(p, FALSE)) {
        best = 0;
        goto EXIT;
    }

    /*
     * check extension
     */

    incheck = InCheck(p, p->turn);
    if (incheck && p->material[p->turn] > 0) {
        extend += CheckExtend(p);
        ChkExt++;
    }

    /*
     * Check the hashtable
     */

    st = sd->current;

    HTry++;
#if MP
    switch (ProbeHT(p->hkey, &tmp, depth, &(st->st_hashmove), &threat, sd->ply,
                    exclusiveP, sd->localHashTable))
#else
    switch (ProbeHT(p->hkey, &tmp, depth, &(st->st_hashmove), &threat, sd->ply))
#endif /* MP */
    {
    case ExactScore:
        HHit++;
        best = tmp;
        goto EXIT;
    case UpperBound:
        if (tmp <= alpha) {
            HHit++;
            best = tmp;
            goto EXIT;
        }
        break;
    case LowerBound:
        if (tmp >= beta) {
            HHit++;
            best = tmp;
            goto EXIT;
        }
        break;
    case Useless:
        threat = !incheck && MateThreat(p, OPP(p->turn));
        break;
#if MP
    case OnEvaluation:
        best = -ON_EVALUATION;
        goto EXIT;
#endif
    }

    /*
     * Probe EGTB
     */

    if (depth > EGTBDepth && ProbeEGTB(p, &tmp, sd->ply)) {
        best = tmp;
        goto EXIT;
    }

    /*
     * Probe recognizers
     */

    switch (ProbeRecognizer(p, &tmp)) {
    case ExactScore:
        best = tmp;
        goto EXIT;
    case LowerBound:
        if (tmp >= beta) {
            best = tmp;
            goto EXIT;
        }
        break;
    case UpperBound:
        if (tmp <= alpha) {
            best = tmp;
            goto EXIT;
        }
        break;
    }

#if NULLMOVE

    /*
     * Null move search.
     * See Christian Donninger, "Null Move and Deep Search"
     * ICCA Journal Volume 16, No. 3, pp. 137-143
     */

    if (!incheck && node_type == CutNode && !threat) {
        int next_depth;
        int nms;

        next_depth = depth - ReduceNullMove;

        if (next_depth > 0) {
            next_depth = depth - ReduceNullMoveDeep;
        }

        DoNull(p);
        if (next_depth < 0) {
            nms = -quies(sd, -beta, -beta+1, 0);
        } else {
#if MP
            nms = -negascout(sd, -beta, -beta+1, next_depth, AllNode, 0);
#else
            nms = -negascout(sd, -beta, -beta+1, next_depth, AllNode);
#endif
        }
        UndoNull(p);

        if (AbortSearch) goto EXIT;
        if (nms >= beta) {
            if (p->nonPawn[p->turn] >= Value[Queen]) {
                best = nms;
                goto EXIT;
            } else {
                if (next_depth < 0) {
                    nms = quies(sd, beta-1, beta, 0);
                } else {
#if MP
                    nms = negascout(sd, beta-1, beta, next_depth, CutNodeNoNull,
                                    0);
#else
                    nms = negascout(sd, beta-1, beta, next_depth,
                                    CutNodeNoNull);
#endif
                }

                if (nms >= beta) {
                    best = nms;
                    goto EXIT;
                } else {
                    extend += ExtendZugzwang;
                    ZZExt++;
                }
            }
        } else if (nms <= -CMLIMIT) {
            threat = TRUE;
        }
    }
#endif /* NULLMOVE */

    lmove = (p->actLog-1)->gl_Move;
    reduce_extensions = (sd->ply > 2*sd->depth);
    talpha = alpha;

    switch (node_type) {
    case AllNode:
        next_type = CutNode;
        break;
    case CutNode:
    case CutNodeNoNull:
        next_type = AllNode;
        break;
    default:
        next_type = PVNode;
        break;
    }

#if FUTILITY
    is_futile = !incheck && !threat && alpha < CMLIMIT && alpha > -CMLIMIT;
    if (is_futile) {
        if (p->turn == White) {
            optimistic = MaterialBalance(p) + MaxPos;
        } else {
            optimistic = -MaterialBalance(p) + MaxPos;
        }
    }
#endif /* FUTILITY */

    /*
     * Internal iterative deepening. If we do not have a move, we try
     * a shallow search to find a good candidate.
     */

    if (depth > 2*OnePly && ((alpha + 1) != beta) && !LegalMove(p, st->st_hashmove)) {
        int useless;
#if MP
        useless = negascout(sd, alpha, beta, depth-2*OnePly, PVNode, 0);
#else
        useless = negascout(sd, alpha, beta, depth-2*OnePly, PVNode);
#endif
        st->st_hashmove = sd->pv_save[sd->ply+1];
    }

    /*
     * Search all legal moves
     */

    while ((move = incheck ? NextEvasion(sd) : NextMove(sd)) != M_NONE) {
        int next_depth = extend;

        if (move & M_CANY && !MayCastle(p, move)) continue;

        /*
         * recapture extension
         */

        if ((move & M_CAPTURE) && (lmove & M_CAPTURE) &&
                M_TO(move) == M_TO(lmove) &&
                IsRecapture(p->piece[M_TO(move)], (p->actLog-1)->gl_Piece)) {
            RCExt += 1;
            next_depth += ExtendRecapture[TYPE(p->piece[M_TO(move)])];
        }

        /*
         * passed pawn push extension
         */

        if (TYPE(p->piece[M_FROM(move)]) == Pawn &&
                p->nonPawn[OPP(p->turn)] <= Value[Queen]) {

            int to = M_TO(move);

            if (((p->turn == White && to >= a7)
                    || (p->turn == Black && to <= h2))
                    && IsPassed(p, to, p->turn) && SwapOff(p, move) >= 0) {
                next_depth += ExtendPassedPawn;
                PPExt += 1;
            }
        }

        /*
         * limit extensions to sensible range.
         */

        if (reduce_extensions) next_depth /= 2;

        next_depth += depth - OnePly;

#if FUTILITY

        /*
         * Futility cutoffs
         */

        if (is_futile) {
            if (next_depth < 0 && !IsCheckingMove(p, move)) {
                tmp = optimistic + ScoreMove(p, move);
                if (tmp <= alpha) {
                    if (tmp > best) {
                        best = tmp;
                        bestm = move;
                        was_futile = TRUE;
                    }
                    continue;
                }
            }
#if EXTENDED_FUTILITY

            /*
             * Extended futility cutoffs and limited razoring.
             * See Ernst A. Heinz, "Extended Futility Pruning"
             * ICCA Journal Volume 21, No. 2, pp 75-83
             */

            else if (next_depth >= 0 && next_depth < OnePly
                     && !IsCheckingMove(p, move)) {
                tmp = optimistic + ScoreMove(p, move) + (3*Value[Pawn]);
                if (tmp <= alpha) {
                    if (tmp > best) {
                        best = tmp;
                        bestm = move;
                        was_futile = TRUE;
                    }
                    continue;
                }
            }
#if RAZORING
            else if (next_depth >= OnePly && next_depth < 2*OnePly
                     && !IsCheckingMove(p, move)) {
                tmp = optimistic + ScoreMove(p, move) + (6*Value[Pawn]);
                if (tmp <= alpha) {
                    next_depth -= OnePly;
                }
            }
#endif /* RAZORING */
#endif /* EXTENDED_FUTILITY */
        }

#endif /* FUTILITY */

        DoMove(p, move);
        if (InCheck(p, OPP(p->turn))) {
            UndoMove(p, move);
        } else {
            /*
             * Check extension
             */

            if (p->material[p->turn] > 0 && InCheck(p, p->turn)) {
                next_depth += (reduce_extensions) ?
                              ExtendInCheck>>1 : ExtendInCheck;
            }

            /*
             * Recursively search this position. If depth is exhausted, use
             * quies, otherwise use negascout.
             */

            if (next_depth < 0) {
                tmp = -quies(sd, -beta, -talpha, 0);
            } else if (bestm != M_NONE && !was_futile) {
#if MP
                tmp = -negascout(sd, -talpha-1, -talpha, next_depth, next_type,
                                 bestm != M_NONE);
                if (tmp != ON_EVALUATION && tmp > talpha && tmp < beta) {
                    tmp = -negascout(sd, -beta, -tmp, next_depth,
                                     node_type == PVNode ? PVNode : AllNode,
                                     bestm != M_NONE);
                }
#else
                tmp = -negascout(sd, -talpha-1, -talpha, next_depth, next_type);
                if (tmp > talpha && tmp < beta) {
                    tmp = -negascout(sd, -beta, -tmp, next_depth,
                                     node_type == PVNode ? PVNode : AllNode);
                }
#endif /* MP */
            } else {
#if MP
                tmp = -negascout(sd, -beta, -talpha, next_depth, next_type,
                                 bestm != M_NONE);
#else
                tmp = -negascout(sd, -beta, -talpha, next_depth, next_type);
#endif /* MP */
            }

            UndoMove(p, move);

            if (AbortSearch) goto EXIT;

#if MP
            if (tmp == ON_EVALUATION) {

                /*
                 * This child is ON_EVALUATION. Remember move and
                 * depth.
                 */

                deferred_list[deferred_cnt] = move;
                deferred_depth[deferred_cnt] = next_depth;
                deferred_cnt++;

            } else {
#endif /* MP */

                /*
                 * beta cutoff, enter move in Killer/Countermove table
                 */

                if (tmp >= beta) {
                    if (!(move & M_TACTICAL)) {
                        PutKiller(sd, move);
                        sd->counterTab[p->turn][lmove & 4095] = move;
                    }
                    StoreResult(sd, tmp, alpha, beta, move, depth, threat);
                    best = tmp;
                    goto EXIT;
                }

                /*
                 * Improvement on best move to date
                 */

                if (tmp > best) {
                    best = tmp;
                    bestm = move;
                    was_futile = FALSE;

                    if (best > talpha) {
                        talpha = best;
                    }
                }

                next_type = CutNode;
#if MP
            }
#endif /* MP */
        }
    }
Exemplo n.º 2
0
/*
********************************************************************************
*                                                                              *
*   Search() is the recursive routine used to implement the alpha/beta         *
*   negamax search (similar to minimax but simpler to code.)  Search() is      *
*   called whenever there is "depth" remaining so that all moves are subject   *
*   to searching, or when the side to move is in check, to make sure that this *
*   side isn't mated.  Search() recursively calls itself until depth is ex-    *
*   hausted, at which time it calls Quiesce() instead.                         *
*                                                                              *
********************************************************************************
*/
int Search(int alpha, int beta, int wtm, int depth, int ply, int do_null)
{
  register int first_move=1;
  register BITBOARD save_hash_key;
  register int initial_alpha, value;
  register int extensions;
/*
 ----------------------------------------------------------
|                                                          |
|   check to see if we have searched enough nodes that it  |
|   is time to peek at how much time has been used, or if  |
|   is time to check for operator keyboard input.  this is |
|   usually enough nodes to force a time/input check about |
|   once per second, except when the target time per move  |
|   is very small, in which case we try to check the time  |
|   at least 10 times during the search.                   |
|                                                          |
 ----------------------------------------------------------
*/
  if (ply >= MAXPLY-2) return(beta);
  nodes_searched++;
  if (--next_time_check <= 0) {
    next_time_check=nodes_between_time_checks;
    if (CheckInput()) Interrupt(ply);
    time_abort+=TimeCheck(0);
    if (time_abort) {
      abort_search=1;
      return(0);
    }
  }
/*
 ----------------------------------------------------------
|                                                          |
|   check for draw by repetition.                          |
|                                                          |
 ----------------------------------------------------------
*/
  if (RepetitionCheck(ply,wtm)) {
    value=(wtm==root_wtm) ? DrawScore() : -DrawScore();
    if (value < beta) SavePV(ply,value,0);
#if !defined(FAST)
    if(ply <= trace_level) printf("draw by repetition detected, ply=%d.\n",ply);
#endif
    return(value);
  }
/*
 ----------------------------------------------------------
|                                                          |
|   now call LookUp() to see if this position has been     |
|   searched before.  if so, we may get a real score,      |
|   produce a cutoff, or get nothing more than a good move |
|   to try first.  there are four cases to handle:         |
|                                                          |
|   1. LookUp() returned "EXACT_SCORE" if this score is    |
|   greater than beta, return beta.  otherwise, return the |
|   score.  In either case, no further searching is needed |
|   from this position.  note that lookup verified that    |
|   the table position has sufficient "draft" to meet the  |
|   requirements of the current search depth remaining.    |
|                                                          |
|   2.  LookUp() returned "LOWER_BOUND" which means that   |
|   when this position was searched previously, every move |
|   was "refuted" by one of its descendents.  as a result, |
|   when the search was completed, we returned alpha at    |
|   that point.  we simply return alpha here as well.      |
|                                                          |
|   3.  LookUp() returned "UPPER_BOUND" which means that   |
|   when we encountered this position before, we searched  |
|   one branch (probably) which promptly refuted the move  |
|   at the previous ply.                                   |
|                                                          |
|   4.  LookUp() returned "AVOID_NULL_MOVE" which means    |
|   the hashed score/bound was no good, but it indicated   |
|   that trying a null-move in this position will be a     |
|   waste of time.                                         |
|                                                          |
 ----------------------------------------------------------
*/
  switch (LookUp(ply,depth,wtm,&alpha,beta)) {
    case EXACT_SCORE:
      if(alpha >= beta) return(beta);
      else {
        SavePV(ply,alpha,1);
        return(alpha);
      }
    case LOWER_BOUND:
      return(alpha);
    case UPPER_BOUND:
      return(beta);
    case AVOID_NULL_MOVE:
      do_null=0;
  }
/*
 ----------------------------------------------------------
|                                                          |
|   now it's time to try a probe into the endgame table-   |
|   base files.  this is done if (a) the previous move was |
|   a capture or promotion, unless we are at very shallow  |
|   plies (<4) in the search; (b) there are less than 5    |
|   pieces left (currently all interesting 4 piece endings |
|   are available.)                                        |
|                                                          |
 ----------------------------------------------------------
*/
#if defined(TABLEBASES)
  if (TotalPieces < 5) do {
    register int wpawn, bpawn;
    int tb_value;
    if (TotalWhitePawns && TotalBlackPawns) {
      wpawn=FirstOne(WhitePawns);
      bpawn=FirstOne(BlackPawns);
      if (FileDistance(wpawn,bpawn) == 1) {
        if(((Rank(wpawn)==1) && (Rank(bpawn)>2)) ||
           ((Rank(bpawn)==6) && (Rank(wpawn)<5)) || 
           EnPassant(ply)) break;
      }
    }
    tb_probes++;
    if (EGTBScore(ply, wtm, &tb_value)) {
      tb_probes_successful++;
      alpha=tb_value;
      if (abs(alpha) > MATE-100) alpha+=(alpha > 0) ? -(ply-1) : +(ply-1);
      else if (alpha == 0) alpha=(wtm==root_wtm) ? DrawScore() : -DrawScore();
      if(alpha >= beta) return(beta);
      else {
        SavePV(ply,alpha,2);
        return(alpha);
      }
    }
  } while(0);
# endif
/*
 ----------------------------------------------------------
|                                                          |
|   initialize.                                            |
|                                                          |
 ----------------------------------------------------------
*/
  in_check[ply+1]=0;
  extended_reason[ply+1]=no_extension;
  initial_alpha=alpha;
  last[ply]=last[ply-1];
  killer_count1[ply+1]=0;
  killer_count2[ply+1]=0;
/*
 ----------------------------------------------------------
|                                                          |
|  first, we try a null move to see if we can get a quick  |
|  cutoff with only a little work.  this operates as       |
|  follows.  instead of making a legal move, the side on   |
|  move 'passes' and does nothing.  the resulting position |
|  is searched to a shallower depth than normal (usually   |
|  one ply less but settable by the operator) this should  |
|  result in a cutoff or at least should set the lower     |
|  bound better since anything should be better than not   |
|  doing anything.                                         |
|                                                          |
|  this is skipped for any of the following reasons:       |
|                                                          |
|  1.  the side on move is in check.  the null move        |
|      results in an illegal position.                     |
|  2.  no more than one null move can appear in succession |
|      or else the search will degenerate into nothing.    |
|  3.  the side on move has little material left making    |
|      zugzwang positions more likely.                     |
|                                                          |
 ----------------------------------------------------------
*/
# if defined(NULL_MOVE_DEPTH)
  if (do_null && !in_check[ply] && 
      ((wtm) ? TotalWhitePieces : TotalBlackPieces)>2) {
    current_move[ply]=0;
    current_phase[ply]=NULL_MOVE;
#if !defined(FAST)
    if (ply <= trace_level)
      SearchTrace(ply,depth,wtm,alpha,beta,"Search",0);
#endif
    position[ply+1]=position[ply];
    Rule50Moves(ply+1)++;
    save_hash_key=HashKey;
    if (EnPassant(ply)) {
      HashEP(EnPassant(ply+1),HashKey);
      EnPassant(ply+1)=0;
    }
    if ((depth-NULL_MOVE_DEPTH-INCREMENT_PLY) >= INCREMENT_PLY)
      value=-Search(-beta,-alpha,ChangeSide(wtm),depth-NULL_MOVE_DEPTH-INCREMENT_PLY,ply+1,NO_NULL);
    else 
      value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1);
    HashKey=save_hash_key;
    if (abort_search) return(0);
    if (value >= beta) {
      StoreRefutation(ply,depth,wtm,beta);
      return(beta);
    }
  }
# endif
/*
 ----------------------------------------------------------
|                                                          |
|   if there is no best move from the hash table, and this |
|   is a PV node, then we need a good move to search       |
|   first.  while killers and history moves are good, they |
|   are not "good enough".  the simplest action is to try  |
|   a shallow search (depth-2) to get a move.  note that   |
|   when we call Search() with depth-2, it, too, will      |
|   not have a hash move, and will therefore recursively   |
|   continue this process, hence the name "internal        |
|   iterative deepening."                                  |
|                                                          |
 ----------------------------------------------------------
*/
  next_status[ply].phase=FIRST_PHASE;
  if (hash_move[ply]==0 && (depth > 2*INCREMENT_PLY) &&
      (((ply & 1) && alpha == root_alpha && beta == root_beta) ||
      (!(ply & 1) && alpha == -root_beta && beta == -root_alpha))) {
    current_move[ply]=0;
    value=Search(alpha,beta,wtm,depth-2*INCREMENT_PLY,ply,DO_NULL);
    if (abort_search) return(0);
    if (value <= alpha) {
      value=Search(-MATE,beta,wtm,depth-2*INCREMENT_PLY,ply,DO_NULL);
      if (abort_search) return(0);
    }
    else if (value < beta) {
      if ((int) pv[ply-1].path_length >= ply) hash_move[ply]=pv[ply-1].path[ply];
    }
    else hash_move[ply]=current_move[ply];
    last[ply]=last[ply-1];
    next_status[ply].phase=FIRST_PHASE;
  }
/*
 ----------------------------------------------------------
|                                                          |
|   now iterate through the move list and search the       |
|   resulting positions.  note that Search() culls any     |
|   move that is not legal by using Check().  the special  |
|   case is that we must find one legal move to search to  |
|   confirm that it's not a mate or draw.                  |
|                                                          |
 ----------------------------------------------------------
*/
  while ((current_phase[ply]=(in_check[ply]) ? NextEvasion(ply,wtm) : 
                                               NextMove(depth,ply,wtm))) {
    extended_reason[ply]&=check_extension;
#if !defined(FAST)
    if (ply <= trace_level) SearchTrace(ply,depth,wtm,alpha,beta,"Search",current_phase[ply]);
#endif
/*
 ----------------------------------------------------------
|                                                          |
|   if two successive moves are capture / re-capture so    |
|   that the material score is restored, extend the search |
|   by one ply on the re-capture since it is pretty much   |
|   forced and easy to analyze.                            |
|                                                          |
 ----------------------------------------------------------
*/
    extensions=-INCREMENT_PLY;
    if (Captured(current_move[ply]) && Captured(current_move[ply-1]) &&
        To(current_move[ply-1]) == To(current_move[ply]) &&
        (p_values[Captured(current_move[ply-1])+7] == 
         p_values[Captured(current_move[ply])+7] ||
         Promote(current_move[ply-1])) &&
        !(extended_reason[ply-1]&recapture_extension)) {
      extended_reason[ply]|=recapture_extension;
      recapture_extensions_done++;
      extensions+=RECAPTURE;
    }
/*
 ----------------------------------------------------------
|                                                          |
|   if we push a passed pawn, we need to look deeper to    |
|   see if it is a legitimate threat.                      |
|                                                          |
 ----------------------------------------------------------
*/
    if (Piece(current_move[ply])==pawn && !FutileAhead(wtm) &&
         ((wtm && To(current_move[ply])>H5 && TotalBlackPieces<16 &&
          !And(mask_pawn_passed_w[To(current_move[ply])],BlackPawns)) ||
         (!wtm && To(current_move[ply])<A4 && TotalWhitePieces<16 &&
          !And(mask_pawn_passed_b[To(current_move[ply])],WhitePawns)) ||
         push_extensions[To(current_move[ply])]) &&
         Swap(From(current_move[ply]),To(current_move[ply]),wtm) >= 0) {
      extended_reason[ply]|=passed_pawn_extension;
      passed_pawn_extensions_done++;
      extensions+=PASSED_PAWN_PUSH;
    }
/*
 ----------------------------------------------------------
|                                                          |
|   now make the move and search the resulting position.   |
|   if we are in check, the current move must be legal     |
|   since NextEvasion ensures this, otherwise we have to   |
|   make sure the side-on-move is not in check after the   |
|   move to weed out illegal moves and save time.          |
|                                                          |
 ----------------------------------------------------------
*/
    MakeMove(ply,current_move[ply],wtm);
    if (in_check[ply] || !Check(wtm)) {
/*
 ----------------------------------------------------------
|                                                          |
|   if the move to be made checks the opponent, then we    |
|   need to remember that he's in check and also extend    |
|   the depth by one ply for him to get out.               |
|                                                          |
 ----------------------------------------------------------
*/
      if (Check(ChangeSide(wtm))) {
        in_check[ply+1]=1;
        extended_reason[ply+1]=check_extension;
        check_extensions_done++;
        extensions+=IN_CHECK;
      }
      else {
        in_check[ply+1]=0;
        extended_reason[ply+1]=no_extension;
      }
/*
 ----------------------------------------------------------
|                                                          |
|   now we toss in the "razoring" trick, which simply says |
|   if we are doing fairly badly, we can reduce the depth  |
|   an additional ply, if there was nothing at the current |
|   ply that caused an extension.                          |
|                                                          |
 ----------------------------------------------------------
*/
      if (depth < 3*INCREMENT_PLY && !in_check[ply] &&
          extensions == -INCREMENT_PLY) {
        register int val=(wtm) ? Material : -Material;
        if (val+1500 < alpha) extensions-=INCREMENT_PLY;
      }
/*
 ----------------------------------------------------------
|                                                          |
|   if there's only one legal move, extend the search one  |
|   additional ply since this node is very easy to search. |
|                                                          |
 ----------------------------------------------------------
*/
      if (first_move) {
        if (last[ply]-last[ply-1] == 1) {
          extended_reason[ply]|=one_reply_extension;
          one_reply_extensions_done++;
          extensions+=ONE_REPLY_TO_CHECK;
        }
        if (depth+MaxExtensions(extensions) >= INCREMENT_PLY)
          value=-Search(-beta,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL);
        else {
          value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1);
        }
        if (abort_search) {
          UnMakeMove(ply,current_move[ply],wtm);
          return(0);
        }
        first_move=0;
      }
      else {
        if (depth+MaxExtensions(extensions) >= INCREMENT_PLY)
          value=-Search(-alpha-1,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL);
        else {
          value=-Quiesce(-alpha-1,-alpha,ChangeSide(wtm),ply+1);
        }
        if (abort_search) {
          UnMakeMove(ply,current_move[ply],wtm);
          return(0);
        }
        if (value>alpha && value<beta) {
          if (depth+MaxExtensions(extensions) >= INCREMENT_PLY)
            value=-Search(-beta,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL);
          else 
            value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1);
          if (abort_search) {
            UnMakeMove(ply,current_move[ply],wtm);
            return(0);
          }
        }
      }
      if (value > alpha) {
        if(value >= beta) {
          HistoryRefutation(ply,depth,wtm);
          UnMakeMove(ply,current_move[ply],wtm);
          StoreRefutation(ply,depth,wtm,beta);
          return(beta);
        }
        alpha=value;
      }
    }
    UnMakeMove(ply,current_move[ply],wtm);
  }
/*
 ----------------------------------------------------------
|                                                          |
|   all moves have been searched.  if none were legal,     |
|   return either MATE or DRAW depending on whether the    |
|   side to move is in check or not.                       |
|                                                          |
 ----------------------------------------------------------
*/
  if (first_move == 1) {
    value=(Check(wtm)) ? -(MATE-ply) :
                         ((wtm==root_wtm) ? DrawScore() : -DrawScore());
    if(value > beta) value=beta;
    else if (value < alpha) value=alpha;
    if (value >=alpha && value <beta) {
      SavePV(ply,value,0);
#if !defined(FAST)
      if (ply <= trace_level) printf("Search() no moves!  ply=%d\n",ply);
#endif
    }
    return(value);
  }
  else {
    if (alpha != initial_alpha) {
      memcpy(&pv[ply-1].path[ply],&pv[ply].path[ply],(pv[ply].path_length-ply+1)*4);
      memcpy(&pv[ply-1].path_hashed,&pv[ply].path_hashed,3);
      pv[ply-1].path[ply-1]=current_move[ply-1];
      HistoryBest(ply,depth,wtm);
    }
    StoreBest(ply,depth,wtm,alpha,initial_alpha);
/*
 ----------------------------------------------------------
|                                                          |
|   if the 50-move rule is drawing close, then adjust the  |
|   score to reflect the impending draw.                   |
|                                                          |
 ----------------------------------------------------------
*/
    if (Rule50Moves(ply) > 99) {
      value=(wtm==root_wtm) ? DrawScore() : -DrawScore();
      if (value < beta) SavePV(ply,value,0);
#if !defined(FAST)
      if(ply <= trace_level) printf("draw by 50-move rule detected, ply=%d.\n",ply);
#endif
      return(value);
    }
    return(alpha);
  }
}