/*
 
    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 "xboard.h"
 
#include "coordination.h"
 
#include "tools.h"
 
#include "io.h"
 
#include "fen.h"
 
#include "pgn.h"
 
#ifdef INCLUDE_TABLEBASE_ACCESS
 
#include "tablebase.h"
 
#include "hash.h"
 
#endif
 
#include <signal.h>
 
#include <stdio.h>
 
#include <string.h>
 
#include <stdlib.h>
 
#include <stdarg.h>
 
#include <assert.h>
 
#include <ctype.h>
 
 
 
static SearchTask task;
 
static Variation variation;
 
static PGNGame game;
 
static XboardStatus status;
 
bool resetSharedHashtable = FALSE;
 
static int numUciMoves = 1000;
 
const int valueRangePct = 10;
 
int drawScore = 0;
 
int numPvs = 1;
 
const int DRAW_SCORE_MAX = 10000;
 
 
 
#define MIN_SEND_DEPTH_DEFAULT 12
 
#define MIN_SEND_TIME_DEFAULT 1000
 
#define MAX_SEND_HEIGHT_DEFAULT 24
 
#define MAX_ENTRIES_PS_DEFAULT 4
 
 
 
int minPvHashEntrySendDepth = MIN_SEND_DEPTH_DEFAULT;
 
int minPvHashEntrySendTime = MIN_SEND_TIME_DEFAULT;
 
int maxPvHashEntrySendHeight = MAX_SEND_HEIGHT_DEFAULT;
 
int maxPvHashEntriesSendPerSecond = MAX_ENTRIES_PS_DEFAULT;
 
int pvHashEntriesSendInterval = 1000 / MAX_ENTRIES_PS_DEFAULT;
 
 
 
const char *USN_NT = "Threads";
 
const char *USN_QO = "Value Queen Opening";
 
const char *USN_QE = "Value Queen Endgame";
 
const char *USN_RO = "Value Rook Opening";
 
const char *USN_RE = "Value Rook Endgame";
 
const char *USN_BO = "Value Bishop Opening";
 
const char *USN_BE = "Value Bishop Endgame";
 
const char *USN_NO = "Value Knight Opening";
 
const char *USN_NE = "Value Knight Endgame";
 
const char *USN_PO = "Value Pawn Opening";
 
const char *USN_PE = "Value Pawn Endgame";
 
const char *USN_BPO = "Value Bishop pair Opening";
 
const char *USN_BPE = "Value Bishop pair Endgame";
 
const char *USN_DS = "Draw Score";
 
const char *USN_PV = "MultiPV";
 
 
 
const char *USN_SD = "Min PVHashEntry Send Depth";
 
const char *USN_ST = "Min PVHashEntry Send Time";
 
const char *USN_SH = "Max PVHashEntry Send Height";
 
const char *USN_PS = "Max PVHashEntries Send Per Second";
 
 
 
/* #define DEBUG_GUI_PROTOCOL */
 
/* #define DEBUG_GUI_PROTOCOL_BRIEF */
 
/* #define DEBUG_GUI_CONVERSATION */
 
 
 
static Move readUciMove(const char *buffer)
 
{
 
   const Square from = getSquare(buffer[0] - 'a', buffer[1] - '1');
 
   const Square to = getSquare(buffer[2] - 'a', buffer[3] - '1');
 
   Piece newPiece = NO_PIECE;
 
 
 
   switch (buffer[4])
 
   {
 
   case 'q':
 
   case 'Q':
 
      newPiece = (Piece) (QUEEN);
 
      break;
 
 
 
   case 'r':
 
   case 'R':
 
      newPiece = (Piece) (ROOK);
 
      break;
 
 
 
   case 'b':
 
   case 'B':
 
      newPiece = (Piece) (BISHOP);
 
      break;
 
 
 
   case 'n':
 
   case 'N':
 
      newPiece = (Piece) (KNIGHT);
 
      break;
 
 
 
   default:
 
      newPiece = (Piece) (NO_PIECE);
 
   }
 
 
 
   return getPackedMove(from, to, newPiece);
 
}
 
 
 
static const char *getUciToken(const char *uciString, const char *tokenName)
 
{
 
   size_t tokenNameLength 
= strlen(tokenName
);  
   const char *tokenHit 
= strstr(uciString
, tokenName
);  
 
 
#ifdef   DEBUG_GUI_PROTOCOL
 
   logDebug("find >%s< in >%s<\n", tokenName, uciString);
 
#endif
 
 
 
   if (tokenHit == 0)
 
   {
 
      return 0;
 
   }
 
   else
 
   {
 
      const char nextChar = *(tokenHit + tokenNameLength);
 
 
 
      if ((tokenHit 
== uciString 
|| isspace((int) *(tokenHit 
- 1))) &&  
          (nextChar 
== '\0' || isspace((int) nextChar
)))  
      {
 
         return tokenHit;
 
      }
 
      else
 
      {
 
         const char *nextPosstibleTokenOccurence =
 
            tokenHit + tokenNameLength + 1;
 
 
 
         if (nextPosstibleTokenOccurence + tokenNameLength <=
 
             uciString 
+ strlen(uciString
)) 
         {
 
            return getUciToken(nextPosstibleTokenOccurence, tokenName);
 
         }
 
         else
 
         {
 
            return 0;
 
         }
 
      }
 
   }
 
}
 
 
 
static void getNextUciToken(const char *uciString, char *buffer)
 
{
 
   const char *start = uciString, *end;
 
   unsigned int tokenLength;
 
 
 
   while (*start 
!= '\0' && isspace(*start
))  
   {
 
      start++;
 
   }
 
 
 
   if (*start == '\0')
 
   {
 
 
 
      return;
 
   }
 
 
 
 
 
   end = start + 1;
 
 
 
   while (*end 
!= '\0' && isspace(*end
) == FALSE
)  
   {
 
      end++;
 
   }
 
 
 
 
 
   tokenLength = (unsigned int) (end - start);
 
 
 
   strncpy(buffer
, start
, tokenLength
);  
   buffer[tokenLength] = '\0';
 
}
 
 
 
static long getLongUciValue(const char *uciString, const char *name,
 
                            int defaultValue)
 
