/*
 
    Protector -- a UCI chess engine
 
 
 
    Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)
 
 
 
    This program is free software: you can redistribute it and/or modify
 
    it under the terms of the GNU General Public License as published by
 
    the Free Software Foundation, either version 3 of the License, or
 
    (at your option) any later version.
 
 
 
    This program is distributed in the hope that it will be useful,
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
    GNU General Public License for more details.
 
 
 
    You should have received a copy of the GNU General Public License
 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
 
*/
 
 
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <assert.h>
 
#include <time.h>
 
#include <math.h>
 
#include "search.h"
 
#include "matesearch.h"
 
#include "io.h"
 
#include "movegeneration.h"
 
#include "hash.h"
 
#include "evaluation.h"
 
#include "book.h"
 
#include "coordination.h"
 
#include "xboard.h"
 
 
 
#ifdef INCLUDE_TABLEBASE_ACCESS
 
#include "tablebase.h"
 
#endif
 
 
 
/* #define DEBUG_THREAD_COORDINATION */
 
 
 
extern bool resetSharedHashtable;
 
const int HASH_DEPTH_OFFSET = 3;
 
const UINT64 GUI_NODE_COUNT_MIN = 250000;
 
 
 
#define NUM_FUTILITY_MARGIN_VALUES 13
 
#define NUM_LOG_VALUES 256
 
INT32 checkTimeCount = 0;
 
 
 
int quietMoveCountLimit[2][32]; /* number of quiet moves to be examined @ specific restDepth */
 
int quietPvMoveReduction[64][64];       /* [restDepth][moveCount] */
 
int quietMoveReduction[2][64][64];      /* [variationType][restDepth][moveCount] */
 
int futilityMargin[NUM_FUTILITY_MARGIN_VALUES + 1];     /* [restDepth] */
 
int maxPieceValue[16];          /* the maximal value of a piece type */
 
 
 
/* Prototypes */
 
static int searchBest(Variation * variation, int alpha, int beta,
 
                      const int ply, const int restDepth, const bool pvNode,
 
                      const bool cutNode, Move * bestMove, Move excludeMove,
 
                      const bool tryEarlyPrunings); // Pierre-Marie Baty -- missing const in prototype
 
 
 
static bool moveIsQuiet(const Move move, const Position * position)
 
{
 
   return (bool) (position->piece[getToSquare(move)] == NO_PIECE &&
 
                  getNewPiece(move) == NO_PIECE &&
 
                  (getToSquare(move) != position->enPassantSquare ||
 
                   pieceType(position->piece[getFromSquare(move)]) != PAWN));
 
}
 
 
 
static void checkTerminationConditions(Variation * variation)
 
{
 
   if (variation->searchStatus == SEARCH_STATUS_RUNNING)
 
   {
 
      if (variation->terminate != FALSE &&
 
          (variation->iteration > 1 || variation->threadNumber > 0))
 
      {
 
         variation->searchStatus = SEARCH_STATUS_TERMINATE;
 
      }
 
   }
 
}
 
 
 
bool checkNodeExclusion(int restDepth)
 
{
 
   return restDepth >= 6 * DEPTH_RESOLUTION && getNumberOfThreads() > 1;
 
}
 
 
 
static bool hasDangerousPawns(const Position * position, const Color color)
 
{
 
   if (color == WHITE)
 
   {
 
      return (bool)
 
         ((position->piecesOfType[WHITE_PAWN] & squaresOfRank[RANK_7]) !=
 
          EMPTY_BITBOARD);
 
   }
 
   else
 
   {
 
      return (bool)
 
         ((position->piecesOfType[BLACK_PAWN] & squaresOfRank[RANK_2]) !=
 
          EMPTY_BITBOARD);
 
   }
 
}
 
 
 
int getEvalValue(Variation * variation)
 
{
 
   EvaluationBase base;
 
 
 
   base.ownColor = variation->ownColor;
 
   return
 
      getValue(&variation->singlePosition, &base, variation->pawnHashtable,
 
               variation->kingsafetyHashtable);
 
}
 
 
 
static int getStaticValue(Variation * variation, const int ply)
 
{
 
   PlyInfo *pi = &variation->plyInfo[ply];
 
 
 
   if (pi->staticValueAvailable == FALSE)
 
   {
 
      EvaluationBase base;
 
 
 
      base.ownColor = variation->ownColor;
 
      pi->staticValue = pi->refinedStaticValue =
 
         getValue(&variation->singlePosition, &base, variation->pawnHashtable,
 
                  variation->kingsafetyHashtable);
 
      pi->staticValueAvailable = TRUE;
 
   }
 
   else
 
   {
 
      assert(pi
->staticValue 
== getEvalValue
(variation
));  
   }
 
 
 
   return pi->staticValue;
 
}
 
 
 
static int getRefinedStaticValue(Variation * variation, const int ply)
 
{
 
   PlyInfo *pi = &variation->plyInfo[ply];
 
 
 
   if (pi->staticValueAvailable == FALSE)
 
   {
 
      EvaluationBase base;
 
 
 
      base.ownColor = variation->ownColor;
 
      pi->staticValue = pi->refinedStaticValue =
 
         getValue(&variation->singlePosition, &base, variation->pawnHashtable,
 
                  variation->kingsafetyHashtable);
 
      pi->staticValueAvailable = TRUE;
 
   }
 
   else
 
   {
 
      assert(pi
->staticValue 
== getEvalValue
(variation
));  
   }
 
 
 
   return pi->refinedStaticValue;
 
}
 
 
 
static void updateGains(Variation * variation, const int ply)
 
{
 
   if (variation->plyInfo[ply].gainsUpdated == FALSE &&
 
       variation->plyInfo[ply - 1].quietMove != FALSE)
 
   {
 
      const int moveIndex = variation->plyInfo[ply - 1].indexCurrentMove;
 
      INT16 *storedGain = &variation->positionalGain[moveIndex];
 
      const INT16 currentDiff = (INT16)
 
         (getStaticValue(variation, ply) +
 
          variation->plyInfo[ply - 1].staticValue);
 
 
 
      *storedGain =
 
         (currentDiff >= (*storedGain) ? currentDiff : (*storedGain) - 1);
 
   }
 
 
 
   variation->plyInfo[ply].gainsUpdated = TRUE;
 
}
 
 
 
static void updateCounterMoves(Variation * variation, const int ply,
 
                               Move counterMove)
 
{
 
   if (variation->plyInfo[ply - 1].currentMove != NULLMOVE)
 
   {
 
      const int moveIndex = variation->plyInfo[ply - 1].indexCurrentMove;
 
 
 
      if (counterMove != variation->counterMove1[moveIndex])
 
      {
 
         variation->counterMove2[moveIndex] =
 
            variation->counterMove1[moveIndex];
 
         variation->counterMove1[moveIndex] = counterMove;
 
      }
 
   }
 
}
 
 
 
static void updateFollowupMoves(Variation * variation, const int ply,
 
                                Move followupMove)
 
{
 
   if (variation->plyInfo[ply - 2].currentMove != NULLMOVE)
 
   {
 
      const int moveIndex = variation->plyInfo[ply - 2].indexCurrentMove;
 
 
 
      if (followupMove != variation->followupMove1[moveIndex])
 
      {
 
         variation->followupMove2[moveIndex] =
 
            variation->followupMove1[moveIndex];
 
         variation->followupMove1[moveIndex] = followupMove;
 
      }
 
   }
 
}
 
 
 
static bool positionIsWellKnown(Variation * variation,
 
                                Position * position,
 
                                const UINT64 hashKey,
 
                                Hashentry ** bestTableHit,
 
                                const int ply, const int alpha,
 
                                const int beta, const int restDepth,
 
                                const bool pvNode,
 
                                const bool updateGainValues,
 
                                Move * hashmove,
 
                                const Move excludeMove, int *value)
 
{
 
   Hashentry *tableHit = getHashentry(getSharedHashtable(),
 
                                      hashKey);
 
 
 
   if (tableHit != NULL)
 
   {                            /* 45% */
 
      const int importance = getHashentryImportance(tableHit);
 
      const int flag = getHashentryFlag(tableHit);
 
      const int hashEntryValue =
 
         calcEffectiveValue(getHashentryValue(tableHit), ply);
 
      PlyInfo *pi = &variation->plyInfo[ply];
 
 
 
      *bestTableHit = tableHit;
 
      *value = hashEntryValue;
 
 
 
      /*
 
         if (getHashentryStaticValue(tableHit) != getStaticValue(variation, ply))
 
         {
 
         logDebug("hv=%d sv=%d ev=%d\n", getHashentryStaticValue(tableHit),
 
         getStaticValue(variation, ply), getEvalValue(variation));
 
         dumpVariation(variation);
 
         }
 
       */
 
 
 
      assert(getHashentryStaticValue
(tableHit
) ==  
             getStaticValue(variation, ply));
 
 
 
      if (pi->staticValueAvailable == FALSE)
 
      {
 
         pi->staticValue = pi->refinedStaticValue =
 
            getHashentryStaticValue(tableHit);
 
         pi->staticValueAvailable = TRUE;
 
 
 
         if (updateGainValues)
 
         {
 
            updateGains(variation, ply);
 
         }
 
      }
 
 
 
      if (pvNode == FALSE && excludeMove != NULLMOVE &&
 
          restDepth <= importance && flag != HASHVALUE_EVAL)
 
      {                         /* 99% */
 
         assert(flag 
== HASHVALUE_UPPER_LIMIT 
||  
                flag == HASHVALUE_EXACT || flag == HASHVALUE_LOWER_LIMIT);
 
         assert(hashEntryValue 
>= VALUE_MATED 
&&  
                hashEntryValue < -VALUE_MATED);
 
 
 
         switch (flag)
 
         {
 
         case HASHVALUE_UPPER_LIMIT:
 
            if (hashEntryValue <= alpha)
 
            {
 
               *value = (hashEntryValue <= VALUE_MATED ?
 
                         alpha : hashEntryValue);
 
 
 
               return TRUE;
 
            }
 
 
 
            if (restDepth >= HASH_DEPTH_OFFSET &&
 
                hashEntryValue < getStaticValue(variation, ply))
 
            {
 
               variation->plyInfo[ply].refinedStaticValue = hashEntryValue;
 
            }
 
            break;
 
 
 
         case HASHVALUE_EXACT:
 
            *value = hashEntryValue;
 
 
 
            return TRUE;
 
 
 
         case HASHVALUE_LOWER_LIMIT:
 
            if (hashEntryValue >= beta)
 
            {
 
               *value = (hashEntryValue >= -VALUE_MATED ?
 
                         beta : hashEntryValue);
 
 
 
               return TRUE;
 
            }
 
 
 
            if (restDepth >= HASH_DEPTH_OFFSET &&
 
                hashEntryValue > getStaticValue(variation, ply))
 
            {
 
               variation->plyInfo[ply].refinedStaticValue = hashEntryValue;
 
            }
 
            break;
 
 
 
         default:;
 
         }
 
      }
 
   }
 
 
 
   if (*bestTableHit != 0)
 
   {
 
      *hashmove = (Move) getHashentryMove(*bestTableHit);
 
 
 
      if (*hashmove != NO_MOVE)
 
      {                         /* 81% */
 
         assert(moveIsPseudoLegal
(position
, *hashmove
));  
 
 
         if (moveIsPseudoLegal(position, *hashmove))
 
         {
 
            assert(moveIsLegal
(position
, *hashmove
));  
         }
 
         else
 
         {
 
            *hashmove = NO_MOVE;
 
         }
 
      }
 
   }
 
 
 
   return FALSE;
 
}
 
 
 