{
 
   long value;
 
   char valueBuffer[256];
 
   const char *nameStart = getUciToken(uciString, name);
 
 
 
   if (nameStart == 0)
 
   {
 
      value = defaultValue;
 
   }
 
   else
 
   {
 
      getNextUciToken
(nameStart 
+ strlen(name
), valueBuffer
); 
      value 
= atol(valueBuffer
); 
   }
 
 
 
#ifdef   DEBUG_GUI_PROTOCOL
 
   logDebug("get uci long value; %s = %ld\n", name, value);
 
#endif
 
 
 
   return value;
 
}
 
 
 
static void getStringUciValue(const char *uciString, const char *name,
 
                              char *stringValue)
 
{
 
   const char *nameStart = getUciToken(uciString, name);
 
 
 
   if (nameStart == 0)
 
   {
 
      stringValue[0] = 0;
 
   }
 
   else
 
   {
 
      getNextUciToken
(nameStart 
+ strlen(name
), stringValue
); 
   }
 
 
 
#ifdef   DEBUG_GUI_PROTOCOL
 
   logDebug("get uci string value; %s = %ld\n", name, stringValue);
 
#endif
 
}
 
 
 
static void getUciNamedValue(const char *uciString, char *name, char *value)
 
{
 
   const char *nameTagStart = getUciToken(uciString, "name");
 
   const char *valueTagStart = getUciToken(uciString, "value");
 
 
 
   name[0] = value[0] = '\0';
 
 
 
   if (nameTagStart != 0 && valueTagStart != 0 &&
 
       nameTagStart < valueTagStart)
 
   {
 
      const int nameLength = (int) (valueTagStart - 1 - (nameTagStart + 5));
 
 
 
      strncpy(name
, nameTagStart 
+ 5, nameLength
);  
      name[nameLength] = '\0';
 
      getNextUciToken(valueTagStart + 6, value);
 
 
 
      trim(name);
 
      trim(value);
 
 
 
      /* logDebug("name=<%s> value=<%s>\n", name, value); */
 
   }
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Get the specified move in xboard format.
 
 *
 
 ******************************************************************************/
 
static void getGuiMoveString(const Move move, char *buffer)
 
{
 
   char from[16], to[16];
 
 
 
   getSquareName(getFromSquare(move), from);
 
   getSquareName(getToSquare(move), to);
 
 
 
   if (getNewPiece(move) == NO_PIECE)
 
   {
 
   }
 
   else
 
   {
 
      const int pieceIndex = getLimitedValue(0, 15, getNewPiece(move));
 
 
 
      sprintf(buffer
, "%s%s%c", from
, to
, pieceSymbol
[pieceIndex
]);  
   }
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Get the specified param from the specified xboard command string.
 
 *
 
 ******************************************************************************/
 
static void getTokenByNumber(const char *command, int paramNumber,
 
                             char *buffer)
 
{
 
   int paramCount = 0;
 
   char currentChar;
 
   bool escapeMode = FALSE;
 
   char *pbuffer = buffer;
 
 
 
   while ((currentChar = *command++) != '\0' && paramCount <= paramNumber)
 
   {
 
      if (currentChar == '{')
 
      {
 
         escapeMode = TRUE;
 
      }
 
      else if (currentChar == '}')
 
      {
 
         escapeMode = FALSE;
 
      }
 
 
 
      if (isspace(currentChar
) && escapeMode 
== FALSE
)  
      {
 
         paramCount++;
 
 
 
         {
 
            command++;
 
         }
 
      }
 
 
 
      if (paramCount == paramNumber)
 
      {
 
         *pbuffer++ = currentChar;
 
      }
 
   }
 
 
 
   *pbuffer = '\0';
 
   trim(buffer);
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Send the specified command via stdout to xboard.
 
 *
 
 ******************************************************************************/
 
static void sendToXboard(const char *fmt, ...)
 
{
 
   va_list args;
 
   char buffer[4096];
 
 
 
 
 
 
 
#ifdef DEBUG_GUI_CONVERSATION
 
   logDebug("### sent to xboard: %s\n", buffer);
 
#endif
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Send the specified command via stdout to xboard.
 
 *
 
 ******************************************************************************/
 
static void sendToXboardNonDebug(const char *fmt, ...)
 
{
 
   va_list args;
 
   char buffer[4096];
 
 
 
 
 
 
 
#ifdef DEBUG_GUI_CONVERSATION
 
   logDebug("### sent to xboard: %s\n", buffer);
 
#endif
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Determine the calculation time for the next task in milliseconds.
 
 *
 
 ******************************************************************************/
 
static int getCalculationTime(TimecontrolData * data)
 
{
 
   if (data->restTime < 0)
 
   {
 
      return 2 * data->incrementTime;
 
   }
 
 
 
   if (data->movesToGo > 0)
 
   {
 
      return data->restTime / data->movesToGo + data->incrementTime;
 
   }
 
   else
 
   {
 
      const int movesToGo = max(32, 58 - data->numberOfMovesPlayed);
 
 
 
      return (data->incrementTime > 0 ?
 
              data->incrementTime + data->restTime / movesToGo :
 
              data->restTime / movesToGo);
 
   }
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Determine the calculation time for the next task in milliseconds.
 
 *
 
 ******************************************************************************/
 
static int getMaximumCalculationTime(TimecontrolData * data)
 
{
 
   if (data->restTime < 0)
 
   {
 
      return data->incrementTime;
 
   }
 
 
 
   if (data->movesToGo > 0)
 
   {
 
      const int standardTime = data->restTime / data->movesToGo;
 
      const int maxTime = data->restTime / 2;
 
 
 
      return min(7 * standardTime, maxTime);
 
   }
 
   else
 
   {
 
      const int maxTime1 = (100 * data->restTime) / 256;
 
      const int maxTime2 = 7 * getCalculationTime(data);
 
 
 
      return min(maxTime1, maxTime2);
 
   }
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Determine the calculation time for the next task in milliseconds.
 
 *
 
 ******************************************************************************/
 
static int determineCalculationTime(bool targetTime)
 
{
 
   if (status.operationMode == XBOARD_OPERATIONMODE_ANALYSIS)
 
   {
 
      return 0;
 
   }
 
   else
 
   {
 
      TimecontrolData *tcd = &status.timecontrolData[status.engineColor];
 
 
 
      initializeVariationFromGame(&variation, &game);
 
      tcd->numberOfMovesPlayed = variation.singlePosition.moveNumber - 1;
 
 
 
      if (targetTime)
 
      {
 
         return max(1, 95 * getCalculationTime(tcd) / 100);
 
      }
 
      else
 
      {
 
         return max(1, 95 * getMaximumCalculationTime(tcd) / 100);
 
      }
 
   }
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Start the calculation of the current position.
 
 *
 
 ******************************************************************************/
 
static void startCalculation()
 
{
 
   variation.timeTarget = determineCalculationTime(TRUE);
 
   variation.timeLimit = determineCalculationTime(FALSE);
 
   setDrawScore(&variation, drawScore, status.engineColor);
 
   status.bestMoveWasSent = FALSE;
 
 
 
#ifdef DEBUG_GUI_CONVERSATION
 
   logDebug("Scheduling task. Timelimits: %d/%d\n", variation.timeTarget,
 
            variation.timeLimit);
 
#endif
 
 
 
   scheduleTask(&task);
 
}
 
 
 
static void startPostPonderCalculation()
 
{
 
   const long elapsedTime = getElapsedTime();
 
   const long nominalRestTime = variation.timeLimit - elapsedTime;
 
   const long minimalRestTime = max(1, variation.timeLimit / 4);
 
 
 
   /* logDebug
 
      ("Preparing post ponder calculation. timelimit: %d elapsed time: %d nomimalRestTime: %d minimal rest time: %d \n",
 
      variation.timeLimit, elapsedTime, nominalRestTime, minimalRestTime); */
 
 
 
   variation.timeLimit = max(nominalRestTime, minimalRestTime);
 
   variation.ponderMode = FALSE;
 
 
 
   /* logDebug("Starting post ponder calculation. Timelimits: %d/%d\n",
 
      variation.timeTarget, variation.timeLimit); */
 
 
 
#ifdef DEBUG_GUI_CONVERSATION
 
   logDebug("Starting post ponder calculation. Timelimits: %d/%d\n",
 
            variation.timeTarget, variation.timeLimit);
 
#endif
 
 
 
   startTimerThread(&task);
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Delete the current ponder result.
 
 *
 
 ******************************************************************************/
 
static void deletePonderResult()
 
{
 
   status.ponderResultMove = NO_MOVE;
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Get a UCI-compliant pv.
 
 *
 
 ******************************************************************************/
 
static char *getUciPv(const PrincipalVariation * pv, char *buffer)
 
{
 
   int i;
 
 
 
 
 
   for (i = 0; i < min(32, pv->length); i++)
 
   {
 
      const Move move = (Move) pv->move[i];
 
 
 
      if (move != NO_MOVE)
 
      {
 
         char moveBuffer[16];
 
 
 
         if (i > 0)
 
         {
 
         }
 
 
 
         getGuiMoveString(move, moveBuffer);
 
      }
 
      else
 
      {
 
         break;
 
      }
 
   }
 
 
 
   return buffer;
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Post a principal variation line.
 
 *
 
 ******************************************************************************/
 
static void postPv(Variation * var, bool sendAnyway)
 
{
 
   const char *format =
 
      "info depth %d seldepth %d time %.0f nodes %llu score %s %s tbhits %lu %s pv %s";
 
   double time = getTimestamp
() - var
->startTime
;  
   char pvBuffer[2048];
 
   char pvMovesBuffer[1024];
 
   char scoreBuffer[16];
 
   char scoreTypeBuffer[32] = "";
 
   char multiPvBuffer[32] = "";
 
 
 
   if (time >= 250 || sendAnyway
)  
   {
 
      const UINT64 nodeCount = getNodeCount();
 
      const PrincipalVariation *pv = &var->pv[var->pvId];
 
 
 
      if (numPvs > 1)
 
      {
 
         sprintf(multiPvBuffer
, "multipv %d", var
->pvId 
+ 1);  
      }
 
 
 
      getUciPv(pv, pvMovesBuffer);
 
      formatUciValue(pv->score, scoreBuffer);
 
 
 
      if (pv->scoreType == HASHVALUE_LOWER_LIMIT)
 
      {
 
         sprintf(scoreTypeBuffer
, "lowerbound");  
      }
 
      else if (pv->scoreType == HASHVALUE_UPPER_LIMIT)
 
      {
 
         sprintf(scoreTypeBuffer
, "upperbound");  
      }
 
 
 
      sprintf(pvBuffer
, format
, var
->iteration
, var
->selDepth
, time,  
              nodeCount, scoreBuffer, scoreTypeBuffer, var->tbHits,
 
              multiPvBuffer, pvMovesBuffer);
 
 
 
      sendToXboardNonDebug("%s", pvBuffer);
 
 
 
      var->numPvUpdates++;
 
   }
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Post a statistics information about the current search.
 
 *
 
 ******************************************************************************/
 
static void reportBaseMoveUpdate(const Variation * var)
 
{
 
   const double time = getTimestamp
() - var
->startTime
;  
   char movetext[16];
 
 
 
   {
 
      getGuiMoveString(var->currentBaseMove, movetext);
 
 
 
      sendToXboardNonDebug
 
         ("info depth %d seldepth %d currmove %s currmovenumber %d",
 
          var->iteration, var->selDepth, movetext,
 
          var->numberOfCurrentBaseMove);
 
   }
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Post a statistics information about the current search.
 
 *
 
 ******************************************************************************/
 
 
 
static void reportStatisticsUpdate(Variation * var)
 
{
 
   UINT64 nodeCount = getNodeCount();
 
   const double time = getTimestamp
() - var
->startTime
;  
   const double nps 
= (nodeCount 
/ max
((double) 0.001, (time / 1000.0)));  
   const double hashUsage =
 
      ((double) getSharedHashtable()->entriesUsed * 1000.0) /
 
      (max((double) 1.0, (double) getSharedHashtable()->tableSize));
 
 
 
   sendToXboardNonDebug
 
      ("info time %0.f nodes %lld nps %.0f hashfull %.0f tbhits %lu",
 
       time, nodeCount
, nps
, hashUsage
, var
->tbHits
);  
   reportBaseMoveUpdate(var);
 
 
 
   if (var->numPvUpdates == 0)
 
   {
 
      postPv(var, FALSE);
 
   }
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Send a bestmove info to the gui.
 
 *
 
 ******************************************************************************/
 
static void sendBestmoveInfo(Variation * var)
 
{
 
   char moveBuffer[8];
 
 
 
   postPv(var, TRUE);
 
 
 
   if (moveIsLegal(&var->startPosition, var->bestBaseMove))
 
   {
 
      Variation tmp = *var;
 
 
 
      getGuiMoveString(var->bestBaseMove, moveBuffer);
 
      status.ponderingMove = (Move) var->completePv.move[1];
 
      setBasePosition(&tmp, &var->startPosition);
 
      makeMove(&tmp, var->bestBaseMove);
 
 
 
      if (status.ponderingMove != NO_MOVE &&
 
          moveIsLegal(&tmp.singlePosition, status.ponderingMove))
 
      {
 
         char ponderMoveBuffer[16];
 
 
 
         getGuiMoveString(status.ponderingMove, ponderMoveBuffer);
 
         sendToXboardNonDebug("bestmove %s ponder %s", moveBuffer,
 
                              ponderMoveBuffer);
 
#ifdef DEBUG_GUI_PROTOCOL_BRIEF
 
         logDebug("bestmove %s ponder %s\n", moveBuffer, ponderMoveBuffer);
 
#endif
 
      }
 
      else
 
      {
 
         status.ponderingMove = NO_MOVE;
 
         sendToXboardNonDebug("bestmove %s", moveBuffer);
 
#ifdef DEBUG_GUI_PROTOCOL_BRIEF
 
         logDebug("bestmove %s\n", moveBuffer);
 
#endif
 
      }
 
 
 
      unmakeLastMove(&tmp);
 
   }
 
   else
 
   {
 
      getGuiMoveString(var->bestBaseMove, moveBuffer);
 
 
 
#ifdef DEBUG_GUI_CONVERSATION
 
      logDebug("### Illegal best move %s ###\n", moveBuffer);
 
      logDebug("### Sending bestmove 0000 ###\n");
 
#endif
 
 
 
      sendToXboardNonDebug("bestmove 0000");
 
   }
 
 
 
   status.bestMoveWasSent = TRUE;
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Send a hash entry via UCI.
 
 *
 
 ******************************************************************************/
 
void sendHashentry(Hashentry * entry)
 
{
 
   /* const char *fmt = "info transkey %llx transdata %llx"; */
 
   /* kh 2015-09-21 %llx prints only 32 bits on some windows systems */
 
   const char *fmt = "info transkey %I64x transdata %I64x";
 
 
 
   getGuiSearchMutex();
 
   sendToXboard(fmt, entry->key, entry->data);
 
   releaseGuiSearchMutex();
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Handle events generated by the search engine.
 
 *
 
 ******************************************************************************/
 
static void handleSearchEvent(int eventId, void *var)
 
{
 
   Variation *variation = (Variation *) var;
 
 
 
   switch (eventId)
 
   {
 
   case SEARCHEVENT_SEARCH_FINISHED:
 
      if (status.engineIsPondering == FALSE)
 
      {
 
         sendBestmoveInfo(variation);
 
      }
 
      else
 
      {
 
#ifdef DEBUG_GUI_CONVERSATION
 
         logDebug("Search finished. Engine was pondering.\n");
 
         logDebug("No best move info sent.\n");
 
#endif
 
         postPv(variation, TRUE);
 
      }
 
 
 
      status.engineIsActive = FALSE;
 
      break;
 
 
 
   case SEARCHEVENT_PLY_FINISHED:
 
      postPv(variation, TRUE);
 
      break;
 
 
 
   case SEARCHEVENT_NEW_BASEMOVE:
 
      reportBaseMoveUpdate(variation);
 
      reportStatisticsUpdate(variation);
 
      break;
 
 
 
   case SEARCHEVENT_STATISTICS_UPDATE:
 
      reportStatisticsUpdate(variation);
 
      break;
 
 
 
   case SEARCHEVENT_NEW_PV:
 
      postPv(variation, TRUE);
 
      break;
 
 
 
   default:
 
      break;
 
   }
 
 
 
}
 
 
 
static int getIntValue(const char *value, int minValue, int defaultValue,
 
                       int maxValue)
 
{
 
   int parsedValue 
= atoi(value
);  
 
 
   if (parsedValue == 0 && parsedValue < minValue)
 
   {
 
      return defaultValue;
 
   }
 
   else
 
   {
 
      return min(max(parsedValue, minValue), maxValue);
 
   }
 
}
 
 
 
static int getValueLimit(int value, int diffPct)
 
{
 
   return (value * (100 + diffPct)) / 100;
 
}
 
 
 
static int getStdIntValue(const char *value, int defaultValue)
 
{
 
   const int minValue = getValueLimit(defaultValue, -valueRangePct);
 
   const int maxValue = getValueLimit(defaultValue, valueRangePct);
 
 
 
   return getIntValue(value, minValue, defaultValue, maxValue);
 
}
 
 
 
static void sendUciSpinOption(const char *name, const int defaultValue,
 
                              int minValue, int maxValue)
 
{
 
   sendToXboardNonDebug("option name %s type spin default %d min %d max %d",
 
                        name, defaultValue, minValue, maxValue);
 
}
 
 
 
void addHashentry(Hashentry * entry)
 
{
 
   setHashentry(getSharedHashtable(), entry->key, getHashentryValue(entry),
 
                getHashentryImportance(entry), getHashentryMove(entry),
 
                getHashentryFlag(entry), getHashentryStaticValue(entry));
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Process the specified UCI command.
 
 *
 
 ******************************************************************************/
 
static int processUciCommand(const char *command)
 
{
 
   char buffer[8192];
 
 
 
#ifdef DEBUG_GUI_PROTOCOL_BRIEF
 
   logDebug("%s\n", command);
 
#endif
 
 
 
   getTokenByNumber(command, 0, buffer);
 
 
 
   if (strcmp(buffer
, "uci") == 0)  
   {
 
      char nameString[256];
 
 
 
      getGuiSearchMutex();
 
      strcpy(nameString
, "id name Protector ");  
      strcat(nameString
, programVersionNumber
);  
      sendToXboardNonDebug(nameString);
 
      sendToXboardNonDebug("id author Raimund Heid");
 
      sendToXboardNonDebug
 
         ("option name Hash type spin default 16 min 8 max 65536");
 
#ifdef INCLUDE_TABLEBASE_ACCESS
 
      sendToXboardNonDebug
 
         ("option name NalimovPath type string default <empty>");
 
      sendToXboardNonDebug
 
         ("option name NalimovCache type spin default 4 min 1 max 64");
 
#endif
 
      sendToXboardNonDebug("option name Ponder type check default true");
 
      sendUciSpinOption(USN_NT, 1, 1, MAX_THREADS);
 
      sendUciSpinOption(USN_PO, DEFAULTVALUE_PAWN_OPENING,
 
                        getValueLimit(DEFAULTVALUE_PAWN_OPENING,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_PAWN_OPENING,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_PE, DEFAULTVALUE_PAWN_ENDGAME,
 
                        getValueLimit(DEFAULTVALUE_PAWN_ENDGAME,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_PAWN_ENDGAME,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_NO, DEFAULTVALUE_KNIGHT_OPENING,
 
                        getValueLimit(DEFAULTVALUE_KNIGHT_OPENING,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_KNIGHT_OPENING,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_NE, DEFAULTVALUE_KNIGHT_ENDGAME,
 
                        getValueLimit(DEFAULTVALUE_KNIGHT_ENDGAME,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_KNIGHT_ENDGAME,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_BO, DEFAULTVALUE_BISHOP_OPENING,
 
                        getValueLimit(DEFAULTVALUE_BISHOP_OPENING,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_BISHOP_OPENING,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_BE, DEFAULTVALUE_BISHOP_ENDGAME,
 
                        getValueLimit(DEFAULTVALUE_BISHOP_ENDGAME,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_BISHOP_ENDGAME,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_RO, DEFAULTVALUE_ROOK_OPENING,
 
                        getValueLimit(DEFAULTVALUE_ROOK_OPENING,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_ROOK_OPENING,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_RE, DEFAULTVALUE_ROOK_ENDGAME,
 
                        getValueLimit(DEFAULTVALUE_ROOK_ENDGAME,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_ROOK_ENDGAME,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_QO, DEFAULTVALUE_QUEEN_OPENING,
 
                        getValueLimit(DEFAULTVALUE_QUEEN_OPENING,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_QUEEN_OPENING,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_QE, DEFAULTVALUE_QUEEN_ENDGAME,
 
                        getValueLimit(DEFAULTVALUE_QUEEN_ENDGAME,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_QUEEN_ENDGAME,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_BPO, DEFAULTVALUE_BISHOP_PAIR_OPENING,
 
                        getValueLimit(DEFAULTVALUE_BISHOP_PAIR_OPENING,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_BISHOP_PAIR_OPENING,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_BPE, DEFAULTVALUE_BISHOP_PAIR_ENDGAME,
 
                        getValueLimit(DEFAULTVALUE_BISHOP_PAIR_ENDGAME,
 
                                      -valueRangePct),
 
                        getValueLimit(DEFAULTVALUE_BISHOP_PAIR_ENDGAME,
 
                                      valueRangePct));
 
      sendUciSpinOption(USN_DS, 0, -DRAW_SCORE_MAX, DRAW_SCORE_MAX);
 
      sendUciSpinOption(USN_PV, 1, 1, MAX_NUM_PV);
 
 
 
#ifdef SEND_HASH_ENTRIES
 
      sendUciSpinOption(USN_SD, MIN_SEND_DEPTH_DEFAULT, 8, 16);
 
      sendUciSpinOption(USN_ST, MIN_SEND_TIME_DEFAULT, 100, 5000);
 
      sendUciSpinOption(USN_SH, MAX_SEND_HEIGHT_DEFAULT, 16, 64);
 
      sendUciSpinOption(USN_PS, MAX_ENTRIES_PS_DEFAULT, 1, 20);
 
#endif
 
 
 
      sendToXboardNonDebug("uciok");
 
      releaseGuiSearchMutex();
 
 
 
      return TRUE;
 
   }
 
 
 
   if (strcmp(buffer
, "isready") == 0)  
   {
 
      getGuiSearchMutex();
 
      sendToXboardNonDebug("readyok");
 
      releaseGuiSearchMutex();
 
 
 
      return TRUE;
 
   }
 
 
 
   if (strcmp(buffer
, "ucinewgame") == 0)  
   {
 
      resetSharedHashtable = TRUE;
 
 
 
      return TRUE;
 
   }
 
 
 
   if (strcmp(buffer
, "setoption") == 0)  
   {
 
      char name[256], value[256];
 
 
 
      getUciNamedValue(command, name, value);
 
 
 
#ifdef INCLUDE_TABLEBASE_ACCESS
 
      if (strcmp(name
, "NalimovPath") == 0)  
      {
 
         initializeTablebase(value);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, "NalimovCache") == 0)  
      {
 
         const int cacheSize 
= atoi(value
);  
 
 
         setTablebaseCacheSize(cacheSize);
 
 
 
         return TRUE;
 
      }
 
#endif
 
 
 
      if (strcmp(name
, "Hash") == 0)  
      {
 
         const unsigned int hashsize 
= (unsigned int) max
(8, atoi(value
));  
 
 
         setHashtableSizeInMb(hashsize);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_NT
) == 0)  
      {
 
         const unsigned int numThreads =
 
            (unsigned int) getIntValue(value, 1, 1, MAX_THREADS);
 
 
 
         setNumberOfThreads(numThreads);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_PO
) == 0)  
      {
 
         VALUE_PAWN_OPENING = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_PAWN_OPENING);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_PE
) == 0)  
      {
 
         VALUE_PAWN_ENDGAME = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_PAWN_ENDGAME);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_NO
) == 0)  
      {
 
         VALUE_KNIGHT_OPENING = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_KNIGHT_OPENING);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_NE
) == 0)  
      {
 
         VALUE_KNIGHT_ENDGAME = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_KNIGHT_ENDGAME);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_BO
) == 0)  
      {
 
         VALUE_BISHOP_OPENING = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_BISHOP_OPENING);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_BE
) == 0)  
      {
 
         VALUE_BISHOP_ENDGAME = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_BISHOP_ENDGAME);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_RO
) == 0)  
      {
 
         VALUE_ROOK_OPENING = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_ROOK_OPENING);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_RE
) == 0)  
      {
 
         VALUE_ROOK_ENDGAME = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_ROOK_ENDGAME);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_QO
) == 0)  
      {
 
         VALUE_QUEEN_OPENING = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_QUEEN_OPENING);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_QE
) == 0)  
      {
 
         VALUE_QUEEN_ENDGAME = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_QUEEN_ENDGAME);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_BPO
) == 0)  
      {
 
         VALUE_BISHOP_PAIR_OPENING = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_BISHOP_PAIR_OPENING);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_BPE
) == 0)  
      {
 
         VALUE_BISHOP_PAIR_ENDGAME = (unsigned int)
 
            getStdIntValue(value, DEFAULTVALUE_BISHOP_PAIR_ENDGAME);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_DS
) == 0)  
      {
 
         drawScore = getIntValue(value, -DRAW_SCORE_MAX, 0, DRAW_SCORE_MAX);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_PV
) == 0)  
      {
 
         numPvs = getIntValue(value, 1, 1, MAX_NUM_PV);
 
 
 
         return TRUE;
 
      }
 
 
 
#ifdef SEND_HASH_ENTRIES
 
      if (strcmp(name
, USN_SD
) == 0)  
      {
 
         minPvHashEntrySendDepth =
 
            getIntValue(value, 0, MIN_SEND_DEPTH_DEFAULT, 1000000);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_ST
) == 0)  
      {
 
         minPvHashEntrySendTime =
 
            getIntValue(value, 0, MIN_SEND_TIME_DEFAULT, 1000000);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_SH
) == 0)  
      {
 
         maxPvHashEntrySendHeight =
 
            getIntValue(value, 0, MAX_SEND_HEIGHT_DEFAULT, 1000000);
 
 
 
         return TRUE;
 
      }
 
 
 
      if (strcmp(name
, USN_PS
) == 0)  
      {
 
         maxPvHashEntriesSendPerSecond =
 
            getIntValue(value, 1, MAX_ENTRIES_PS_DEFAULT, 1000000);
 
 
 
         pvHashEntriesSendInterval = 1000 / maxPvHashEntriesSendPerSecond;
 
 
 
         return TRUE;
 
      }
 
#endif
 
   }
 
 
 
   if (strcmp(buffer
, "position") == 0)  
   {
 
      resetPGNGame(&game);
 
 
 
      if (getUciToken(command, "fen") != 0)
 
      {
 
         const char *fenStart = getUciToken(command, "fen") + 3;
 
         const char *fenEnd = getUciToken(command, "moves");
 
 
 
         if (fenEnd == 0)
 
         {
 
         }
 
         else
 
         {
 
            const int length = (int) (fenEnd - fenStart - 1);
 
 
 
            strncpy(game.
fen, fenStart
, length
);  
            game.fen[length] = '\0';
 
         }
 
 
 
         trim(game.fen);
 
 
 
#ifdef   DEBUG_GUI_PROTOCOL
 
         logDebug("fen set: >%s<\n", game.fen);
 
#endif
 
      }
 
 
 
      if (getUciToken(command, "moves") != 0)
 
      {
 
         char moveBuffer[8];
 
         const char *currentMove = getUciToken(command, "moves") + 5;
 
         bool finished = FALSE;
 
         int moveCount = 0;
 
 
 
         do
 
         {
 
            getNextUciToken(currentMove, moveBuffer);
 
 
 
            {
 
               Move move = readUciMove(moveBuffer);
 
 
 
#ifdef   DEBUG_GUI_PROTOCOL
 
               logDebug("move found: >%s<\n", moveBuffer);
 
#endif
 
 
 
               if (appendMove(&game, move) == 0)
 
               {
 
                  currentMove 
+= strlen(moveBuffer
) + 1; 
                  moveCount++;
 
               }
 
               else
 
               {
 
                  finished = TRUE;
 
               }
 
            }
 
            else
 
            {
 
               finished = TRUE;
 
            }
 
         }
 
         while (finished == FALSE);
 
 
 
         if (moveCount < numUciMoves - 1)
 
         {
 
            resetSharedHashtable = TRUE;
 
         }
 
 
 
         numUciMoves = moveCount;
 
      }
 
 
 
      return TRUE;
 
   }
 
 
 
   if (strcmp(buffer
, "stop") == 0)  
   {
 
      getGuiSearchMutex();
 
 
 
      status.engineIsPondering = FALSE;
 
 
 
      if (status.engineIsActive)
 
      {
 
#ifdef DEBUG_GUI_CONVERSATION
 
         logDebug("stopping search...\n");
 
#endif
 
         prepareSearchAbort();
 
         releaseGuiSearchMutex();
 
         waitForSearchTermination();
 
 
 
         if (status.bestMoveWasSent == FALSE)
 
         {
 
            logDebug("### best move was not sent on stop ...\n");
 
            reportVariation(getCurrentVariation());
 
            sendBestmoveInfo(getCurrentVariation());
 
         }
 
 
 
         return TRUE;
 
      }
 
      else
 
      {
 
         if (status.bestMoveWasSent == FALSE)
 
         {
 
#ifdef DEBUG_GUI_CONVERSATION
 
            logDebug("sending best move info on stop ...\n");
 
#endif
 
            sendBestmoveInfo(getCurrentVariation());
 
         }
 
      }
 
 
 
      releaseGuiSearchMutex();
 
 
 
      return TRUE;
 
   }
 
 
 
   if (strcmp(buffer
, "ponderhit") == 0)  
   {
 
#ifdef DEBUG_GUI_CONVERSATION
 
      logDebug("handling ponderhit...\n");
 
#endif
 
 
 
      getGuiSearchMutex();
 
 
 
      status.engineIsPondering = FALSE;
 
 
 
      if (status.engineIsActive)
 
      {
 
         if (getCurrentVariation()->terminateSearchOnPonderhit &&
 
             getCurrentVariation()->failingLow == FALSE)
 
         {
 
#ifdef DEBUG_GUI_CONVERSATION
 
            logDebug("immediate termination of pondering.\n");
 
#endif
 
 
 
            prepareSearchAbort();
 
         }
 
         else
 
         {
 
#ifdef DEBUG_GUI_CONVERSATION
 
            logDebug("unsetting pondering mode.\n");
 
#endif
 
 
 
            unsetPonderMode();
 
            startPostPonderCalculation();
 
         }
 
      }
 
      else
 
      {
 
#ifdef DEBUG_GUI_CONVERSATION
 
         logDebug("Pondering finished prematurely. Sending best move info\n");
 
#endif
 
 
 
         sendBestmoveInfo(getCurrentVariation());
 
      }
 
 
 
      releaseGuiSearchMutex();
 
 
 
      return TRUE;
 
   }
 
 
 
   if (strcmp(buffer
, "go") == 0)  
   {
 
      getGuiSearchMutex();
 
      status.engineIsActive = TRUE;
 
      task.type = TASKTYPE_BEST_MOVE;
 
 
 
      initializeVariationFromGame(&variation, &game);
 
      status.engineColor = variation.singlePosition.activeColor;
 
 
 
      if (getUciToken(command, "depth") != 0)
 
      {
 
         status.operationMode = XBOARD_OPERATIONMODE_ANALYSIS;
 
      }
 
      else if (getUciToken(command, "nodes") != 0)
 
      {
 
         status.operationMode = XBOARD_OPERATIONMODE_ANALYSIS;
 
      }
 
      else if (getUciToken(command, "mate") != 0)
 
      {
 
         task.type = TASKTYPE_MATE_IN_N;
 
         task.numberOfMoves = getLongUciValue(command, "mate", 1);
 
         status.operationMode = XBOARD_OPERATIONMODE_ANALYSIS;
 
 
 
#ifdef   DEBUG_GUI_PROTOCOL
 
         logDebug("Searching mate in %d", task.numberOfMoves);
 
#endif
 
      }
 
      else if (getUciToken(command, "movetime") != 0)
 
      {
 
         status.operationMode = XBOARD_OPERATIONMODE_USERGAME;
 
 
 
         status.timecontrolData[WHITE].restTime =
 
            status.timecontrolData[BLACK].restTime = -1;
 
         status.timecontrolData[WHITE].incrementTime =
 
            status.timecontrolData[BLACK].incrementTime =
 
            getLongUciValue(command, "movetime", 5000);
 
      }
 
      else if (getUciToken(command, "infinite") != 0)
 
      {
 
         status.operationMode = XBOARD_OPERATIONMODE_ANALYSIS;
 
      }
 
      else
 
      {
 
         const int numMovesPlayed = variation.singlePosition.moveNumber - 1;
 
         const int movesToGo = (int) getLongUciValue(command, "movestogo", 0);
 
 
 
         status.operationMode = XBOARD_OPERATIONMODE_USERGAME;
 
 
 
         status.timecontrolData[WHITE].restTime =
 
            getLongUciValue(command, "wtime", 1000);
 
         status.timecontrolData[WHITE].incrementTime =
 
            getLongUciValue(command, "winc", 0);
 
         status.timecontrolData[BLACK].restTime =
 
            getLongUciValue(command, "btime", 1000);
 
         status.timecontrolData[BLACK].incrementTime =
 
            getLongUciValue(command, "binc", 0);
 
         status.timecontrolData[WHITE].numberOfMovesPlayed =
 
            status.timecontrolData[BLACK].numberOfMovesPlayed =
 
            numMovesPlayed;
 
 
 
         if (movesToGo > 0)
 
         {
 
            status.timecontrolData[WHITE].movesToGo =
 
               status.timecontrolData[BLACK].movesToGo = movesToGo;
 
         }
 
         else
 
         {
 
            status.timecontrolData[WHITE].movesToGo =
 
               status.timecontrolData[BLACK].movesToGo = 0;
 
         }
 
      }
 
 
 
      if (getUciToken(command, "ponder") == 0)
 
      {
 
         status.engineIsPondering = variation.ponderMode = FALSE;
 
      }
 
      else
 
      {
 
         status.engineIsPondering = variation.ponderMode = TRUE;
 
         variation.terminateSearchOnPonderhit = FALSE;  /* avoid premature search aborts */
 
      }
 
 
 
      startCalculation();
 
      releaseGuiSearchMutex();
 
 
 
      return TRUE;
 
   }
 
 
 
   if (strcmp(buffer
, "settransentry") == 0)  
   {
 
      char value[256];
 
      Hashentry entry;
 
      bool entryIsValid = TRUE;
 
 
 
      getStringUciValue(command, "key", value);
 
 
 
      {
 
         /* printf("key value: %s\n",value); */
 
 
 
         entry.key = getUnsignedLongLongFromHexString(value);
 
      }
 
      else
 
      {
 
         entryIsValid = FALSE;
 
      }
 
 
 
      getStringUciValue(command, "data", value);
 
 
 
      {
 
         /* printf("data value: %s\n",value); */
 
 
 
         entry.data = getUnsignedLongLongFromHexString(value);
 
      }
 
      else
 
      {
 
         entryIsValid = FALSE;
 
      }
 
 
 
      if (entryIsValid)
 
      {
 
         addHashentry(&entry);
 
      }
 
   }
 
 
 
   if (strcmp(buffer
, "quit") == 0)  
   {
 
      status.engineIsPondering = FALSE;
 
      prepareSearchAbort();
 
 
 
      return FALSE;
 
   }
 
 
 
   return TRUE;
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Read xboard's commands from stdin and handle them.
 
 *
 
 ******************************************************************************/
 