static int searchBestQuiescence(Variation * variation, int alpha, int beta,
 
                                const int ply, const int restDepth,
 
                                Move * bestMove, const bool pvNode)
 
{
 
   const int oldAlpha = alpha;
 
   UINT8 hashentryFlag;
 
   UINT8 hashDepth = (restDepth >= 0 ? 2 : 1);
 
   Position *position = &variation->singlePosition;
 
   int best = VALUE_MATED, currentValue = VALUE_MATED, historyLimit, i;
 
   const int VALUE_MATE_HERE = -VALUE_MATED - ply + 1;
 
   const int VALUE_MATED_HERE = VALUE_MATED + ply;
 
   Movelist movelist;
 
   Move currentMove, bestReply, hashmove = NO_MOVE;
 
   const bool inCheck = variation->plyInfo[ply - 1].currentMoveIsCheck;
 
   EvaluationBase base;
 
   Hashentry *bestTableHit = 0;
 
   int hashValue;
 
 
 
   assert(alpha 
>= VALUE_MATED 
&& alpha 
<= -VALUE_MATED
);  
   assert(beta 
>= VALUE_MATED 
&& beta 
<= -VALUE_MATED
);  
   assert(ply 
> 0 && ply 
< MAX_DEPTH
);  
   assert(restDepth 
< DEPTH_RESOLUTION
);  
   assert(passiveKingIsSafe
(position
));  
   assert((inCheck 
!= FALSE
) == (activeKingIsSafe
(position
) == FALSE
));  
 
 
   *bestMove = NO_MOVE;
 
   variation->plyInfo[ply].quietMove = FALSE;   /* avoid subsequent gain updates */
 
   variation->plyInfo[ply].pv.length = 0;
 
   variation->plyInfo[ply].pv.move[0] = NO_MOVE;
 
   movelist.positionalGain = &(variation->positionalGain[0]);
 
 
 
   variation->nodes++;
 
   checkTerminationConditions(variation);
 
 
 
   if (variation->searchStatus != SEARCH_STATUS_RUNNING &&
 
       variation->iteration > 1)
 
   {
 
      return 0;
 
   }
 
 
 
   /* Check for a draw according to the 50-move-rule */
 
   /* ---------------------------------------------- */
 
   if (position->halfMoveClock > 100)
 
   {
 
      return variation->drawScore[position->activeColor];
 
   }
 
 
 
   /* Check for a draw by repetition. */
 
   /* ------------------------------- */
 
   historyLimit = POSITION_HISTORY_OFFSET + variation->ply -
 
      position->halfMoveClock;
 
 
 
 
 
   for (i = POSITION_HISTORY_OFFSET + variation->ply - 4;
 
        i >= historyLimit; i -= 2)
 
   {
 
      if (position->hashKey == variation->positionHistory[i])
 
      {
 
         return variation->drawScore[position->activeColor];
 
      }
 
   }
 
 
 
   /* Probe the transposition table */
 
   /* ----------------------------- */
 
 
 
   if (positionIsWellKnown(variation, position, position->hashKey,
 
                           &bestTableHit, ply, alpha, beta,
 
                           hashDepth, pvNode, FALSE,
 
                           &hashmove, NO_MOVE, &hashValue))
 
   {
 
      *bestMove = hashmove;
 
 
 
      return hashValue;
 
   }
 
 
 
   if (hashmove != NO_MOVE && inCheck == FALSE &&
 
       moveIsQuiet(hashmove, position))
 
   {
 
      hashmove = NO_MOVE;
 
   }
 
 
 
   if (inCheck == FALSE)
 
   {
 
      const bool staticValueAvailable =
 
         variation->plyInfo[ply].staticValueAvailable;
 
 
 
                      variation->pawnHashtable,
 
                      variation->kingsafetyHashtable) != FALSE);
 
 
 
      if (staticValueAvailable == FALSE)
 
      {
 
         base.ownColor = variation->ownColor;
 
         best = getValue(position,
 
                         &base,
 
                         variation->pawnHashtable,
 
                         variation->kingsafetyHashtable);
 
         variation->plyInfo[ply].staticValue =
 
            variation->plyInfo[ply].refinedStaticValue = best;
 
         variation->plyInfo[ply].staticValueAvailable = TRUE;
 
      }
 
      else
 
      {
 
         best = variation->plyInfo[ply].staticValue;
 
      }
 
 
 
      if (bestTableHit != 0)
 
      {
 
         const int flag = getHashentryFlag(bestTableHit);
 
         const int requiredFlag = (hashValue > best ?
 
                                   HASHVALUE_LOWER_LIMIT :
 
                                   HASHVALUE_UPPER_LIMIT);
 
 
 
         if (flag == requiredFlag)
 
         {
 
            best = hashValue;
 
         }
 
      }
 
 
 
      if (best > alpha)
 
      {
 
         alpha = best;
 
 
 
         if (best >= beta)
 
         {
 
            if (staticValueAvailable == FALSE)
 
            {
 
               UINT8 hashentryFlag = HASHVALUE_EVAL;
 
 
 
               setHashentry(getSharedHashtable(), position->hashKey,
 
                            calcHashtableValue(best, ply),
 
                            0, packedMove(NO_MOVE), hashentryFlag,
 
                            (INT16) getStaticValue(variation, ply));
 
            }
 
 
 
            return best;
 
         }
 
      }
 
 
 
      currentValue = best;
 
   }
 
 
 
   if (ply >= MAX_DEPTH)
 
   {
 
                      variation->pawnHashtable,
 
                      variation->kingsafetyHashtable) != FALSE);
 
 
 
      return getStaticValue(variation, ply);
 
   }
 
 
 
   if (alpha < VALUE_MATED_HERE && inCheck == FALSE)
 
   {
 
      alpha = VALUE_MATED_HERE;
 
 
 
      if (alpha >= beta)
 
      {
 
         return VALUE_MATED_HERE;
 
      }
 
   }
 
 
 
   if (beta > VALUE_MATE_HERE)
 
   {
 
      beta = VALUE_MATE_HERE;
 
 
 
      if (beta <= alpha)
 
      {
 
         return VALUE_MATE_HERE;
 
      }
 
   }
 
 
 
   initQuiescenceMovelist(&movelist, &variation->singlePosition,
 
                          &variation->plyInfo[ply],
 
                          &variation->historyValue[0],
 
                          hashmove, restDepth, inCheck);
 
   initializePlyInfo(variation);
 
 
 
   while ((currentMove = getNextMove(&movelist)) != NO_MOVE)
 
   {
 
      int value, newDepth =
 
         (inCheck ? restDepth : restDepth - DEPTH_RESOLUTION);
 
      int optValue = currentValue + 43 +
 
         maxPieceValue[position->piece[getToSquare(currentMove)]];
 
      const Square toSquare = getToSquare(currentMove);
 
 
 
      if (pvNode == FALSE && inCheck == FALSE && optValue < alpha &&
 
          optValue > VALUE_ALMOST_MATED &&
 
          movesAreEqual(currentMove, hashmove) == FALSE &&
 
          getNewPiece(currentMove) != (Piece) QUEEN &&
 
          numberOfNonPawnPieces(position, position->activeColor) > 1 &&
 
          (pieceType(position->piece[getFromSquare(currentMove)]) != PAWN ||
 
           colorRank(position->activeColor, toSquare) != RANK_7))
 
      {
 
         const bool enPassant = (bool)
 
            (toSquare == position->enPassantSquare &&
 
             pieceType(position->piece[getFromSquare(currentMove)]) == PAWN);
 
 
 
         if (getNewPiece(currentMove) != NO_PIECE)
 
         {
 
            optValue +=
 
               maxPieceValue[getNewPiece(currentMove)] - basicValue[PAWN];
 
         }
 
 
 
         if (enPassant)
 
         {
 
            optValue += maxPieceValue[PAWN];
 
         }
 
 
 
         if (optValue < alpha && moveIsCheck(currentMove, position) == FALSE)
 
         {
 
            best = max(best, optValue);
 
 
 
            continue;
 
         }
 
      }
 
 
 
      assert(moveIsPseudoLegal
(position
, currentMove
));  
 
 
      if (makeMoveFast(variation, currentMove) != 0 ||
 
          passiveKingIsSafe(&variation->singlePosition) == FALSE)
 
      {
 
         unmakeLastMove(variation);
 
 
 
         continue;
 
      }
 
 
 
      variation->plyInfo[ply].currentMoveIsCheck =
 
         activeKingIsSafe(&variation->singlePosition) == FALSE;
 
 
 
      assert(position
->piece
[getToSquare
(currentMove
)] != NO_PIECE 
||  
             (getToSquare(currentMove) == position->enPassantSquare &&
 
              position->piece[getFromSquare(currentMove)] ==
 
              (PAWN | position->activeColor)) ||
 
             getNewPiece(currentMove) != NO_PIECE ||
 
             inCheck || variation->plyInfo[ply].currentMoveIsCheck);
 
 
 
             basicValue[position->piece[getFromSquare(currentMove)]] <=
 
             basicValue[position->piece[getToSquare(currentMove)]] ||
 
             seeMove(position, currentMove) >= 0);
 
 
 
      value = -searchBestQuiescence(variation, -beta, -alpha, ply + 1,
 
                                    newDepth, &bestReply, pvNode);
 
 
 
      unmakeLastMove(variation);
 
 
 
      if (variation->searchStatus != SEARCH_STATUS_RUNNING &&
 
          variation->iteration > 1)
 
      {
 
         return 0;
 
      }
 
 
 
      if (value > best)
 
      {
 
         best = value;
 
 
 
         if (best > alpha)
 
         {
 
            alpha = best;
 
            *bestMove = currentMove;
 
 
 
            if (pvNode)
 
            {
 
               appendMoveToPv(&(variation->plyInfo[ply].pv),
 
                              &(variation->plyInfo[ply - 1].pv), currentMove);
 
            }
 
 
 
            if (best >= beta)
 
            {
 
               break;
 
            }
 
         }
 
      }
 
   }
 
 
 
   if (best == VALUE_MATED)
 
   {
 
      /* mate */
 
 
 
 
 
      best = VALUE_MATED + ply;
 
   }
 
 
 
   /* Store the value in the transposition table. */
 
   /* ------------------------------------------- */
 
   if (best >= beta)
 
   {
 
      hashentryFlag = HASHVALUE_LOWER_LIMIT;
 
   }
 
   else
 
   {
 
      hashentryFlag = (best > oldAlpha && pvNode ?
 
                       HASHVALUE_EXACT : HASHVALUE_UPPER_LIMIT);
 
   }
 
 
 
   setHashentry(getSharedHashtable(), position->hashKey,
 
                calcHashtableValue(best, ply),
 
                hashDepth, packedMove(*bestMove), hashentryFlag,
 
                (INT16) getStaticValue(variation, ply));
 
 
 
   return best;
 
}
 
 
 