void acceptGuiCommands()
 
{
 
   bool finished = FALSE;
 
   char command[8192];
 
 
 
   /* signal(SIGINT, SIG_IGN); */
 
 
 
   while (finished == FALSE)
 
   {
 
      if (fgets(command
, sizeof(command
), stdin
) == NULL
)  
      {
 
         finished = TRUE;
 
      }
 
      else
 
      {
 
         trim(command);
 
 
 
#ifdef DEBUG_GUI_CONVERSATION
 
         logDebug("\n### gui command: >%s<\n", command);
 
#endif
 
 
 
         finished = (bool) (processUciCommand(command) == FALSE);
 
 
 
#ifdef DEBUG_GUI_CONVERSATION
 
         logDebug(">%s< processed.\n\n", command);
 
#endif
 
      }
 
   }
 
 
 
#ifdef   DEBUG_GUI_PROTOCOL
 
   logDebug("GUI command processing terminated.\n");
 
#endif
 
}
 
 
 
/******************************************************************************
 
 *
 
 * (See the header file comment for this function.)
 
 *
 
 ******************************************************************************/
 
int initializeModuleXboard()
 
{
 
   status.operationMode = XBOARD_OPERATIONMODE_USERGAME;
 
   status.engineColor = WHITE;
 
   status.pondering = TRUE;
 
   status.ponderingMove = NO_MOVE;
 
   status.engineIsPondering = FALSE;
 
   status.engineIsActive = FALSE;
 
   status.bestMoveWasSent = TRUE;
 
   status.maxPlies = 0;
 
   status.timecontrolData[WHITE].movesToGo = 0;
 
   status.timecontrolData[WHITE].incrementTime = 0;
 
   status.timecontrolData[WHITE].numberOfMovesPlayed = 0;
 
   status.timecontrolData[WHITE].restTime = 300 * 1000;
 
   status.timecontrolData[BLACK].movesToGo = 0;
 
   status.timecontrolData[BLACK].incrementTime =
 
      status.timecontrolData[WHITE].incrementTime;
 
   status.timecontrolData[BLACK].numberOfMovesPlayed = 0;
 
   status.timecontrolData[BLACK].restTime =
 
      status.timecontrolData[WHITE].restTime;
 
   deletePonderResult();
 
 
 
   initializePGNGame(&game);
 
   variation.timeLimit = 5000;
 
   variation.ponderMode = FALSE;
 
   variation.handleSearchEvent = &handleSearchEvent;
 
   task.variation = &variation;
 
   task.type = TASKTYPE_BEST_MOVE;
 
 
 
   return 0;
 
}
 
 
 