static bool moveIsCastling(const Move move, const Position * position)
 
{
 
   return (bool) (pieceType(position->piece[getFromSquare(move)]) == KING &&
 
                  distance(getFromSquare(move), getToSquare(move)) == 2);
 
}
 
 
 
static bool isPassedPawnMove(const Square pawnSquare,
 
                             const Square targetSquare,
 
                             const Position * position)
 
{
 
   const Piece piece = position->piece[pawnSquare];
 
 
 
   if (pieceType(piece) == PAWN)
 
   {
 
      return pawnIsPassed(position, targetSquare, pieceColor(piece));
 
   }
 
   else
 
   {
 
      return FALSE;
 
   }
 
}
 
 
 
static int getSingleMoveExtensionDepth(const bool pvNode)
 
{
 
   return (pvNode ? 6 : 8) * DEPTH_RESOLUTION;
 
}
 
 
 
static int searchBest(Variation * variation, int alpha, int beta,
 
                      const int ply, const int restDepth, const bool pvNode,
 
                      const bool cutNode, Move * bestMove, Move excludeMove,
 
                      const bool tryEarlyPrunings)
 
{
 
   Position *position = &variation->singlePosition;
 
   int best = VALUE_MATED;
 
   const int VALUE_MATE_HERE = -VALUE_MATED - ply + 1;
 
   const int VALUE_MATED_HERE = VALUE_MATED + ply;
 
   const int numPieces =
 
      numberOfNonPawnPieces(position, position->activeColor);
 
   Movelist movelist;
 
   UINT8 hashentryFlag;
 
   int i, historyLimit, numMovesPlayed = 0;
 
   Move hashmove = NO_MOVE;
 
   Hashentry *bestTableHit = 0;
 
   Move currentMove, bestReply;
 
   const bool inCheck = variation->plyInfo[ply - 1].currentMoveIsCheck;
 
   const int rdBasic = restDepth / DEPTH_RESOLUTION;
 
   bool singularExtensionNode = FALSE;
 
   int hashEntryValue = 0;
 
   const UINT64 hashKey = position->hashKey;
 
   const bool cutsAreAllowed 
= (bool
) (abs(beta
) < -(VALUE_ALMOST_MATED
));  
   int variationType = (ply < 2 ? 1 : 0);
 
   int quietMoveIndex[MAX_MOVES_PER_POSITION], quietMoveCount = 0;
 
   int deferCount = 0;
 
 
 
   *bestMove = NO_MOVE;
 
   variation->plyInfo[ply].quietMove = FALSE;   /* avoid subsequent gain updates */
 
   variation->plyInfo[ply].isHashMove = FALSE;
 
   variation->plyInfo[ply].pv.length = 0;
 
   variation->plyInfo[ply].pv.move[0] = NO_MOVE;
 
   movelist.positionalGain = &(variation->positionalGain[0]);
 
 
 
   if (ply + 1 > variation->selDepth)
 
   {
 
      variation->selDepth = ply + 1;
 
   }
 
 
 
   assert(alpha 
>= VALUE_MATED 
&& alpha 
<= -VALUE_MATED
);  
   assert(beta 
>= VALUE_MATED 
&& beta 
<= -VALUE_MATED
);  
   assert(ply 
> 0 && ply 
< MAX_DEPTH
);  
   assert(passiveKingIsSafe
(position
));  
   assert((inCheck 
!= FALSE
) == (activeKingIsSafe
(position
) == FALSE
));  
 
 
   /* Check for a draw according to the 50-move-rule */
 
   /* ---------------------------------------------- */
 
   if (position->halfMoveClock > 100)
 
   {
 
      return variation->drawScore[position->activeColor];
 
   }
 
 
 
   /* Check for a draw by repetition. */
 
   /* ------------------------------- */
 
   historyLimit = POSITION_HISTORY_OFFSET + variation->ply -
 
      position->halfMoveClock;
 
 
 
 
 
   for (i = POSITION_HISTORY_OFFSET + variation->ply - 4;
 
        i >= historyLimit; i -= 2)
 
   {
 
      if (position->hashKey == variation->positionHistory[i])
 
      {
 
         return variation->drawScore[position->activeColor];
 
      }
 
   }
 
 
 
   if (restDepth < DEPTH_RESOLUTION)
 
   {                            /* 63% */
 
      int qsValue;
 
 
 
      qsValue =
 
         searchBestQuiescence(variation, alpha, beta, ply, 0, bestMove,
 
                              pvNode);
 
 
 
      if (inCheck == FALSE &&
 
          variation->plyInfo[ply].staticValueAvailable != FALSE)
 
      {
 
         updateGains(variation, ply);
 
      }
 
 
 
      return qsValue;
 
   }
 
 
 
   variation->nodes++;
 
   checkTerminationConditions(variation);
 
 
 
   if (variation->searchStatus != SEARCH_STATUS_RUNNING &&
 
       variation->iteration > 1)
 
   {
 
      return 0;
 
   }
 
 
 
#ifdef INCLUDE_TABLEBASE_ACCESS
 
   /* Probe the tablebases in case of reduced material */
 
   /* ------------------------------------------------ */
 
   if (ply >= 2 && (pvNode || restDepth >= 10 * DEPTH_RESOLUTION) &&
 
       position->numberOfPieces[WHITE] +
 
       position->numberOfPieces[BLACK] <= 6 &&
 
       excludeMove == NO_MOVE && tbAvailable != FALSE)
 
   {
 
      int tbValue;
 
 
 
      tbValue = probeTablebase(position);
 
 
 
      if (tbValue != TABLEBASE_ERROR)
 
      {
 
         variation->tbHits++;
 
 
 
         if (tbValue == 0)
 
         {
 
            return variation->drawScore[position->activeColor];
 
         }
 
 
 
         return (tbValue > 0 ? tbValue - ply : tbValue + ply);
 
      }
 
   }
 
#endif
 
 
 
   /* Probe the transposition table */
 
   /* ----------------------------- */
 
   if (excludeMove == NO_MOVE)
 
   {
 
      int hashValue;
 
 
 
      if (positionIsWellKnown(variation, position, hashKey,
 
                              &bestTableHit, ply, alpha, beta,
 
                              restDepth + HASH_DEPTH_OFFSET, pvNode,
 
                              inCheck == FALSE, &hashmove, excludeMove,
 
                              &hashValue))
 
      {
 
         *bestMove = hashmove;
 
 
 
         if (hashValue >= beta && *bestMove != NO_MOVE &&
 
             moveIsQuiet(*bestMove, position))
 
         {
 
            Move killerMove = *bestMove;
 
            const Piece movingPiece =
 
               position->piece[getFromSquare(killerMove)];
 
 
 
            setMoveValue(&killerMove, movingPiece);
 
            registerKillerMove(&variation->plyInfo[ply], killerMove);
 
         }
 
 
 
         return hashValue;
 
      }
 
   }
 
 
 
   if (inCheck == FALSE)
 
   {
 
      updateGains(variation, ply);
 
   }
 
 
 
   if (ply >= 2)
 
   {
 
      if (variation->plyInfo[ply].staticValue >=
 
          variation->plyInfo[ply - 2].staticValue ||
 
          variation->plyInfo[ply].staticValueAvailable == FALSE ||
 
          variation->plyInfo[ply - 2].staticValueAvailable == FALSE)
 
      {
 
         variationType = 1;
 
      }
 
   }
 
 
 
   if (ply >= MAX_DEPTH)
 
   {
 
      return getStaticValue(variation, ply);
 
   }
 
 
 
   if (alpha < VALUE_MATED_HERE && inCheck == FALSE)
 
   {
 
      alpha = VALUE_MATED_HERE;
 
 
 
      if (alpha >= beta)
 
      {
 
         return VALUE_MATED_HERE;
 
      }
 
   }
 
 
 
   if (beta > VALUE_MATE_HERE)
 
   {
 
      beta = VALUE_MATE_HERE;
 
 
 
      if (beta <= alpha)
 
      {
 
         return VALUE_MATE_HERE;
 
      }
 
   }
 
 
 
   initializePlyInfo(variation);
 
 
 
   if (tryEarlyPrunings == FALSE)
 
   {
 
      goto checkAvailableMoves;
 
   }
 
 
 
   /* Razoring */
 
   if (pvNode == FALSE && restDepth < 4 * DEPTH_RESOLUTION &&
 
       inCheck == FALSE && hashmove == NO_MOVE && cutsAreAllowed &&
 
       hasDangerousPawns(position, position->activeColor) == FALSE)
 
   {
 
      const int limit = alpha - (204 + 22 * restDepth);
 
 
 
      if (getRefinedStaticValue(variation, ply) < limit)
 
      {
 
         const int qsValue =
 
            searchBestQuiescence(variation, limit - 1, limit, ply, 0,
 
                                 bestMove, pvNode);
 
 
 
         if (qsValue < limit)
 
         {
 
            return qsValue;
 
         }
 
      }
 
   }
 
 
 
   /* Static nullmove pruning */
 
   if (pvNode == FALSE && restDepth < 4 * DEPTH_RESOLUTION &&
 
       inCheck == FALSE && cutsAreAllowed && excludeMove == NO_MOVE &&
 
       numPieces >= 2 &&
 
       !hasDangerousPawns(position, opponent(position->activeColor)))
 
   {
 
      const int referenceValue =
 
         getRefinedStaticValue(variation, ply) - (40 + 41 * restDepth);
 
 
 
      if (referenceValue >= beta)
 
      {
 
         return referenceValue;
 
      }
 
   }
 
 
 
   /* Nullmove pruning with verification */
 
   if (restDepth >= 2 * DEPTH_RESOLUTION && inCheck == FALSE &&
 
       pvNode == FALSE && cutsAreAllowed && excludeMove == NO_MOVE &&
 
       numPieces >= 2 && getRefinedStaticValue(variation, ply) >= beta)
 
   {                            /* 16-32% */
 
      const int diff = getRefinedStaticValue(variation, ply) - beta;
 
      const int additionalReduction = min(diff / 110, 3) * DEPTH_RESOLUTION;
 
      const int newDepth =
 
         restDepth - 3 * DEPTH_RESOLUTION - restDepth / 4 -
 
         additionalReduction;
 
      const int verificationDepth =
 
         (numPieces >= 3 ? 12 : 6) * DEPTH_RESOLUTION;
 
      int nullValue;
 
 
 
                      variation->pawnHashtable,
 
                      variation->kingsafetyHashtable) != FALSE);
 
 
 
      makeMoveFast(variation, NULLMOVE);
 
      variation->plyInfo[ply].currentMoveIsCheck = FALSE;
 
      nullValue = -searchBest(variation, -beta, -beta + 1, ply + 1,
 
                              newDepth, pvNode, !cutNode, &bestReply,
 
                              NO_MOVE, FALSE);
 
      unmakeLastMove(variation);
 
 
 
      if (nullValue >= VALUE_MATE_HERE)
 
      {
 
         nullValue = beta;
 
      }
 
 
 
      if (nullValue >= beta && newDepth >= DEPTH_RESOLUTION &&
 
          restDepth >= verificationDepth)
 
      {                         /* 2% */
 
         const int noNullValue = searchBest(variation, alpha, beta, ply,
 
                                            newDepth,
 
                                            FALSE, FALSE, &bestReply,
 
                                            NULLMOVE, FALSE);
 
 
 
         if (noNullValue >= beta)
 
         {                      /* 99% */
 
            best = nullValue;
 
 
 
            goto storeResult;
 
         }
 
      }
 
      else if (nullValue >= beta)
 
      {                         /* 70% */
 
         if (numPieces >= 3)
 
         {
 
            best = nullValue;
 
 
 
            goto storeResult;
 
         }
 
         else
 
         {
 
            return nullValue;
 
         }
 
      }
 
   }
 
 
 
   /* Try to find a quick refutation of the opponent's previous move */
 
   if (pvNode == FALSE && cutsAreAllowed &&
 
       restDepth >= 5 * DEPTH_RESOLUTION && excludeMove == NO_MOVE &&
 
       inCheck == FALSE)
 
   {
 
      const int limit = beta + 192;
 
      const int staticValue = getRefinedStaticValue(variation, ply);
 
      const Move qrHashmove = (hashmove != NO_MOVE &&
 
                               position->piece[getToSquare(hashmove)] !=
 
                               NO_PIECE ? hashmove : NO_MOVE);
 
 
 
      initCaptureMovelist(&movelist, &variation->singlePosition,
 
                          &variation->plyInfo[ply],
 
                          &variation->historyValue[0], qrHashmove, inCheck);
 
 
 
      while ((currentMove = getNextMove(&movelist)) != NO_MOVE)
 
      {
 
         const Square toSquare = getToSquare(currentMove);
 
         const Piece capturedPiece = position->piece[toSquare];
 
         int moveValue;
 
 
 
         if (staticValue + maxPieceValue[capturedPiece] < limit - 38)
 
         {
 
            continue;
 
         }
 
 
 
         /* Execute the current move and check if it is legal. */
 
         /* -------------------------------------------------- */
 
         if (makeMoveFast(variation, currentMove) != 0 ||
 
             passiveKingIsSafe(&variation->singlePosition) == FALSE)
 
         {
 
            unmakeLastMove(variation);
 
 
 
            continue;
 
         }
 
 
 
         variation->plyInfo[ply].currentMoveIsCheck =
 
            activeKingIsSafe(&variation->singlePosition) == FALSE;
 
         variation->plyInfo[ply].indexCurrentMove =
 
            historyIndex(currentMove, position);
 
         variation->plyInfo[ply].quietMove = FALSE;
 
         variation->plyInfo[ply].isHashMove = FALSE;
 
 
 
         moveValue = -searchBest(variation, -limit, -limit + 1, ply + 1,
 
                                 restDepth - 4 * DEPTH_RESOLUTION, FALSE,
 
                                 !cutNode, &bestReply, NO_MOVE, TRUE);
 
 
 
         unmakeLastMove(variation);
 
 
 
         if (moveValue >= limit)
 
         {
 
            best = moveValue;
 
            *bestMove = currentMove;
 
 
 
            goto storeResult;
 
         }
 
      }
 
   }
 
 
 
 checkAvailableMoves:
 
 
 
   /* Internal iterative deepening. */
 
   /* ----------------------------- */
 
   if (hashmove == NO_MOVE &&
 
       restDepth >= (pvNode ? 3 : 7) * DEPTH_RESOLUTION &&
 
       (pvNode || (inCheck == FALSE &&
 
                   getRefinedStaticValue(variation, ply) >= beta - 100)))
 
   {
 
      const Move excludeHere =
 
         (excludeMove != NO_MOVE ? excludeMove : NULLMOVE);
 
      const int searchDepth =
 
         (pvNode ? restDepth - 2 * DEPTH_RESOLUTION : restDepth / 2);
 
 
 
      searchBest(variation, alpha, beta, ply, searchDepth, pvNode,
 
                 TRUE, &bestReply, excludeHere, FALSE);
 
 
 
      if (moveIsPseudoLegal(position, bestReply))
 
      {
 
         hashmove = bestReply;
 
      }
 
 
 
      if (hashmove != NO_MOVE && excludeMove == NO_MOVE &&
 
          restDepth >= getSingleMoveExtensionDepth(pvNode))
 
      {
 
         Hashentry *tableHit = getHashentry(getSharedHashtable(),
 
                                            variation->singlePosition.
 
                                            hashKey);
 
 
 
         if (tableHit != 0)
 
         {
 
            bestTableHit = tableHit;
 
         }
 
      }
 
   }
 
 
 
   /* Check if the conditions for a singular extension are met */
 
   if (bestTableHit != 0 && excludeMove == NO_MOVE && hashmove != NO_MOVE)
 
   {
 
      const int singleMoveExtensionDepth =
 
         getSingleMoveExtensionDepth(pvNode);
 
      /* const int singleMoveExtensionDepth = 8 * DEPTH_RESOLUTION; */
 
      const int importance =
 
         getHashentryImportance(bestTableHit) - HASH_DEPTH_OFFSET;
 
      const int flag = getHashentryFlag(bestTableHit);
 
 
 
      if (restDepth >= singleMoveExtensionDepth &&
 
          importance >= restDepth - 3 * DEPTH_RESOLUTION &&
 
          flag != HASHVALUE_UPPER_LIMIT)
 
      {
 
         singularExtensionNode = TRUE;
 
         hashEntryValue =
 
            calcEffectiveValue(getHashentryValue(bestTableHit), ply);
 
      }
 
   }
 
 
 
   if (ply >= 1)
 
   {
 
      const int moveIndex = variation->plyInfo[ply - 1].indexCurrentMove;
 
 
 
      variation->plyInfo[ply].killerMove3 =
 
         variation->counterMove1[moveIndex];
 
      variation->plyInfo[ply].killerMove4 =
 
         variation->counterMove2[moveIndex];
 
   }
 
   else
 
   {
 
      variation->plyInfo[ply].killerMove3 = NO_MOVE;
 
      variation->plyInfo[ply].killerMove4 = NO_MOVE;
 
   }
 
 
 
   if (ply >= 2)
 
   {
 
      const int moveIndex = variation->plyInfo[ply - 2].indexCurrentMove;
 
 
 
      variation->plyInfo[ply].killerMove5 =
 
         variation->followupMove1[moveIndex];
 
      variation->plyInfo[ply].killerMove6 =
 
         variation->followupMove2[moveIndex];
 
   }
 
   else
 
   {
 
      variation->plyInfo[ply].killerMove5 = NO_MOVE;
 
      variation->plyInfo[ply].killerMove6 = NO_MOVE;
 
   }
 
 
 
   initStandardMovelist(&movelist, &variation->singlePosition,
 
                        &variation->plyInfo[ply],
 
                        &variation->historyValue[0], hashmove, inCheck);
 
 
 
   /* Ensure that a static value for this ply is available. */
 
   getStaticValue(variation, ply);
 
 
 
   /* Loop through all moves in this node. */
 
   /* ------------------------------------ */
 
   while ((currentMove = getNextMove(&movelist)) != NO_MOVE)
 
   {
 
      const int stage = moveGenerationStage[movelist.currentStage];
 
      int value = 0, newDepth;
 
      int extension = 0;
 
      const int moveIndex = min(63, numMovesPlayed);
 
      const int depthIndex = min(63, rdBasic);
 
      const int gain =
 
         variation->positionalGain[historyIndex(currentMove, position)];
 
      const int reduction =
 
         (pvNode ? quietPvMoveReduction[depthIndex][moveIndex] :
 
          quietMoveReduction[variationType][depthIndex][moveIndex] +
 
          (cutNode /* || gain < 0 */ ? DEPTH_RESOLUTION : 0));
 
      bool check;
 
      const bool quietMove = moveIsQuiet(currentMove, position);
 
      const Square toSquare = getToSquare(currentMove);
 
      const Piece capturedPiece = position->piece[toSquare];
 
      bool reduce = FALSE, nodeWasBlocked = FALSE;
 
 
 
      if (variation->searchStatus != SEARCH_STATUS_RUNNING &&
 
          variation->iteration > 1)
 
      {
 
         return 0;
 
      }
 
 
 
      if (excludeMove != NO_MOVE && movesAreEqual(currentMove, excludeMove))
 
      {
 
         assert(excludeMove 
!= NULLMOVE
);  
 
 
         continue;              /* exclude excludeMove */
 
      }
 
 
 
      variation->plyInfo[ply].indexCurrentMove =
 
         historyIndex(currentMove, position);
 
      variation->plyInfo[ply].quietMove = quietMove;
 
      variation->plyInfo[ply].isHashMove =
 
         movesAreEqual(currentMove, hashmove);
 
 
 
      assert(moveIsPseudoLegal
(position
, currentMove
));  
      assert(hashmove 
== NO_MOVE 
|| numMovesPlayed 
> 0 ||  
             movesAreEqual(currentMove, hashmove));
 
 
 
      /* Optimistic futility cuts */
 
      if (pvNode == FALSE && inCheck == FALSE && quietMove != FALSE &&
 
          !isPassedPawnMove(getFromSquare(currentMove), toSquare, position) &&
 
          !moveIsCastling(currentMove, position) &&
 
          best > VALUE_ALMOST_MATED && cutsAreAllowed && restDepth < 32)
 
      {
 
         bool moveIsRelevant = FALSE;
 
         const int predictedDepth = restDepth - reduction;
 
 
 
         if (numMovesPlayed >= quietMoveCountLimit[variationType][restDepth])
 
         {
 
            if (simpleMoveIsCheck(position, currentMove))
 
            {
 
               moveIsRelevant = TRUE;
 
            }
 
            else
 
            {
 
               continue;
 
            }
 
         }
 
 
 
         if (moveIsRelevant == FALSE &&
 
             predictedDepth <= NUM_FUTILITY_MARGIN_VALUES)
 
         {
 
            const int futLimit =
 
               getRefinedStaticValue(variation, ply) + gain +
 
               futilityMargin[predictedDepth];
 
 
 
            if (futLimit < beta)
 
            {
 
               if (simpleMoveIsCheck(position, currentMove))
 
               {
 
                  moveIsRelevant = TRUE;
 
               }
 
               else
 
               {
 
                  if (futLimit > best)
 
                  {
 
                     best = futLimit;
 
                  }
 
 
 
                  continue;
 
               }
 
            }
 
         }
 
 
 
         if (moveIsRelevant == FALSE &&
 
             predictedDepth < 4 * DEPTH_RESOLUTION &&
 
             seeMove(position, currentMove) < 0 &&
 
             simpleMoveIsCheck(position, currentMove) == FALSE)
 
         {
 
            continue;
 
         }
 
      }
 
 
 
      /* Execute the current move and check if it is legal. */
 
      /* -------------------------------------------------- */
 
      if (makeMoveFast(variation, currentMove) != 0 ||
 
          passiveKingIsSafe(&variation->singlePosition) == FALSE)
 
      {
 
         unmakeLastMove(variation);
 
 
 
         continue;
 
      }
 
 
 
      if (numMovesPlayed > 0 && inCheck == FALSE &&
 
          stage == MGS_REST && deferCount < 10 &&
 
          checkNodeExclusion(restDepth))
 
      {
 
         if (nodeIsInUse(position->hashKey, restDepth))
 
         {
 
            deferMove(&movelist, currentMove);
 
            deferCount++;
 
            unmakeLastMove(variation);
 
            continue;
 
         }
 
         else
 
         {
 
            nodeWasBlocked = setNodeUsage(position->hashKey, restDepth);
 
         }
 
      }
 
 
 
      /* Check the conditions for search extensions and finally */
 
      /* calculate the rest depth for the next ply.             */
 
      /* ------------------------------------------------------ */
 
      variation->plyInfo[ply].currentMoveIsCheck = check =
 
         activeKingIsSafe(&variation->singlePosition) == FALSE;
 
 
 
      if (check)
 
      {
 
         unmakeLastMove(variation);
 
 
 
         if (seeMove(position, currentMove) >= 0)
 
         {
 
            extension = DEPTH_RESOLUTION;
 
         }
 
 
 
         makeMoveFast(variation, currentMove);
 
      }
 
 
 
      if (pvNode)
 
      {
 
         if (pieceType(position->piece[toSquare]) == PAWN &&
 
             pawnIsPassed(position, toSquare,
 
                          opponent(position->activeColor)))
 
         {
 
            extension = DEPTH_RESOLUTION;
 
         }
 
         else if (capturedPiece != NO_PIECE &&
 
                  pieceType(capturedPiece) != PAWN &&
 
                  numberOfNonPawnPieces(position, WHITE) ==
 
                  numberOfNonPawnPieces(position, BLACK))
 
         {
 
            extension = DEPTH_RESOLUTION;
 
         }
 
      }
 
 
 
      if (singularExtensionNode != FALSE &&
 
          extension < DEPTH_RESOLUTION &&
 
          movesAreEqual(currentMove, hashmove))
 
      {
 
         const int limitValue = hashEntryValue - (266 * restDepth) / 256;
 
 
 
         assert(excludeMove 
== NO_MOVE
);  
 
 
         if (limitValue > VALUE_ALMOST_MATED &&
 
             limitValue < -VALUE_ALMOST_MATED)
 
         {
 
            int excludeValue;
 
            PlyInfo pi = variation->plyInfo[ply];
 
 
 
            unmakeLastMove(variation);
 
            excludeValue =
 
               searchBest(variation, limitValue - 1, limitValue, ply,
 
                          restDepth / 2, FALSE, cutNode, &bestReply,
 
                          hashmove, FALSE);
 
            makeMoveFast(variation, currentMove);
 
            variation->plyInfo[ply] = pi;
 
 
 
            if (excludeValue < limitValue)
 
            {
 
               extension = DEPTH_RESOLUTION;
 
            }
 
         }
 
      }
 
 
 
      newDepth = restDepth - DEPTH_RESOLUTION + extension;
 
 
 
      /* History pruning */
 
      /* --------------- */
 
      if (inCheck == FALSE && extension == 0 &&
 
          restDepth >= 3 * DEPTH_RESOLUTION && quietMove != FALSE &&
 
          stage != MGS_GOOD_CAPTURES_AND_PROMOTIONS &&
 
          movesAreEqual(currentMove,
 
                        variation->plyInfo[ply].killerMove1) == FALSE &&
 
          movesAreEqual(currentMove,
 
                        variation->plyInfo[ply].killerMove2) == FALSE &&
 
          isPassedPawnMove(toSquare, toSquare, position) == FALSE)
 
      {
 
         reduce = TRUE;
 
      }
 
 
 
      /* Recursive search */
 
      /* ---------------- */
 
      if (pvNode && numMovesPlayed == 0)
 
      {
 
         value = -searchBest(variation, -beta, -alpha, ply + 1,
 
                             newDepth, pvNode, FALSE, &bestReply, NO_MOVE,
 
                             TRUE);
 
      }
 
      else
 
      {
 
         const Move cm1 = variation->plyInfo[ply].killerMove3;
 
         const Move cm2 = variation->plyInfo[ply].killerMove4;
 
         const bool counterMove = movesAreEqual(currentMove, cm1) ||
 
            movesAreEqual(currentMove, cm2);
 
         const int effectiveReduction =
 
            max(0, reduction - (counterMove ? DEPTH_RESOLUTION : 0));
 
         const int minRestDepth = (pvNode ? DEPTH_RESOLUTION : 0);
 
         const int reducedRestDepth =
 
            max(minRestDepth, newDepth - effectiveReduction);
 
 
 
         bool fullDepthSearch = TRUE;
 
 
 
         if (reduce && effectiveReduction > 0)
 
         {
 
            value = -searchBest(variation, -alpha - 1, -alpha, ply + 1,
 
                                reducedRestDepth, FALSE, TRUE,
 
                                &bestReply, NO_MOVE, TRUE);
 
 
 
            if (value > alpha && effectiveReduction >= 4 * DEPTH_RESOLUTION &&
 
                stage != MGS_KILLER_MOVES)
 
            {
 
               const int limitedRestDepth =
 
                  max(DEPTH_RESOLUTION, newDepth - 2 * DEPTH_RESOLUTION);
 
 
 
               value = -searchBest(variation, -alpha - 1, -alpha, ply + 1,
 
                                   limitedRestDepth, FALSE,
 
                                   TRUE, &bestReply, NO_MOVE, TRUE);
 
            }
 
 
 
            fullDepthSearch = (bool) (value > alpha);
 
         }
 
 
 
         if (fullDepthSearch)
 
         {
 
            value = -searchBest(variation, -alpha - 1, -alpha, ply + 1,
 
                                newDepth, FALSE, !cutNode, &bestReply,
 
                                NO_MOVE, TRUE);
 
 
 
            if (pvNode && value > alpha && value < beta)
 
            {                   /* 2% */
 
               value = -searchBest(variation, -beta, -alpha, ply + 1,
 
                                   newDepth, TRUE, FALSE, &bestReply,
 
                                   NO_MOVE, TRUE);
 
            }
 
         }
 
      }
 
 
 
      assert(value 
>= VALUE_MATED 
&& value 
<= -VALUE_MATED
);  
 
 
      if (nodeWasBlocked)
 
      {
 
         resetNodeUsage(position->hashKey, restDepth);
 
      }
 
 
 
      unmakeLastMove(variation);
 
      numMovesPlayed++;
 
 
 
      if (quietMove && inCheck == FALSE)
 
      {
 
         quietMoveIndex[quietMoveCount++] =
 
            historyIndex(currentMove, position);
 
      }
 
 
 
      /* Calculate the parameters controlling the search tree. */
 
      /* ----------------------------------------------------- */
 
      if (value > best)
 
      {                         /* 32% */
 
         best = value;
 
 
 
         if (best > alpha)
 
         {                      /* 63% */
 
            alpha = best;
 
            *bestMove = currentMove;
 
 
 
            if (pvNode)
 
            {
 
               appendMoveToPv(&(variation->plyInfo[ply].pv),
 
                              &(variation->plyInfo[ply - 1].pv), currentMove);
 
            }
 
 
 
            if (best >= beta)
 
            {                   /* 99% */
 
               break;
 
            }
 
         }
 
      }
 
   }
 
 
 
   /* No legal move was found. Check if it's mate or stalemate. */
 
   /* --------------------------------------------------------- */
 
   if (best == VALUE_MATED)
 
   {
 
      if (excludeMove != NO_MOVE && excludeMove != NULLMOVE)
 
      {
 
         return beta - 1;
 
      }
 
 
 
      if (inCheck)
 
      {
 
         /* mate */
 
 
 
         best = VALUE_MATED + ply;
 
      }
 
      else
 
      {
 
         /* stalemate */
 
 
 
         best = variation->drawScore[position->activeColor];
 
      }
 
   }
 
 
 
   /* Calculate the parameters controlling the move ordering. */
 
   /* ------------------------------------------------------- */
 
   if (*bestMove != NO_MOVE && moveIsQuiet(*bestMove, position) &&
 
       inCheck == FALSE &&
 
       (excludeMove == NO_MOVE || excludeMove == NULLMOVE))
 
   {
 
      Move killerMove = *bestMove;
 
      const Piece movingPiece = position->piece[getFromSquare(killerMove)];
 
      const int index = historyIndex(*bestMove, position);
 
      const UINT16 bestMoveValue = (UINT16)
 
         (variation->historyValue[index] +
 
          ((HISTORY_MAX - variation->historyValue[index]) * restDepth) / 256);
 
      int i;
 
 
 
      for (i = 0; i < quietMoveCount; i++)
 
      {
 
         const int historyIdx = quietMoveIndex[i];
 
 
 
         variation->historyValue[historyIdx] = (UINT16)
 
            (variation->historyValue[historyIdx] -
 
             (variation->historyValue[historyIdx] * restDepth) / 128);
 
         assert(variation
->historyValue
[historyIdx
] <= HISTORY_MAX
);  
      }
 
 
 
      variation->historyValue[index] = bestMoveValue;
 
      assert(variation
->historyValue
[index
] <= HISTORY_MAX
);  
 
 
      setMoveValue(&killerMove, movingPiece);
 
      registerKillerMove(&variation->plyInfo[ply], killerMove);
 
      updateCounterMoves(variation, ply, killerMove);
 
 
 
      if (ply >= 2 && variation->plyInfo[ply - 1].isHashMove)
 
      {
 
         updateFollowupMoves(variation, ply, killerMove);
 
      }
 
   }
 
 
 
 storeResult:
 
 
 
   /* Store the value in the transposition table. */
 
   /* ------------------------------------------- */
 
   if ((excludeMove == NO_MOVE || excludeMove == NULLMOVE) &&
 
       variation->searchStatus == SEARCH_STATUS_RUNNING)
 
   {
 
      if (best >= beta)
 
      {
 
         hashentryFlag = HASHVALUE_LOWER_LIMIT;
 
      }
 
      else
 
      {
 
         hashentryFlag = (pvNode && *bestMove != NO_MOVE ?
 
                          HASHVALUE_EXACT : HASHVALUE_UPPER_LIMIT);
 
      }
 
 
 
      setHashentry(getSharedHashtable(), hashKey,
 
                   calcHashtableValue(best, ply),
 
                   (UINT8) (restDepth + HASH_DEPTH_OFFSET),
 
                   packedMove(*bestMove), hashentryFlag,
 
                   (INT16) getStaticValue(variation, ply));
 
 
 
#ifdef SEND_HASH_ENTRIES
 
      if (hashentryFlag == HASHVALUE_EXACT &&
 
          restDepth >= minPvHashEntrySendDepth &&
 
          ply <= maxPvHashEntrySendHeight)
 
      {
 
         const long timestamp = getTimestamp();
 
         const long elapsedTime = timestamp - variation->startTime;
 
         const long intervalTime = timestamp - variation->hashSendTimestamp;
 
 
 
         if (elapsedTime >= minPvHashEntrySendTime &&
 
             intervalTime >= pvHashEntriesSendInterval)
 
         {
 
            Hashentry entry =
 
               constructHashEntry(hashKey, calcHashtableValue(best, ply),
 
                                  (INT16) getStaticValue(variation, ply),
 
                                  (UINT8) (restDepth + HASH_DEPTH_OFFSET),
 
                                  packedMove(*bestMove), 0, hashentryFlag);
 
 
 
            sendHashentry(&entry);
 
            variation->hashSendTimestamp = timestamp;
 
         }
 
      }
 
#endif
 
   }
 
 
 
   return best;
 
}
 
 
 
void copyPvFromHashtable(Variation * variation, const int pvIndex,
 
                         PrincipalVariation * pv, const Move bestBaseMove)
 
{
 
   Move bestMove = NO_MOVE;
 
 
 
   if (pvIndex == 0)
 
   {
 
      bestMove = bestBaseMove;
 
   }
 
   else
 
   {
 
      Hashentry *tableHit = getHashentry(getSharedHashtable(),
 
                                         variation->singlePosition.hashKey);
 
 
 
      if (tableHit != NULL)
 
      {
 
         Move currentMove = (Move) getHashentryMove(tableHit);
 
 
 
         if (moveIsLegal(&variation->singlePosition, currentMove))
 
         {
 
            bestMove = (Move) getHashentryMove(tableHit);
 
         }
 
      }
 
   }
 
 
 
   if (bestMove != NO_MOVE && pvIndex < MAX_DEPTH)
 
   {
 
      pv->move[pvIndex] = (UINT16) bestMove;
 
      pv->move[pvIndex + 1] = (UINT16) NO_MOVE;
 
      pv->length = pvIndex + 1;
 
      makeMove(variation, bestMove);
 
      copyPvFromHashtable(variation, pvIndex + 1, pv, bestBaseMove);
 
      unmakeLastMove(variation);
 
   }
 
   else
 
   {
 
      pv->move[pvIndex] = (UINT16) NO_MOVE;
 
      pv->length = pvIndex;
 
   }
 
}
 
 
 