#ifndef NDEBUG
 
 
 
/******************************************************************************
 
 *
 
 * Test parameter parsing.
 
 *
 
 ******************************************************************************/
 
static int testParameterParsing()
 
{
 
   char buffer[1024];
 
 
 
   getTokenByNumber("protover 2", 1, buffer);
 
 
 
   getTokenByNumber("result 1-0 {White mates}", 2, buffer);
 
 
 
   return 0;
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Test time calculation.
 
 *
 
 ******************************************************************************/
 
static int testTimeCalculation()
 
{
 
   return 0;
 
}
 
 
 
/******************************************************************************
 
 *
 
 * Test the uci tokenizer.
 
 *
 
 ******************************************************************************/
 
static int testUciTokenizer()
 
{
 
   char buffer[64], name[64], value[64];
 
   const char *uciString =
 
      "setoption name\tNalimovPath    value  \t  C:\\chess\\tablebases   time  641273423";
 
   const char *trickyUciString =
 
      "setoption name\tNalimovPathvalue    value  \t  C:\\chess\\tablebases   time  641273423 tablebases";
 
   const char *token1 = getUciToken(uciString, "NalimovPath");
 
   const char *token2 = getUciToken(uciString, "tablebases");
 
   const char *token3 = getUciToken(uciString, "name");
 
   const char *token4 = getUciToken(uciString, "value");
 
   const char *token5 = getUciToken(trickyUciString, "tablebases");
 
 
 
 
 
   getNextUciToken(token3 + 4, buffer);
 
 
 
   getNextUciToken(token4 + 5, buffer);
 
 
 
   assert(getLongUciValue
(uciString
, "time", 0) == 641273423);  
 
 
   assert(strstr(trickyUciString
, "423 tablebases") == token5 
- 4);  
 
 
   getUciNamedValue(uciString, name, value);
 
 
 
   getUciNamedValue(trickyUciString, name, value);
 
 
 
   return 0;
 
}
 
 
 
static int testHashUpdate()
 
{
 
   Hashtable *hashtable = getSharedHashtable();
 
   Hashentry *tableHit;
 
   char *transEntryStringFormat = "settransentry key %llx data %llx";
 
   char commandBuffer[256];
 
   UINT64 hashKey = 15021965ull;
 
   INT16 value = 2004;
 
   INT16 staticValue = 2008;
 
   UINT8 importance = 12;
 
   UINT16 bestMove = NO_MOVE;
 
   UINT8 date = 12;
 
   UINT8 flag = HASHVALUE_EXACT;
 
   Hashentry entry =
 
      constructHashEntry(hashKey, value, staticValue, importance,
 
                         bestMove, date, flag);
 
 
 
   sprintf(commandBuffer
, transEntryStringFormat
, entry.
key, entry.
data);  
   processUciCommand(commandBuffer);
 
   tableHit = getHashentry(hashtable, hashKey);
 
 
 
   assert(getHashentryValue
(tableHit
) == value
);  
   assert(getHashentryStaticValue
(tableHit
) == staticValue
);  
   assert(getHashentryImportance
(tableHit
) == importance
);  
   assert(getHashentryMove
(tableHit
) == bestMove
);  
   assert(getHashentryDate
(tableHit
) == hashtable
->date
);  
   assert(getHashentryFlag
(tableHit
) == flag
);  
 
 
   return 0;
 
}
 
 
 
#endif
 
 
 
/******************************************************************************
 
 *
 
 * (See the header file comment for this function.)
 
 *
 
 ******************************************************************************/
 
int testModuleXboard()
 
{
 
#ifndef NDEBUG
 
   int result;
 
 
 
   if ((result = testParameterParsing()) != 0)
 
   {
 
      return result;
 
   }
 
 
 
   if ((result = testTimeCalculation()) != 0)
 
   {
 
      return result;
 
   }
 
 
 
   if ((result = testUciTokenizer()) != 0)
 
   {
 
      return result;
 
   }
 
 
 
   if ((result = testHashUpdate()) != 0)
 
   {
 
      return result;
 
   }
 
#endif
 
 
 
   return 0;
 
}