static void copyPvToHashtable(Variation * variation,
 
                              PrincipalVariation * pv, const int pvIndex)
 
{
 
   Move move = (Move) pv->move[pvIndex];
 
 
 
   if (pvIndex < pv->length && moveIsLegal(&variation->singlePosition, move))
 
   {
 
      UINT8 importance = (UINT8) HASH_DEPTH_OFFSET;
 
      bool entryExists = FALSE;
 
      Move bestMove = NO_MOVE;
 
      Hashentry *tableHit = getHashentry(getSharedHashtable(),
 
                                         variation->singlePosition.hashKey);
 
 
 
      if (tableHit != 0)
 
      {
 
         bestMove = (Move) getHashentryMove(tableHit);
 
         importance = max(importance, getHashentryImportance(tableHit));
 
 
 
         if (bestMove != NO_MOVE && movesAreEqual(bestMove, move))
 
         {
 
            entryExists = TRUE;
 
         }
 
      }
 
 
 
      if (entryExists == FALSE)
 
      {
 
         UINT8 hashentryFlag = HASHVALUE_LOWER_LIMIT;
 
 
 
         /* Store the move in the transposition table. */
 
         /* ------------------------------------------- */
 
 
 
         setHashentry(getSharedHashtable(),
 
                      variation->singlePosition.hashKey, VALUE_MATED,
 
                      importance, packedMove(move),
 
                      hashentryFlag, getEvalValue(variation));
 
      }
 
 
 
      makeMove(variation, move);
 
      copyPvToHashtable(variation, pv, pvIndex + 1);
 
      unmakeLastMove(variation);
 
   }
 
}
 
 
 
static void registerBestMove(Variation * variation, Move * move,
 
                             const int value)
 
{
 
   if (variation->searchStatus == SEARCH_STATUS_RUNNING)
 
   {
 
      setMoveValue(move, value);
 
      variation->bestBaseMove = *move;
 
 
 
      if (variation->iteration > 4 && variation->numberOfCurrentBaseMove > 1)
 
      {
 
         variation->bestMoveChangeCount += 256;
 
      }
 
   }
 
}
 
 
 
static int getBaseMoveValue(Variation * variation, const Move move,
 
                            const int alpha, const int beta,
 
                            const bool fullWindow)
 
{
 
   int depth = DEPTH_RESOLUTION * variation->iteration;
 
   int value;
 
   Move bestReply;
 
 
 
   assert(alpha 
<= -VALUE_MATED
);  
 
 
   makeMoveFast(variation, move);
 
 
 
   if (variation->nodes > GUI_NODE_COUNT_MIN &&
 
       variation->threadNumber == 0 && variation->handleSearchEvent != 0)
 
   {
 
      getGuiSearchMutex();
 
      variation->handleSearchEvent(SEARCHEVENT_NEW_BASEMOVE, variation);
 
      releaseGuiSearchMutex();
 
   }
 
 
 
   if (activeKingIsSafe(&variation->singlePosition) == FALSE)
 
   {
 
      variation->plyInfo[0].currentMoveIsCheck = TRUE;
 
      depth += DEPTH_RESOLUTION;
 
   }
 
   else
 
   {
 
      variation->plyInfo[0].currentMoveIsCheck = FALSE;
 
   }
 
 
 
   if (fullWindow)
 
   {
 
      value = -searchBest(variation, -beta, -alpha, 1,
 
                          depth - DEPTH_RESOLUTION, TRUE, FALSE,
 
                          &bestReply, NO_MOVE, TRUE);
 
   }
 
   else
 
   {
 
      value = -searchBest(variation, -alpha - 1, -alpha, 1,
 
                          depth - DEPTH_RESOLUTION, FALSE, TRUE,
 
                          &bestReply, NO_MOVE, TRUE);
 
 
 
      if (value > alpha)
 
      {
 
         value = -searchBest(variation, -beta, -alpha, 1,
 
                             depth - DEPTH_RESOLUTION, TRUE,
 
                             FALSE, &bestReply, NO_MOVE, TRUE);
 
      }
 
   }
 
 
 
   unmakeLastMove(variation);
 
 
 
   return value;
 
}
 
 
 
int getPvScoreType(int value, int alpha, int beta)
 
{
 
   if (value <= alpha)
 
   {
 
      return HASHVALUE_UPPER_LIMIT;
 
   }
 
   else if (value >= beta)
 
   {
 
      return HASHVALUE_LOWER_LIMIT;
 
   }
 
   else
 
   {
 
      return HASHVALUE_EXACT;
 
   }
 
}
 
 
 
static void sendPvInfo(Variation * variation, const int eventType)
 
{
 
   if ((variation->nodes > GUI_NODE_COUNT_MIN ||
 
        eventType == SEARCHEVENT_PLY_FINISHED) &&
 
       variation->threadNumber == 0 && variation->handleSearchEvent != 0)
 
   {
 
      int i;
 
 
 
      getGuiSearchMutex();
 
 
 
      for (i = 0; i < numPvs && variation->pv[i].length > 0; i++)
 
      {
 
         variation->pvId = i;
 
         variation->handleSearchEvent(eventType, variation);
 
      }
 
 
 
      releaseGuiSearchMutex();
 
   }
 
}
 
 
 
static void exploreBaseMoves(Variation * variation, Movelist * basemoves,
 
                             const int aspirationWindow)
 
{
 
   const int previousBest = variation->previousBest;
 
   const int ply = 0;
 
   Position *position = &variation->singlePosition;
 
   const bool fullWindow = (bool) (variation->iteration <= 3);
 
   int window = aspirationWindow, best;
 
   bool exactValueFound = FALSE;
 
   const int staticValue = getEvalValue(variation);
 
   int alpha =
 
      (fullWindow ? VALUE_MATED : max(VALUE_MATED, previousBest - window));
 
   int beta =
 
      (fullWindow ? -VALUE_MATED : min(-VALUE_MATED, previousBest + window));
 
 
 
   variation->failingLow = FALSE;
 
   variation->selDepth = variation->iteration;
 
   initializePvsOfVariation(variation);
 
 
 
   do
 
   {
 
      int pvCount = 0, worstValue = VALUE_MATED;
 
      const int numPvLimit = min(basemoves->numberOfMoves, numPvs);
 
 
 
      initializeMoveValues(basemoves);
 
      resetPvsOfVariation(variation);
 
      best = VALUE_MATED;
 
 
 
      for (variation->numberOfCurrentBaseMove = 1;
 
           variation->numberOfCurrentBaseMove <= basemoves->numberOfMoves;
 
           variation->numberOfCurrentBaseMove++)
 
      {
 
         int value;
 
         const int icm = variation->numberOfCurrentBaseMove - 1;
 
         const bool searchBelowBest = fullWindow || numPvs > 1;
 
         const bool pvNode = (bool) (icm == 0 || searchBelowBest);
 
         const int searchAlpha = (searchBelowBest ? alpha : max(alpha, best));
 
 
 
         resetPlyInfo(variation);
 
         variation->currentBaseMove = basemoves->moves[icm];
 
         variation->plyInfo[ply].indexCurrentMove =
 
            historyIndex(variation->currentBaseMove, position);
 
 
 
         value = getBaseMoveValue(variation, basemoves->moves[icm],
 
                                  searchAlpha, beta, pvNode);
 
 
 
         if (variation->searchStatus != SEARCH_STATUS_RUNNING &&
 
             variation->iteration > 1)
 
         {
 
            break;
 
         }
 
 
 
         if (icm == 0 || value > searchAlpha)
 
         {
 
            PrincipalVariation pv;
 
 
 
            setMoveValue(&basemoves->moves[icm], value);
 
            pv.score = value;
 
            pv.scoreType = getPvScoreType(value, searchAlpha, beta);
 
            appendMoveToPv(&(variation->plyInfo[0].pv), &pv,
 
                           basemoves->moves[icm]);
 
            addPvByScore(variation, &pv);
 
 
 
            if (++pvCount >= numPvLimit)
 
            {
 
               sendPvInfo(variation, SEARCHEVENT_NEW_PV);
 
            }
 
 
 
            if (icm == 0 || value > best)
 
            {
 
               registerBestMove(variation, &basemoves->moves[icm], value);
 
 
 
               if (value > best && value < beta)
 
               {
 
                  variation->completePv = pv;
 
               }
 
            }
 
         }
 
 
 
         if (value > best)
 
         {
 
            best = value;
 
 
 
            if (value >= beta && numPvs == 1)
 
            {
 
               break;
 
            }
 
         }
 
      }
 
 
 
      /* Store the value in the transposition table. */
 
      /* ------------------------------------------- */
 
      if (variation->searchStatus == SEARCH_STATUS_RUNNING)
 
      {
 
         UINT8 hashentryFlag;
 
         const int depth = DEPTH_RESOLUTION * variation->iteration;
 
         const Move bestMove = variation->bestBaseMove;
 
 
 
         if (best > alpha)
 
         {
 
            hashentryFlag =
 
               (best >= beta ? HASHVALUE_LOWER_LIMIT : HASHVALUE_EXACT);
 
         }
 
         else
 
         {
 
            hashentryFlag = HASHVALUE_UPPER_LIMIT;
 
         }
 
 
 
         setHashentry(getSharedHashtable(), position->hashKey,
 
                      calcHashtableValue(best, ply),
 
                      (UINT8) (depth + HASH_DEPTH_OFFSET),
 
                      packedMove(bestMove), hashentryFlag,
 
                      (INT16) staticValue);
 
      }
 
 
 
      worstValue = (numPvs == 1 ? best :
 
                    max(VALUE_MATED + 3, variation->pv[numPvs - 1].score));
 
 
 
      if (best >= beta)
 
      {
 
         beta = min(-VALUE_MATED, best + window);
 
      }
 
      else if (worstValue <= alpha && worstValue > VALUE_MATED + 2)
 
      {
 
         alpha = max(VALUE_MATED, alpha - window);
 
         variation->failingLow = TRUE;
 
      }
 
      else
 
      {
 
         exactValueFound = TRUE;        /* exact value found */
 
      }
 
 
 
      window = window + window / 2;
 
 
 
      sortMoves(basemoves);
 
 
 
             movesAreEqual(basemoves->moves[0], variation->bestBaseMove));
 
 
 
      if (variation->threadNumber == 0)
 
      {
 
         copyPvToHashtable(variation, &variation->completePv, 0);
 
      }
 
   }
 
   while (variation->searchStatus == SEARCH_STATUS_RUNNING &&
 
          exactValueFound == FALSE);
 
 
 
   variation->pv[0].score = getMoveValue(variation->bestBaseMove);
 
 
 
   if (variation->threadNumber == 0 && variation->iteration > 1 &&
 
       variation->completePv.length <= 1)
 
   {
 
      PrincipalVariation tmpPv;
 
 
 
      copyPvFromHashtable(variation, 0, &tmpPv, variation->bestBaseMove);
 
 
 
      if (tmpPv.length > 1)
 
      {
 
         variation->completePv = tmpPv;
 
      }
 
   }
 
 
 
   sendPvInfo(variation, SEARCHEVENT_PLY_FINISHED);
 
}
 
 
 
static void initializePawnHashtable(PawnHashInfo * pawnHashtable)
 
{
 
   int i;
 
 
 
   for (i = 0; i < PAWN_HASHTABLE_SIZE; i++)
 
   {
 
      pawnHashtable[i].hashKey = 0;
 
   }
 
}
 
 
 
static void initializeKingsafetyHashtable(KingSafetyHashInfo *
 
                                          kingsafetyHashtable)
 
{
 
   int i;
 
 
 
   for (i = 0; i < KINGSAFETY_HASHTABLE_SIZE; i++)
 
   {
 
      kingsafetyHashtable[i].hashKey = 0;
 
   }
 
}
 
 
 
static void updatePieceValues()
 
{
 
   maxPieceValue[WHITE_QUEEN] = maxPieceValue[BLACK_QUEEN] =
 
      max(getOpeningValue(basicValue[WHITE_QUEEN]),
 
          getEndgameValue(basicValue[WHITE_QUEEN])) - 42;
 
   maxPieceValue[WHITE_ROOK] = maxPieceValue[BLACK_ROOK] =
 
      max(getOpeningValue(basicValue[WHITE_ROOK]),
 
          getEndgameValue(basicValue[WHITE_ROOK]));
 
   maxPieceValue[WHITE_BISHOP] = maxPieceValue[BLACK_BISHOP] =
 
      max(getOpeningValue(basicValue[WHITE_BISHOP]),
 
          getEndgameValue(basicValue[WHITE_BISHOP]));
 
   maxPieceValue[WHITE_KNIGHT] = maxPieceValue[BLACK_KNIGHT] =
 
      max(getOpeningValue(basicValue[WHITE_KNIGHT]),
 
          getEndgameValue(basicValue[WHITE_KNIGHT]));
 
   maxPieceValue[WHITE_PAWN] = maxPieceValue[BLACK_PAWN] =
 
      max(getOpeningValue(basicValue[WHITE_PAWN]),
 
          getEndgameValue(basicValue[WHITE_PAWN]));
 
}
 
 
 
Move search(Variation * variation, Movelist * acceptableSolutions)
 
{
 
   Movelist movelist;
 
   long timeTarget;
 
   int stableIterationCount = 0;
 
   int stableBestMoveCount = 0;
 
   Move bestMove = NO_MOVE;
 
   UINT64 nodeCount = 0;
 
   int iv1 = 0, iv2 = 0, iv3 = 0;
 
 
 
   if (resetSharedHashtable)
 
   {
 
      resetHashtable(getSharedHashtable());
 
      initializePawnHashtable(variation->pawnHashtable);
 
      initializeKingsafetyHashtable(variation->kingsafetyHashtable);
 
      resetSharedHashtable = FALSE;
 
   }
 
 
 
   resetHistoryValues(variation);
 
   resetGainValues(variation);
 
 
 
   variation->ply = 0;
 
   variation->ownColor = variation->singlePosition.activeColor;
 
   variation->nodes = variation->nodesAtTimeCheck = 0;
 
   variation->startTimeProcess = getProcessTimestamp();
 
   variation->timestamp = variation->startTime + 1;
 
   variation->hashSendTimestamp = variation->startTime;
 
   variation->tbHits = 0;
 
   variation->numPvUpdates = 0;
 
   variation->terminateSearchOnPonderhit = FALSE;
 
   variation->previousBest = getStaticValue(variation, 0);
 
   variation->bestBaseMove = NO_MOVE;
 
   variation->failingLow = FALSE;
 
   movelist.positionalGain = &(variation->positionalGain[0]);
 
   initializePlyInfo(variation);
 
   getLegalMoves(variation, &movelist);
 
 
 
#ifdef TRACE_EVAL
 
   getValue(&variation->singlePosition, VALUE_MATED, -VALUE_MATED,
 
            variation->pawnHashtable, variation->kingsafetyHashtable);
 
#endif
 
 
 
#ifdef USE_BOOK
 
   if (globalBook.indexFile != NULL && globalBook.moveFile != NULL &&
 
       &variation->singlePosition->moveNumber <= 12)
 
   {
 
      Move bookMove = getBookmove(&globalBook,
 
                                  &variation->singlePosition->hashKey,
 
                                  &movelist);
 
 
 
      if (bookMove != NO_MOVE)
 
      {
 
         variation->bestBaseMove = bookMove;
 
         variation->searchStatus = SEARCH_STATUS_TERMINATE;
 
         variation->finishTime = getTimestamp();
 
 
 
         if (variation->handleSearchEvent != 0)
 
         {
 
            getGuiSearchMutex();
 
            variation->handleSearchEvent(SEARCHEVENT_SEARCH_FINISHED,
 
                                         variation);
 
            releaseGuiSearchMutex();
 
         }
 
 
 
         variation->nodes = 0;
 
 
 
         return variation->bestBaseMove;
 
      }
 
   }
 
#endif
 
 
 
   variation->numberOfBaseMoves = movelist.numberOfMoves;
 
   setMoveValue(&variation->bestBaseMove, VALUE_MATED);
 
 
 
   for (variation->iteration = 1; variation->iteration <= MAX_DEPTH;
 
        variation->iteration++)
 
   {
 
      long calculationTime;
 
      int iterationValue, aspirationWindow;
 
 
 
      variation->ply = 0;
 
 
 
      aspirationWindow =
 
         min
(12, max
(8, (abs(iv1 
- iv2
) + abs(iv2 
- iv3
)) / 2)); 
      exploreBaseMoves(variation, &movelist, aspirationWindow);
 
      calculationTime =
 
         (unsigned long) (getTimestamp() - variation->startTime);
 
 
 
      if (movesAreEqual(variation->bestBaseMove, bestMove))
 
      {
 
         stableBestMoveCount++;
 
      }
 
      else
 
      {
 
         stableBestMoveCount = 0;
 
      }
 
 
 
      bestMove = variation->bestBaseMove;
 
      iv3 = iv2;
 
      iv2 = iv1;
 
      iv1 = iterationValue = getMoveValue(variation->bestBaseMove);
 
 
 
      variation->previousBest = iterationValue;
 
 
 
 
 
      if (acceptableSolutions != 0 &&
 
          listContainsMove(acceptableSolutions, variation->bestBaseMove))
 
      {
 
         stableIterationCount++;
 
 
 
         if (stableIterationCount == 1)
 
         {
 
            nodeCount = variation->nodes;
 
         }
 
      }
 
      else
 
      {
 
         stableIterationCount = 0;
 
         nodeCount = variation->nodes;
 
      }
 
 
 
      /* Check for a fail low. */
 
      /* --------------------- */
 
 
 
      if (variation->numberOfBaseMoves == 1)
 
      {
 
         timeTarget = (19 * variation->timeTarget) / 256;
 
      }
 
      else
 
      {
 
         const int timeWeight = 160 +
 
            (223 * variation->bestMoveChangeCount) / 256;
 
 
 
         timeTarget = (timeWeight * variation->timeTarget) / 256;
 
      }
 
 
 
      variation->bestMoveChangeCount =
 
         (17 * variation->bestMoveChangeCount) / 32;
 
 
 
      getGuiSearchMutex();
 
 
 
      if (variation->threadNumber == 0 &&
 
          variation->searchStatus == SEARCH_STATUS_RUNNING &&
 
          variation->iteration > 8 && variation->timeLimit != 0 &&
 
          calculationTime >= timeTarget)
 
      {
 
#ifdef DEBUG_THREAD_COORDINATION
 
         logDebug
 
            ("Time target reached (%lu/%lu ms, %lu%%)).\n",
 
             calculationTime, variation->timeTarget,
 
             (calculationTime * 100) / variation->timeTarget);
 
#endif
 
 
 
         if (variation->ponderMode)
 
         {
 
            variation->terminateSearchOnPonderhit = TRUE;
 
 
 
#ifdef DEBUG_THREAD_COORDINATION
 
            logDebug("Setting ponder termination flag.\n");
 
#endif
 
         }
 
         else
 
         {
 
            variation->searchStatus = SEARCH_STATUS_TERMINATE;
 
 
 
#ifdef DEBUG_THREAD_COORDINATION
 
            logDebug("Terminating search.\n");
 
#endif
 
         }
 
      }
 
 
 
      if (variation->searchStatus == SEARCH_STATUS_RUNNING &&
 
          (getMoveValue(variation->bestBaseMove) <=
 
           VALUE_MATED + variation->iteration ||
 
           getMoveValue(variation->bestBaseMove) >=
 
           -VALUE_MATED - variation->iteration))
 
      {
 
#ifdef DEBUG_THREAD_COORDINATION
 
         logDebug("Best value out of bounds (%d). Terminating search.\n",
 
                  getMoveValue(variation->bestBaseMove));
 
#endif
 
         variation->searchStatus = SEARCH_STATUS_TERMINATE;
 
      }
 
 
 
      if (variation->searchStatus == SEARCH_STATUS_RUNNING &&
 
          variation->iteration == MAX_DEPTH)
 
      {
 
#ifdef DEBUG_THREAD_COORDINATION
 
         logDebug("Max depth reached. Terminating search.\n");
 
#endif
 
         variation->searchStatus = SEARCH_STATUS_TERMINATE;
 
      }
 
 
 
      if (acceptableSolutions != 0 && stableIterationCount >= 1 &&
 
          (getMoveValue(variation->bestBaseMove) > 20000 ||
 
           (stableIterationCount >= 2 &&
 
            (getMoveValue(variation->bestBaseMove) >= 25 ||
 
             (getTimestamp() - variation->startTime) >= 3000))))
 
      {
 
#ifdef DEBUG_THREAD_COORDINATION
 
         logDebug("Solution found (value=%d). Terminating search.\n",
 
                  getMoveValue(variation->bestBaseMove));
 
#endif
 
         variation->searchStatus = SEARCH_STATUS_TERMINATE;
 
      }
 
 
 
      if (variation->searchStatus != SEARCH_STATUS_RUNNING)
 
      {
 
         variation->terminateSearchOnPonderhit = TRUE;
 
         variation->searchStatus = SEARCH_STATUS_TERMINATE;
 
 
 
         variation->finishTime = getTimestamp();
 
         variation->finishTimeProcess = getProcessTimestamp();
 
 
 
         if (variation->threadNumber == 0)
 
         {
 
            incrementDate(getSharedHashtable());
 
 
 
            if (variation->handleSearchEvent != 0)
 
            {
 
               variation->handleSearchEvent(SEARCHEVENT_SEARCH_FINISHED,
 
                                            variation);
 
            }
 
         }
 
      }
 
 
 
      if (variation->searchStatus != SEARCH_STATUS_RUNNING)
 
      {
 
#ifdef DEBUG_THREAD_COORDINATION
 
         logReport
 
            ("search status != SEARCH_STATUS_RUNNING -> exiting search.\n",
 
             getMoveValue(variation->bestBaseMove));
 
#endif
 
 
 
         releaseGuiSearchMutex();
 
         break;
 
      }
 
 
 
      releaseGuiSearchMutex();
 
   }
 
 
 
   variation->nodes = nodeCount;
 
 
 
   if (statCount1 != 0 || statCount2 != 0)
 
   {
 
      logReport("statCount1=%lld statCount2=%lld (%lld%%) \n",
 
                statCount1, statCount2,
 
                (statCount2 * 100) / max(1, statCount1));
 
   }
 
 
 
   return variation->bestBaseMove;
 
}
 
 
 
/* #define DEBUG_FUT_VALUES */
 
 
 
static void initializeArrays(void)
 
{
 
   int i, j;
 
 
 
   for (i = 0; i < 64; i++)
 
   {
 
      for (j = 0; j < 64; j++)
 
      {
 
         if (i == 0 || j == 0)
 
         {
 
            quietPvMoveReduction[i][j] =
 
               quietMoveReduction[0][i][j] = quietMoveReduction[1][i][j] = 0;
 
         }
 
         else
 
         {
 
            const double baseFactor 
= log((double) (i
)) * log((double) (j
));  
            const double pvReduction = baseFactor / 2.93;
 
            const double nonPvReduction = 0.33 + baseFactor / 2.21;
 
 
 
            quietPvMoveReduction[i][j] =
 
               (int) (pvReduction >= 1.0 ?
 
                      floor(pvReduction 
* DEPTH_RESOLUTION
) : 0);  
            quietMoveReduction[0][i][j] =
 
               quietMoveReduction[1][i][j] =
 
               (int) (nonPvReduction >= 1.0 ?
 
                      floor(nonPvReduction 
* DEPTH_RESOLUTION
) : 0);  
 
 
            if (quietMoveReduction[0][i][j] > 2 * DEPTH_RESOLUTION)
 
            {
 
               quietMoveReduction[0][i][j] += DEPTH_RESOLUTION;
 
            }
 
            else if (quietMoveReduction[0][i][j] > DEPTH_RESOLUTION)
 
            {
 
               quietMoveReduction[0][i][j] += DEPTH_RESOLUTION / 2;
 
            }
 
         }
 
      }
 
   }
 
 
 
   for (i = 0; i < 32; i++)
 
   {
 
      quietMoveCountLimit
[0][i
] = (int) (2.4 + 0.231 * pow(i
, 1.8)); 
      quietMoveCountLimit
[1][i
] = (int) (3.1 + 0.344 * pow(i 
+ 0.78, 1.8)); 
 
 
#ifdef DEBUG_FUT_VALUES
 
      logDebug("mcl[%d]=%d\n", i, quietMoveCountLimit[i]);
 
#endif
 
   }
 
 
 
   for (i = 0; i <= NUM_FUTILITY_MARGIN_VALUES; i++)
 
   {
 
      futilityMargin[i] = (3125 * i) / 64 - 12800 / 256;
 
 
 
#ifdef DEBUG_FUT_VALUES
 
      if (j <= 2)
 
      {
 
         logDebug("fm[%d][%d]=%d\n", i, j, futilityMargin[i][j]);
 
      }
 
#endif
 
   }
 
 
 
   updatePieceValues();
 
 
 
#ifdef DEBUG_FUT_VALUES
 
   getKeyStroke();
 
#endif
 
}
 
 
 
int initializeModuleSearch(void)
 
{
 
   initializeArrays();
 
 
 
   return 0;
 
}
 
 
 
int testModuleSearch(void)
 
{
 
   return 0;
 
}