Subversion Repositories Games.Chess Giants

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.     Protector -- a UCI chess engine
  3.  
  4.     Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)
  5.  
  6.     This program is free software: you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation, either version 3 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  
  19. */
  20.  
  21. #include "xboard.h"
  22. #include "coordination.h"
  23. #include "tools.h"
  24. #include "io.h"
  25. #include "fen.h"
  26. #include "pgn.h"
  27. #ifdef INCLUDE_TABLEBASE_ACCESS
  28. #include "tablebase.h"
  29. #include "hash.h"
  30. #endif
  31. #include <signal.h>
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <stdlib.h>
  35. #include <stdarg.h>
  36. #include <assert.h>
  37. #include <ctype.h>
  38.  
  39. static SearchTask task;
  40. static Variation variation;
  41. static PGNGame game;
  42. static XboardStatus status;
  43. bool resetSharedHashtable = FALSE;
  44. static int numUciMoves = 1000;
  45. const int valueRangePct = 10;
  46. int drawScore = 0;
  47. int numPvs = 1;
  48. const int DRAW_SCORE_MAX = 10000;
  49.  
  50. #define MIN_SEND_DEPTH_DEFAULT 12
  51. #define MIN_SEND_TIME_DEFAULT 1000
  52. #define MAX_SEND_HEIGHT_DEFAULT 24
  53. #define MAX_ENTRIES_PS_DEFAULT 4
  54.  
  55. int minPvHashEntrySendDepth = MIN_SEND_DEPTH_DEFAULT;
  56. int minPvHashEntrySendTime = MIN_SEND_TIME_DEFAULT;
  57. int maxPvHashEntrySendHeight = MAX_SEND_HEIGHT_DEFAULT;
  58. int maxPvHashEntriesSendPerSecond = MAX_ENTRIES_PS_DEFAULT;
  59. int pvHashEntriesSendInterval = 1000 / MAX_ENTRIES_PS_DEFAULT;
  60.  
  61. const char *USN_NT = "Threads";
  62. const char *USN_QO = "Value Queen Opening";
  63. const char *USN_QE = "Value Queen Endgame";
  64. const char *USN_RO = "Value Rook Opening";
  65. const char *USN_RE = "Value Rook Endgame";
  66. const char *USN_BO = "Value Bishop Opening";
  67. const char *USN_BE = "Value Bishop Endgame";
  68. const char *USN_NO = "Value Knight Opening";
  69. const char *USN_NE = "Value Knight Endgame";
  70. const char *USN_PO = "Value Pawn Opening";
  71. const char *USN_PE = "Value Pawn Endgame";
  72. const char *USN_BPO = "Value Bishop pair Opening";
  73. const char *USN_BPE = "Value Bishop pair Endgame";
  74. const char *USN_DS = "Draw Score";
  75. const char *USN_PV = "MultiPV";
  76.  
  77. const char *USN_SD = "Min PVHashEntry Send Depth";
  78. const char *USN_ST = "Min PVHashEntry Send Time";
  79. const char *USN_SH = "Max PVHashEntry Send Height";
  80. const char *USN_PS = "Max PVHashEntries Send Per Second";
  81.  
  82. /* #define DEBUG_GUI_PROTOCOL */
  83. /* #define DEBUG_GUI_PROTOCOL_BRIEF */
  84. /* #define DEBUG_GUI_CONVERSATION */
  85.  
  86. static Move readUciMove(const char *buffer)
  87. {
  88.    const Square from = getSquare(buffer[0] - 'a', buffer[1] - '1');
  89.    const Square to = getSquare(buffer[2] - 'a', buffer[3] - '1');
  90.    Piece newPiece = NO_PIECE;
  91.  
  92.    switch (buffer[4])
  93.    {
  94.    case 'q':
  95.    case 'Q':
  96.       newPiece = (Piece) (QUEEN);
  97.       break;
  98.  
  99.    case 'r':
  100.    case 'R':
  101.       newPiece = (Piece) (ROOK);
  102.       break;
  103.  
  104.    case 'b':
  105.    case 'B':
  106.       newPiece = (Piece) (BISHOP);
  107.       break;
  108.  
  109.    case 'n':
  110.    case 'N':
  111.       newPiece = (Piece) (KNIGHT);
  112.       break;
  113.  
  114.    default:
  115.       newPiece = (Piece) (NO_PIECE);
  116.    }
  117.  
  118.    return getPackedMove(from, to, newPiece);
  119. }
  120.  
  121. static const char *getUciToken(const char *uciString, const char *tokenName)
  122. {
  123.    size_t tokenNameLength = strlen(tokenName);
  124.    const char *tokenHit = strstr(uciString, tokenName);
  125.  
  126. #ifdef   DEBUG_GUI_PROTOCOL
  127.    logDebug("find >%s< in >%s<\n", tokenName, uciString);
  128. #endif
  129.  
  130.    if (tokenHit == 0)
  131.    {
  132.       return 0;
  133.    }
  134.    else
  135.    {
  136.       const char nextChar = *(tokenHit + tokenNameLength);
  137.  
  138.       if ((tokenHit == uciString || isspace((int) *(tokenHit - 1))) &&
  139.           (nextChar == '\0' || isspace((int) nextChar)))
  140.       {
  141.          return tokenHit;
  142.       }
  143.       else
  144.       {
  145.          const char *nextPosstibleTokenOccurence =
  146.             tokenHit + tokenNameLength + 1;
  147.  
  148.          if (nextPosstibleTokenOccurence + tokenNameLength <=
  149.              uciString + strlen(uciString))
  150.          {
  151.             return getUciToken(nextPosstibleTokenOccurence, tokenName);
  152.          }
  153.          else
  154.          {
  155.             return 0;
  156.          }
  157.       }
  158.    }
  159. }
  160.  
  161. static void getNextUciToken(const char *uciString, char *buffer)
  162. {
  163.    const char *start = uciString, *end;
  164.    unsigned int tokenLength;
  165.  
  166.    while (*start != '\0' && isspace(*start))
  167.    {
  168.       start++;
  169.    }
  170.  
  171.    if (*start == '\0')
  172.    {
  173.       strcpy(buffer, "");
  174.  
  175.       return;
  176.    }
  177.  
  178.    assert(*start != '\0' && isspace(*start) == FALSE);
  179.  
  180.    end = start + 1;
  181.  
  182.    while (*end != '\0' && isspace(*end) == FALSE)
  183.    {
  184.       end++;
  185.    }
  186.  
  187.    assert(*end == '\0' || isspace(*end));
  188.  
  189.    tokenLength = (unsigned int) (end - start);
  190.  
  191.    strncpy(buffer, start, tokenLength);
  192.    buffer[tokenLength] = '\0';
  193. }
  194.  
  195. static long getLongUciValue(const char *uciString, const char *name,
  196.                             int defaultValue)
  197. {
  198.    long value;
  199.    char valueBuffer[256];
  200.    const char *nameStart = getUciToken(uciString, name);
  201.  
  202.    if (nameStart == 0)
  203.    {
  204.       value = defaultValue;
  205.    }
  206.    else
  207.    {
  208.       getNextUciToken(nameStart + strlen(name), valueBuffer);
  209.       value = atol(valueBuffer);
  210.    }
  211.  
  212. #ifdef   DEBUG_GUI_PROTOCOL
  213.    logDebug("get uci long value; %s = %ld\n", name, value);
  214. #endif
  215.  
  216.    return value;
  217. }
  218.  
  219. static void getStringUciValue(const char *uciString, const char *name,
  220.                               char *stringValue)
  221. {
  222.    const char *nameStart = getUciToken(uciString, name);
  223.  
  224.    if (nameStart == 0)
  225.    {
  226.       stringValue[0] = 0;
  227.    }
  228.    else
  229.    {
  230.       getNextUciToken(nameStart + strlen(name), stringValue);
  231.    }
  232.  
  233. #ifdef   DEBUG_GUI_PROTOCOL
  234.    logDebug("get uci string value; %s = %ld\n", name, stringValue);
  235. #endif
  236. }
  237.  
  238. static void getUciNamedValue(const char *uciString, char *name, char *value)
  239. {
  240.    const char *nameTagStart = getUciToken(uciString, "name");
  241.    const char *valueTagStart = getUciToken(uciString, "value");
  242.  
  243.    name[0] = value[0] = '\0';
  244.  
  245.    if (nameTagStart != 0 && valueTagStart != 0 &&
  246.        nameTagStart < valueTagStart)
  247.    {
  248.       const int nameLength = (int) (valueTagStart - 1 - (nameTagStart + 5));
  249.  
  250.       strncpy(name, nameTagStart + 5, nameLength);
  251.       name[nameLength] = '\0';
  252.       getNextUciToken(valueTagStart + 6, value);
  253.  
  254.       trim(name);
  255.       trim(value);
  256.  
  257.       /* logDebug("name=<%s> value=<%s>\n", name, value); */
  258.    }
  259. }
  260.  
  261. /******************************************************************************
  262.  *
  263.  * Get the specified move in xboard format.
  264.  *
  265.  ******************************************************************************/
  266. static void getGuiMoveString(const Move move, char *buffer)
  267. {
  268.    char from[16], to[16];
  269.  
  270.    getSquareName(getFromSquare(move), from);
  271.    getSquareName(getToSquare(move), to);
  272.  
  273.    if (getNewPiece(move) == NO_PIECE)
  274.    {
  275.       sprintf(buffer, "%s%s", from, to);
  276.    }
  277.    else
  278.    {
  279.       const int pieceIndex = getLimitedValue(0, 15, getNewPiece(move));
  280.  
  281.       sprintf(buffer, "%s%s%c", from, to, pieceSymbol[pieceIndex]);
  282.    }
  283. }
  284.  
  285. /******************************************************************************
  286.  *
  287.  * Get the specified param from the specified xboard command string.
  288.  *
  289.  ******************************************************************************/
  290. static void getTokenByNumber(const char *command, int paramNumber,
  291.                              char *buffer)
  292. {
  293.    int paramCount = 0;
  294.    char currentChar;
  295.    bool escapeMode = FALSE;
  296.    char *pbuffer = buffer;
  297.  
  298.    while ((currentChar = *command++) != '\0' && paramCount <= paramNumber)
  299.    {
  300.       if (currentChar == '{')
  301.       {
  302.          escapeMode = TRUE;
  303.       }
  304.       else if (currentChar == '}')
  305.       {
  306.          escapeMode = FALSE;
  307.       }
  308.  
  309.       if (isspace(currentChar) && escapeMode == FALSE)
  310.       {
  311.          paramCount++;
  312.  
  313.          while (isspace(*command))
  314.          {
  315.             command++;
  316.          }
  317.       }
  318.  
  319.       if (paramCount == paramNumber)
  320.       {
  321.          *pbuffer++ = currentChar;
  322.       }
  323.    }
  324.  
  325.    *pbuffer = '\0';
  326.    trim(buffer);
  327. }
  328.  
  329. /******************************************************************************
  330.  *
  331.  * Send the specified command via stdout to xboard.
  332.  *
  333.  ******************************************************************************/
  334. static void sendToXboard(const char *fmt, ...)
  335. {
  336.    va_list args;
  337.    char buffer[4096];
  338.  
  339.    va_start(args, fmt);
  340.    vsprintf(buffer, fmt, args);
  341.    va_end(args);
  342.  
  343.    fprintf(stdout, "%s\n", buffer);
  344.    fflush(stdout);
  345.  
  346. #ifdef DEBUG_GUI_CONVERSATION
  347.    logDebug("### sent to xboard: %s\n", buffer);
  348. #endif
  349. }
  350.  
  351. /******************************************************************************
  352.  *
  353.  * Send the specified command via stdout to xboard.
  354.  *
  355.  ******************************************************************************/
  356. static void sendToXboardNonDebug(const char *fmt, ...)
  357. {
  358.    va_list args;
  359.    char buffer[4096];
  360.  
  361.    va_start(args, fmt);
  362.    vsprintf(buffer, fmt, args);
  363.    va_end(args);
  364.  
  365.    fprintf(stdout, "%s\n", buffer);
  366.    fflush(stdout);
  367.  
  368. #ifdef DEBUG_GUI_CONVERSATION
  369.    logDebug("### sent to xboard: %s\n", buffer);
  370. #endif
  371. }
  372.  
  373. /******************************************************************************
  374.  *
  375.  * Determine the calculation time for the next task in milliseconds.
  376.  *
  377.  ******************************************************************************/
  378. static int getCalculationTime(TimecontrolData * data)
  379. {
  380.    if (data->restTime < 0)
  381.    {
  382.       return 2 * data->incrementTime;
  383.    }
  384.  
  385.    if (data->movesToGo > 0)
  386.    {
  387.       return data->restTime / data->movesToGo + data->incrementTime;
  388.    }
  389.    else
  390.    {
  391.       const int movesToGo = max(32, 58 - data->numberOfMovesPlayed);
  392.  
  393.       return (data->incrementTime > 0 ?
  394.               data->incrementTime + data->restTime / movesToGo :
  395.               data->restTime / movesToGo);
  396.    }
  397. }
  398.  
  399. /******************************************************************************
  400.  *
  401.  * Determine the calculation time for the next task in milliseconds.
  402.  *
  403.  ******************************************************************************/
  404. static int getMaximumCalculationTime(TimecontrolData * data)
  405. {
  406.    if (data->restTime < 0)
  407.    {
  408.       return data->incrementTime;
  409.    }
  410.  
  411.    if (data->movesToGo > 0)
  412.    {
  413.       const int standardTime = data->restTime / data->movesToGo;
  414.       const int maxTime = data->restTime / 2;
  415.  
  416.       return min(7 * standardTime, maxTime);
  417.    }
  418.    else
  419.    {
  420.       const int maxTime1 = (100 * data->restTime) / 256;
  421.       const int maxTime2 = 7 * getCalculationTime(data);
  422.  
  423.       return min(maxTime1, maxTime2);
  424.    }
  425. }
  426.  
  427. /******************************************************************************
  428.  *
  429.  * Determine the calculation time for the next task in milliseconds.
  430.  *
  431.  ******************************************************************************/
  432. static int determineCalculationTime(bool targetTime)
  433. {
  434.    if (status.operationMode == XBOARD_OPERATIONMODE_ANALYSIS)
  435.    {
  436.       return 0;
  437.    }
  438.    else
  439.    {
  440.       TimecontrolData *tcd = &status.timecontrolData[status.engineColor];
  441.  
  442.       initializeVariationFromGame(&variation, &game);
  443.       tcd->numberOfMovesPlayed = variation.singlePosition.moveNumber - 1;
  444.  
  445.       if (targetTime)
  446.       {
  447.          return max(1, 95 * getCalculationTime(tcd) / 100);
  448.       }
  449.       else
  450.       {
  451.          return max(1, 95 * getMaximumCalculationTime(tcd) / 100);
  452.       }
  453.    }
  454. }
  455.  
  456. /******************************************************************************
  457.  *
  458.  * Start the calculation of the current position.
  459.  *
  460.  ******************************************************************************/
  461. static void startCalculation()
  462. {
  463.    variation.timeTarget = determineCalculationTime(TRUE);
  464.    variation.timeLimit = determineCalculationTime(FALSE);
  465.    setDrawScore(&variation, drawScore, status.engineColor);
  466.    status.bestMoveWasSent = FALSE;
  467.  
  468. #ifdef DEBUG_GUI_CONVERSATION
  469.    logDebug("Scheduling task. Timelimits: %d/%d\n", variation.timeTarget,
  470.             variation.timeLimit);
  471. #endif
  472.  
  473.    scheduleTask(&task);
  474. }
  475.  
  476. static void startPostPonderCalculation()
  477. {
  478.    const long elapsedTime = getElapsedTime();
  479.    const long nominalRestTime = variation.timeLimit - elapsedTime;
  480.    const long minimalRestTime = max(1, variation.timeLimit / 4);
  481.  
  482.    /* logDebug
  483.       ("Preparing post ponder calculation. timelimit: %d elapsed time: %d nomimalRestTime: %d minimal rest time: %d \n",
  484.       variation.timeLimit, elapsedTime, nominalRestTime, minimalRestTime); */
  485.  
  486.    variation.timeLimit = max(nominalRestTime, minimalRestTime);
  487.    variation.ponderMode = FALSE;
  488.  
  489.    /* logDebug("Starting post ponder calculation. Timelimits: %d/%d\n",
  490.       variation.timeTarget, variation.timeLimit); */
  491.  
  492. #ifdef DEBUG_GUI_CONVERSATION
  493.    logDebug("Starting post ponder calculation. Timelimits: %d/%d\n",
  494.             variation.timeTarget, variation.timeLimit);
  495. #endif
  496.  
  497.    startTimerThread(&task);
  498. }
  499.  
  500. /******************************************************************************
  501.  *
  502.  * Delete the current ponder result.
  503.  *
  504.  ******************************************************************************/
  505. static void deletePonderResult()
  506. {
  507.    status.ponderResultMove = NO_MOVE;
  508. }
  509.  
  510. /******************************************************************************
  511.  *
  512.  * Get a UCI-compliant pv.
  513.  *
  514.  ******************************************************************************/
  515. static char *getUciPv(const PrincipalVariation * pv, char *buffer)
  516. {
  517.    int i;
  518.  
  519.    strcpy(buffer, "");
  520.  
  521.    for (i = 0; i < min(32, pv->length); i++)
  522.    {
  523.       const Move move = (Move) pv->move[i];
  524.  
  525.       if (move != NO_MOVE)
  526.       {
  527.          char moveBuffer[16];
  528.  
  529.          if (i > 0)
  530.          {
  531.             strcat(buffer, " ");
  532.          }
  533.  
  534.          getGuiMoveString(move, moveBuffer);
  535.          strcat(buffer, moveBuffer);
  536.       }
  537.       else
  538.       {
  539.          break;
  540.       }
  541.    }
  542.  
  543.    return buffer;
  544. }
  545.  
  546. /******************************************************************************
  547.  *
  548.  * Post a principal variation line.
  549.  *
  550.  ******************************************************************************/
  551. static void postPv(Variation * var, bool sendAnyway)
  552. {
  553.    const char *format =
  554.       "info depth %d seldepth %d time %.0f nodes %llu score %s %s tbhits %lu %s pv %s";
  555.    double time = getTimestamp() - var->startTime;
  556.    char pvBuffer[2048];
  557.    char pvMovesBuffer[1024];
  558.    char scoreBuffer[16];
  559.    char scoreTypeBuffer[32] = "";
  560.    char multiPvBuffer[32] = "";
  561.  
  562.    if (time >= 250 || sendAnyway)
  563.    {
  564.       const UINT64 nodeCount = getNodeCount();
  565.       const PrincipalVariation *pv = &var->pv[var->pvId];
  566.  
  567.       if (numPvs > 1)
  568.       {
  569.          sprintf(multiPvBuffer, "multipv %d", var->pvId + 1);
  570.       }
  571.  
  572.       getUciPv(pv, pvMovesBuffer);
  573.       formatUciValue(pv->score, scoreBuffer);
  574.  
  575.       if (pv->scoreType == HASHVALUE_LOWER_LIMIT)
  576.       {
  577.          sprintf(scoreTypeBuffer, "lowerbound");
  578.       }
  579.       else if (pv->scoreType == HASHVALUE_UPPER_LIMIT)
  580.       {
  581.          sprintf(scoreTypeBuffer, "upperbound");
  582.       }
  583.  
  584.       sprintf(pvBuffer, format, var->iteration, var->selDepth, time,
  585.               nodeCount, scoreBuffer, scoreTypeBuffer, var->tbHits,
  586.               multiPvBuffer, pvMovesBuffer);
  587.  
  588.       sendToXboardNonDebug("%s", pvBuffer);
  589.  
  590.       var->numPvUpdates++;
  591.    }
  592. }
  593.  
  594. /******************************************************************************
  595.  *
  596.  * Post a statistics information about the current search.
  597.  *
  598.  ******************************************************************************/
  599. static void reportBaseMoveUpdate(const Variation * var)
  600. {
  601.    const double time = getTimestamp() - var->startTime;
  602.    char movetext[16];
  603.  
  604.    if (time >= 500)
  605.    {
  606.       getGuiMoveString(var->currentBaseMove, movetext);
  607.  
  608.       sendToXboardNonDebug
  609.          ("info depth %d seldepth %d currmove %s currmovenumber %d",
  610.           var->iteration, var->selDepth, movetext,
  611.           var->numberOfCurrentBaseMove);
  612.    }
  613. }
  614.  
  615. /******************************************************************************
  616.  *
  617.  * Post a statistics information about the current search.
  618.  *
  619.  ******************************************************************************/
  620.  
  621. static void reportStatisticsUpdate(Variation * var)
  622. {
  623.    UINT64 nodeCount = getNodeCount();
  624.    const double time = getTimestamp() - var->startTime;
  625.    const double nps = (nodeCount / max((double) 0.001, (time / 1000.0)));
  626.    const double hashUsage =
  627.       ((double) getSharedHashtable()->entriesUsed * 1000.0) /
  628.       (max((double) 1.0, (double) getSharedHashtable()->tableSize));
  629.  
  630.    sendToXboardNonDebug
  631.       ("info time %0.f nodes %lld nps %.0f hashfull %.0f tbhits %lu",
  632.        time, nodeCount, nps, hashUsage, var->tbHits);
  633.    reportBaseMoveUpdate(var);
  634.  
  635.    if (var->numPvUpdates == 0)
  636.    {
  637.       postPv(var, FALSE);
  638.    }
  639. }
  640.  
  641. /******************************************************************************
  642.  *
  643.  * Send a bestmove info to the gui.
  644.  *
  645.  ******************************************************************************/
  646. static void sendBestmoveInfo(Variation * var)
  647. {
  648.    char moveBuffer[8];
  649.  
  650.    postPv(var, TRUE);
  651.  
  652.    if (moveIsLegal(&var->startPosition, var->bestBaseMove))
  653.    {
  654.       Variation tmp = *var;
  655.  
  656.       getGuiMoveString(var->bestBaseMove, moveBuffer);
  657.       status.ponderingMove = (Move) var->completePv.move[1];
  658.       setBasePosition(&tmp, &var->startPosition);
  659.       makeMove(&tmp, var->bestBaseMove);
  660.  
  661.       if (status.ponderingMove != NO_MOVE &&
  662.           moveIsLegal(&tmp.singlePosition, status.ponderingMove))
  663.       {
  664.          char ponderMoveBuffer[16];
  665.  
  666.          getGuiMoveString(status.ponderingMove, ponderMoveBuffer);
  667.          sendToXboardNonDebug("bestmove %s ponder %s", moveBuffer,
  668.                               ponderMoveBuffer);
  669. #ifdef DEBUG_GUI_PROTOCOL_BRIEF
  670.          logDebug("bestmove %s ponder %s\n", moveBuffer, ponderMoveBuffer);
  671. #endif
  672.       }
  673.       else
  674.       {
  675.          status.ponderingMove = NO_MOVE;
  676.          sendToXboardNonDebug("bestmove %s", moveBuffer);
  677. #ifdef DEBUG_GUI_PROTOCOL_BRIEF
  678.          logDebug("bestmove %s\n", moveBuffer);
  679. #endif
  680.       }
  681.  
  682.       unmakeLastMove(&tmp);
  683.    }
  684.    else
  685.    {
  686.       getGuiMoveString(var->bestBaseMove, moveBuffer);
  687.  
  688. #ifdef DEBUG_GUI_CONVERSATION
  689.       logDebug("### Illegal best move %s ###\n", moveBuffer);
  690.       logDebug("### Sending bestmove 0000 ###\n");
  691. #endif
  692.  
  693.       sendToXboardNonDebug("bestmove 0000");
  694.    }
  695.  
  696.    status.bestMoveWasSent = TRUE;
  697. }
  698.  
  699. /******************************************************************************
  700.  *
  701.  * Send a hash entry via UCI.
  702.  *
  703.  ******************************************************************************/
  704. void sendHashentry(Hashentry * entry)
  705. {
  706.    /* const char *fmt = "info transkey %llx transdata %llx"; */
  707.    /* kh 2015-09-21 %llx prints only 32 bits on some windows systems */
  708.    const char *fmt = "info transkey %I64x transdata %I64x";
  709.  
  710.    getGuiSearchMutex();
  711.    sendToXboard(fmt, entry->key, entry->data);
  712.    releaseGuiSearchMutex();
  713. }
  714.  
  715. /******************************************************************************
  716.  *
  717.  * Handle events generated by the search engine.
  718.  *
  719.  ******************************************************************************/
  720. static void handleSearchEvent(int eventId, void *var)
  721. {
  722.    Variation *variation = (Variation *) var;
  723.  
  724.    switch (eventId)
  725.    {
  726.    case SEARCHEVENT_SEARCH_FINISHED:
  727.       if (status.engineIsPondering == FALSE)
  728.       {
  729.          sendBestmoveInfo(variation);
  730.       }
  731.       else
  732.       {
  733. #ifdef DEBUG_GUI_CONVERSATION
  734.          logDebug("Search finished. Engine was pondering.\n");
  735.          logDebug("No best move info sent.\n");
  736. #endif
  737.          postPv(variation, TRUE);
  738.       }
  739.  
  740.       status.engineIsActive = FALSE;
  741.       break;
  742.  
  743.    case SEARCHEVENT_PLY_FINISHED:
  744.       postPv(variation, TRUE);
  745.       break;
  746.  
  747.    case SEARCHEVENT_NEW_BASEMOVE:
  748.       reportBaseMoveUpdate(variation);
  749.       reportStatisticsUpdate(variation);
  750.       break;
  751.  
  752.    case SEARCHEVENT_STATISTICS_UPDATE:
  753.       reportStatisticsUpdate(variation);
  754.       break;
  755.  
  756.    case SEARCHEVENT_NEW_PV:
  757.       postPv(variation, TRUE);
  758.       break;
  759.  
  760.    default:
  761.       break;
  762.    }
  763.  
  764. }
  765.  
  766. static int getIntValue(const char *value, int minValue, int defaultValue,
  767.                        int maxValue)
  768. {
  769.    int parsedValue = atoi(value);
  770.  
  771.    if (parsedValue == 0 && parsedValue < minValue)
  772.    {
  773.       return defaultValue;
  774.    }
  775.    else
  776.    {
  777.       return min(max(parsedValue, minValue), maxValue);
  778.    }
  779. }
  780.  
  781. static int getValueLimit(int value, int diffPct)
  782. {
  783.    return (value * (100 + diffPct)) / 100;
  784. }
  785.  
  786. static int getStdIntValue(const char *value, int defaultValue)
  787. {
  788.    const int minValue = getValueLimit(defaultValue, -valueRangePct);
  789.    const int maxValue = getValueLimit(defaultValue, valueRangePct);
  790.  
  791.    return getIntValue(value, minValue, defaultValue, maxValue);
  792. }
  793.  
  794. static void sendUciSpinOption(const char *name, const int defaultValue,
  795.                               int minValue, int maxValue)
  796. {
  797.    sendToXboardNonDebug("option name %s type spin default %d min %d max %d",
  798.                         name, defaultValue, minValue, maxValue);
  799. }
  800.  
  801. void addHashentry(Hashentry * entry)
  802. {
  803.    setHashentry(getSharedHashtable(), entry->key, getHashentryValue(entry),
  804.                 getHashentryImportance(entry), getHashentryMove(entry),
  805.                 getHashentryFlag(entry), getHashentryStaticValue(entry));
  806. }
  807.  
  808. /******************************************************************************
  809.  *
  810.  * Process the specified UCI command.
  811.  *
  812.  ******************************************************************************/
  813. static int processUciCommand(const char *command)
  814. {
  815.    char buffer[8192];
  816.  
  817. #ifdef DEBUG_GUI_PROTOCOL_BRIEF
  818.    logDebug("%s\n", command);
  819. #endif
  820.  
  821.    getTokenByNumber(command, 0, buffer);
  822.  
  823.    if (strcmp(buffer, "uci") == 0)
  824.    {
  825.       char nameString[256];
  826.  
  827.       getGuiSearchMutex();
  828.       strcpy(nameString, "id name Protector ");
  829.       strcat(nameString, programVersionNumber);
  830.       sendToXboardNonDebug(nameString);
  831.       sendToXboardNonDebug("id author Raimund Heid");
  832.       sendToXboardNonDebug
  833.          ("option name Hash type spin default 16 min 8 max 65536");
  834. #ifdef INCLUDE_TABLEBASE_ACCESS
  835.       sendToXboardNonDebug
  836.          ("option name NalimovPath type string default <empty>");
  837.       sendToXboardNonDebug
  838.          ("option name NalimovCache type spin default 4 min 1 max 64");
  839. #endif
  840.       sendToXboardNonDebug("option name Ponder type check default true");
  841.       sendUciSpinOption(USN_NT, 1, 1, MAX_THREADS);
  842.       sendUciSpinOption(USN_PO, DEFAULTVALUE_PAWN_OPENING,
  843.                         getValueLimit(DEFAULTVALUE_PAWN_OPENING,
  844.                                       -valueRangePct),
  845.                         getValueLimit(DEFAULTVALUE_PAWN_OPENING,
  846.                                       valueRangePct));
  847.       sendUciSpinOption(USN_PE, DEFAULTVALUE_PAWN_ENDGAME,
  848.                         getValueLimit(DEFAULTVALUE_PAWN_ENDGAME,
  849.                                       -valueRangePct),
  850.                         getValueLimit(DEFAULTVALUE_PAWN_ENDGAME,
  851.                                       valueRangePct));
  852.       sendUciSpinOption(USN_NO, DEFAULTVALUE_KNIGHT_OPENING,
  853.                         getValueLimit(DEFAULTVALUE_KNIGHT_OPENING,
  854.                                       -valueRangePct),
  855.                         getValueLimit(DEFAULTVALUE_KNIGHT_OPENING,
  856.                                       valueRangePct));
  857.       sendUciSpinOption(USN_NE, DEFAULTVALUE_KNIGHT_ENDGAME,
  858.                         getValueLimit(DEFAULTVALUE_KNIGHT_ENDGAME,
  859.                                       -valueRangePct),
  860.                         getValueLimit(DEFAULTVALUE_KNIGHT_ENDGAME,
  861.                                       valueRangePct));
  862.       sendUciSpinOption(USN_BO, DEFAULTVALUE_BISHOP_OPENING,
  863.                         getValueLimit(DEFAULTVALUE_BISHOP_OPENING,
  864.                                       -valueRangePct),
  865.                         getValueLimit(DEFAULTVALUE_BISHOP_OPENING,
  866.                                       valueRangePct));
  867.       sendUciSpinOption(USN_BE, DEFAULTVALUE_BISHOP_ENDGAME,
  868.                         getValueLimit(DEFAULTVALUE_BISHOP_ENDGAME,
  869.                                       -valueRangePct),
  870.                         getValueLimit(DEFAULTVALUE_BISHOP_ENDGAME,
  871.                                       valueRangePct));
  872.       sendUciSpinOption(USN_RO, DEFAULTVALUE_ROOK_OPENING,
  873.                         getValueLimit(DEFAULTVALUE_ROOK_OPENING,
  874.                                       -valueRangePct),
  875.                         getValueLimit(DEFAULTVALUE_ROOK_OPENING,
  876.                                       valueRangePct));
  877.       sendUciSpinOption(USN_RE, DEFAULTVALUE_ROOK_ENDGAME,
  878.                         getValueLimit(DEFAULTVALUE_ROOK_ENDGAME,
  879.                                       -valueRangePct),
  880.                         getValueLimit(DEFAULTVALUE_ROOK_ENDGAME,
  881.                                       valueRangePct));
  882.       sendUciSpinOption(USN_QO, DEFAULTVALUE_QUEEN_OPENING,
  883.                         getValueLimit(DEFAULTVALUE_QUEEN_OPENING,
  884.                                       -valueRangePct),
  885.                         getValueLimit(DEFAULTVALUE_QUEEN_OPENING,
  886.                                       valueRangePct));
  887.       sendUciSpinOption(USN_QE, DEFAULTVALUE_QUEEN_ENDGAME,
  888.                         getValueLimit(DEFAULTVALUE_QUEEN_ENDGAME,
  889.                                       -valueRangePct),
  890.                         getValueLimit(DEFAULTVALUE_QUEEN_ENDGAME,
  891.                                       valueRangePct));
  892.       sendUciSpinOption(USN_BPO, DEFAULTVALUE_BISHOP_PAIR_OPENING,
  893.                         getValueLimit(DEFAULTVALUE_BISHOP_PAIR_OPENING,
  894.                                       -valueRangePct),
  895.                         getValueLimit(DEFAULTVALUE_BISHOP_PAIR_OPENING,
  896.                                       valueRangePct));
  897.       sendUciSpinOption(USN_BPE, DEFAULTVALUE_BISHOP_PAIR_ENDGAME,
  898.                         getValueLimit(DEFAULTVALUE_BISHOP_PAIR_ENDGAME,
  899.                                       -valueRangePct),
  900.                         getValueLimit(DEFAULTVALUE_BISHOP_PAIR_ENDGAME,
  901.                                       valueRangePct));
  902.       sendUciSpinOption(USN_DS, 0, -DRAW_SCORE_MAX, DRAW_SCORE_MAX);
  903.       sendUciSpinOption(USN_PV, 1, 1, MAX_NUM_PV);
  904.  
  905. #ifdef SEND_HASH_ENTRIES
  906.       sendUciSpinOption(USN_SD, MIN_SEND_DEPTH_DEFAULT, 8, 16);
  907.       sendUciSpinOption(USN_ST, MIN_SEND_TIME_DEFAULT, 100, 5000);
  908.       sendUciSpinOption(USN_SH, MAX_SEND_HEIGHT_DEFAULT, 16, 64);
  909.       sendUciSpinOption(USN_PS, MAX_ENTRIES_PS_DEFAULT, 1, 20);
  910. #endif
  911.  
  912.       sendToXboardNonDebug("uciok");
  913.       releaseGuiSearchMutex();
  914.  
  915.       return TRUE;
  916.    }
  917.  
  918.    if (strcmp(buffer, "isready") == 0)
  919.    {
  920.       getGuiSearchMutex();
  921.       sendToXboardNonDebug("readyok");
  922.       releaseGuiSearchMutex();
  923.  
  924.       return TRUE;
  925.    }
  926.  
  927.    if (strcmp(buffer, "ucinewgame") == 0)
  928.    {
  929.       resetSharedHashtable = TRUE;
  930.  
  931.       return TRUE;
  932.    }
  933.  
  934.    if (strcmp(buffer, "setoption") == 0)
  935.    {
  936.       char name[256], value[256];
  937.  
  938.       getUciNamedValue(command, name, value);
  939.  
  940. #ifdef INCLUDE_TABLEBASE_ACCESS
  941.       if (strcmp(name, "NalimovPath") == 0)
  942.       {
  943.          initializeTablebase(value);
  944.  
  945.          return TRUE;
  946.       }
  947.  
  948.       if (strcmp(name, "NalimovCache") == 0)
  949.       {
  950.          const int cacheSize = atoi(value);
  951.  
  952.          setTablebaseCacheSize(cacheSize);
  953.  
  954.          return TRUE;
  955.       }
  956. #endif
  957.  
  958.       if (strcmp(name, "Hash") == 0)
  959.       {
  960.          const unsigned int hashsize = (unsigned int) max(8, atoi(value));
  961.  
  962.          setHashtableSizeInMb(hashsize);
  963.  
  964.          return TRUE;
  965.       }
  966.  
  967.       if (strcmp(name, USN_NT) == 0)
  968.       {
  969.          const unsigned int numThreads =
  970.             (unsigned int) getIntValue(value, 1, 1, MAX_THREADS);
  971.  
  972.          setNumberOfThreads(numThreads);
  973.  
  974.          return TRUE;
  975.       }
  976.  
  977.       if (strcmp(name, USN_PO) == 0)
  978.       {
  979.          VALUE_PAWN_OPENING = (unsigned int)
  980.             getStdIntValue(value, DEFAULTVALUE_PAWN_OPENING);
  981.  
  982.          return TRUE;
  983.       }
  984.  
  985.       if (strcmp(name, USN_PE) == 0)
  986.       {
  987.          VALUE_PAWN_ENDGAME = (unsigned int)
  988.             getStdIntValue(value, DEFAULTVALUE_PAWN_ENDGAME);
  989.  
  990.          return TRUE;
  991.       }
  992.  
  993.       if (strcmp(name, USN_NO) == 0)
  994.       {
  995.          VALUE_KNIGHT_OPENING = (unsigned int)
  996.             getStdIntValue(value, DEFAULTVALUE_KNIGHT_OPENING);
  997.  
  998.          return TRUE;
  999.       }
  1000.  
  1001.       if (strcmp(name, USN_NE) == 0)
  1002.       {
  1003.          VALUE_KNIGHT_ENDGAME = (unsigned int)
  1004.             getStdIntValue(value, DEFAULTVALUE_KNIGHT_ENDGAME);
  1005.  
  1006.          return TRUE;
  1007.       }
  1008.  
  1009.       if (strcmp(name, USN_BO) == 0)
  1010.       {
  1011.          VALUE_BISHOP_OPENING = (unsigned int)
  1012.             getStdIntValue(value, DEFAULTVALUE_BISHOP_OPENING);
  1013.  
  1014.          return TRUE;
  1015.       }
  1016.  
  1017.       if (strcmp(name, USN_BE) == 0)
  1018.       {
  1019.          VALUE_BISHOP_ENDGAME = (unsigned int)
  1020.             getStdIntValue(value, DEFAULTVALUE_BISHOP_ENDGAME);
  1021.  
  1022.          return TRUE;
  1023.       }
  1024.  
  1025.       if (strcmp(name, USN_RO) == 0)
  1026.       {
  1027.          VALUE_ROOK_OPENING = (unsigned int)
  1028.             getStdIntValue(value, DEFAULTVALUE_ROOK_OPENING);
  1029.  
  1030.          return TRUE;
  1031.       }
  1032.  
  1033.       if (strcmp(name, USN_RE) == 0)
  1034.       {
  1035.          VALUE_ROOK_ENDGAME = (unsigned int)
  1036.             getStdIntValue(value, DEFAULTVALUE_ROOK_ENDGAME);
  1037.  
  1038.          return TRUE;
  1039.       }
  1040.  
  1041.       if (strcmp(name, USN_QO) == 0)
  1042.       {
  1043.          VALUE_QUEEN_OPENING = (unsigned int)
  1044.             getStdIntValue(value, DEFAULTVALUE_QUEEN_OPENING);
  1045.  
  1046.          return TRUE;
  1047.       }
  1048.  
  1049.       if (strcmp(name, USN_QE) == 0)
  1050.       {
  1051.          VALUE_QUEEN_ENDGAME = (unsigned int)
  1052.             getStdIntValue(value, DEFAULTVALUE_QUEEN_ENDGAME);
  1053.  
  1054.          return TRUE;
  1055.       }
  1056.  
  1057.       if (strcmp(name, USN_BPO) == 0)
  1058.       {
  1059.          VALUE_BISHOP_PAIR_OPENING = (unsigned int)
  1060.             getStdIntValue(value, DEFAULTVALUE_BISHOP_PAIR_OPENING);
  1061.  
  1062.          return TRUE;
  1063.       }
  1064.  
  1065.       if (strcmp(name, USN_BPE) == 0)
  1066.       {
  1067.          VALUE_BISHOP_PAIR_ENDGAME = (unsigned int)
  1068.             getStdIntValue(value, DEFAULTVALUE_BISHOP_PAIR_ENDGAME);
  1069.  
  1070.          return TRUE;
  1071.       }
  1072.  
  1073.       if (strcmp(name, USN_DS) == 0)
  1074.       {
  1075.          drawScore = getIntValue(value, -DRAW_SCORE_MAX, 0, DRAW_SCORE_MAX);
  1076.  
  1077.          return TRUE;
  1078.       }
  1079.  
  1080.       if (strcmp(name, USN_PV) == 0)
  1081.       {
  1082.          numPvs = getIntValue(value, 1, 1, MAX_NUM_PV);
  1083.  
  1084.          return TRUE;
  1085.       }
  1086.  
  1087. #ifdef SEND_HASH_ENTRIES
  1088.       if (strcmp(name, USN_SD) == 0)
  1089.       {
  1090.          minPvHashEntrySendDepth =
  1091.             getIntValue(value, 0, MIN_SEND_DEPTH_DEFAULT, 1000000);
  1092.  
  1093.          return TRUE;
  1094.       }
  1095.  
  1096.       if (strcmp(name, USN_ST) == 0)
  1097.       {
  1098.          minPvHashEntrySendTime =
  1099.             getIntValue(value, 0, MIN_SEND_TIME_DEFAULT, 1000000);
  1100.  
  1101.          return TRUE;
  1102.       }
  1103.  
  1104.       if (strcmp(name, USN_SH) == 0)
  1105.       {
  1106.          maxPvHashEntrySendHeight =
  1107.             getIntValue(value, 0, MAX_SEND_HEIGHT_DEFAULT, 1000000);
  1108.  
  1109.          return TRUE;
  1110.       }
  1111.  
  1112.       if (strcmp(name, USN_PS) == 0)
  1113.       {
  1114.          maxPvHashEntriesSendPerSecond =
  1115.             getIntValue(value, 1, MAX_ENTRIES_PS_DEFAULT, 1000000);
  1116.  
  1117.          pvHashEntriesSendInterval = 1000 / maxPvHashEntriesSendPerSecond;
  1118.  
  1119.          return TRUE;
  1120.       }
  1121. #endif
  1122.    }
  1123.  
  1124.    if (strcmp(buffer, "position") == 0)
  1125.    {
  1126.       resetPGNGame(&game);
  1127.  
  1128.       if (getUciToken(command, "fen") != 0)
  1129.       {
  1130.          const char *fenStart = getUciToken(command, "fen") + 3;
  1131.          const char *fenEnd = getUciToken(command, "moves");
  1132.  
  1133.          if (fenEnd == 0)
  1134.          {
  1135.             strcpy(game.fen, fenStart);
  1136.          }
  1137.          else
  1138.          {
  1139.             const int length = (int) (fenEnd - fenStart - 1);
  1140.  
  1141.             strncpy(game.fen, fenStart, length);
  1142.             game.fen[length] = '\0';
  1143.          }
  1144.  
  1145.          trim(game.fen);
  1146.          strcpy(game.setup, "1");
  1147.  
  1148. #ifdef   DEBUG_GUI_PROTOCOL
  1149.          logDebug("fen set: >%s<\n", game.fen);
  1150. #endif
  1151.       }
  1152.  
  1153.       if (getUciToken(command, "moves") != 0)
  1154.       {
  1155.          char moveBuffer[8];
  1156.          const char *currentMove = getUciToken(command, "moves") + 5;
  1157.          bool finished = FALSE;
  1158.          int moveCount = 0;
  1159.  
  1160.          do
  1161.          {
  1162.             getNextUciToken(currentMove, moveBuffer);
  1163.  
  1164.             if (strlen(moveBuffer) > 0)
  1165.             {
  1166.                Move move = readUciMove(moveBuffer);
  1167.  
  1168. #ifdef   DEBUG_GUI_PROTOCOL
  1169.                logDebug("move found: >%s<\n", moveBuffer);
  1170. #endif
  1171.  
  1172.                if (appendMove(&game, move) == 0)
  1173.                {
  1174.                   currentMove += strlen(moveBuffer) + 1;
  1175.                   moveCount++;
  1176.                }
  1177.                else
  1178.                {
  1179.                   finished = TRUE;
  1180.                }
  1181.             }
  1182.             else
  1183.             {
  1184.                finished = TRUE;
  1185.             }
  1186.          }
  1187.          while (finished == FALSE);
  1188.  
  1189.          if (moveCount < numUciMoves - 1)
  1190.          {
  1191.             resetSharedHashtable = TRUE;
  1192.          }
  1193.  
  1194.          numUciMoves = moveCount;
  1195.       }
  1196.  
  1197.       return TRUE;
  1198.    }
  1199.  
  1200.    if (strcmp(buffer, "stop") == 0)
  1201.    {
  1202.       getGuiSearchMutex();
  1203.  
  1204.       status.engineIsPondering = FALSE;
  1205.  
  1206.       if (status.engineIsActive)
  1207.       {
  1208. #ifdef DEBUG_GUI_CONVERSATION
  1209.          logDebug("stopping search...\n");
  1210. #endif
  1211.          prepareSearchAbort();
  1212.          releaseGuiSearchMutex();
  1213.          waitForSearchTermination();
  1214.  
  1215.          if (status.bestMoveWasSent == FALSE)
  1216.          {
  1217.             logDebug("### best move was not sent on stop ...\n");
  1218.             reportVariation(getCurrentVariation());
  1219.             sendBestmoveInfo(getCurrentVariation());
  1220.          }
  1221.  
  1222.          return TRUE;
  1223.       }
  1224.       else
  1225.       {
  1226.          if (status.bestMoveWasSent == FALSE)
  1227.          {
  1228. #ifdef DEBUG_GUI_CONVERSATION
  1229.             logDebug("sending best move info on stop ...\n");
  1230. #endif
  1231.             sendBestmoveInfo(getCurrentVariation());
  1232.          }
  1233.       }
  1234.  
  1235.       releaseGuiSearchMutex();
  1236.  
  1237.       return TRUE;
  1238.    }
  1239.  
  1240.    if (strcmp(buffer, "ponderhit") == 0)
  1241.    {
  1242. #ifdef DEBUG_GUI_CONVERSATION
  1243.       logDebug("handling ponderhit...\n");
  1244. #endif
  1245.  
  1246.       getGuiSearchMutex();
  1247.  
  1248.       status.engineIsPondering = FALSE;
  1249.  
  1250.       if (status.engineIsActive)
  1251.       {
  1252.          if (getCurrentVariation()->terminateSearchOnPonderhit &&
  1253.              getCurrentVariation()->failingLow == FALSE)
  1254.          {
  1255. #ifdef DEBUG_GUI_CONVERSATION
  1256.             logDebug("immediate termination of pondering.\n");
  1257. #endif
  1258.  
  1259.             prepareSearchAbort();
  1260.          }
  1261.          else
  1262.          {
  1263. #ifdef DEBUG_GUI_CONVERSATION
  1264.             logDebug("unsetting pondering mode.\n");
  1265. #endif
  1266.  
  1267.             unsetPonderMode();
  1268.             startPostPonderCalculation();
  1269.          }
  1270.       }
  1271.       else
  1272.       {
  1273. #ifdef DEBUG_GUI_CONVERSATION
  1274.          logDebug("Pondering finished prematurely. Sending best move info\n");
  1275. #endif
  1276.  
  1277.          sendBestmoveInfo(getCurrentVariation());
  1278.       }
  1279.  
  1280.       releaseGuiSearchMutex();
  1281.  
  1282.       return TRUE;
  1283.    }
  1284.  
  1285.    if (strcmp(buffer, "go") == 0)
  1286.    {
  1287.       getGuiSearchMutex();
  1288.       status.engineIsActive = TRUE;
  1289.       task.type = TASKTYPE_BEST_MOVE;
  1290.  
  1291.       initializeVariationFromGame(&variation, &game);
  1292.       status.engineColor = variation.singlePosition.activeColor;
  1293.  
  1294.       if (getUciToken(command, "depth") != 0)
  1295.       {
  1296.          status.operationMode = XBOARD_OPERATIONMODE_ANALYSIS;
  1297.       }
  1298.       else if (getUciToken(command, "nodes") != 0)
  1299.       {
  1300.          status.operationMode = XBOARD_OPERATIONMODE_ANALYSIS;
  1301.       }
  1302.       else if (getUciToken(command, "mate") != 0)
  1303.       {
  1304.          task.type = TASKTYPE_MATE_IN_N;
  1305.          task.numberOfMoves = getLongUciValue(command, "mate", 1);
  1306.          status.operationMode = XBOARD_OPERATIONMODE_ANALYSIS;
  1307.  
  1308. #ifdef   DEBUG_GUI_PROTOCOL
  1309.          logDebug("Searching mate in %d", task.numberOfMoves);
  1310. #endif
  1311.       }
  1312.       else if (getUciToken(command, "movetime") != 0)
  1313.       {
  1314.          status.operationMode = XBOARD_OPERATIONMODE_USERGAME;
  1315.  
  1316.          status.timecontrolData[WHITE].restTime =
  1317.             status.timecontrolData[BLACK].restTime = -1;
  1318.          status.timecontrolData[WHITE].incrementTime =
  1319.             status.timecontrolData[BLACK].incrementTime =
  1320.             getLongUciValue(command, "movetime", 5000);
  1321.       }
  1322.       else if (getUciToken(command, "infinite") != 0)
  1323.       {
  1324.          status.operationMode = XBOARD_OPERATIONMODE_ANALYSIS;
  1325.       }
  1326.       else
  1327.       {
  1328.          const int numMovesPlayed = variation.singlePosition.moveNumber - 1;
  1329.          const int movesToGo = (int) getLongUciValue(command, "movestogo", 0);
  1330.  
  1331.          status.operationMode = XBOARD_OPERATIONMODE_USERGAME;
  1332.  
  1333.          status.timecontrolData[WHITE].restTime =
  1334.             getLongUciValue(command, "wtime", 1000);
  1335.          status.timecontrolData[WHITE].incrementTime =
  1336.             getLongUciValue(command, "winc", 0);
  1337.          status.timecontrolData[BLACK].restTime =
  1338.             getLongUciValue(command, "btime", 1000);
  1339.          status.timecontrolData[BLACK].incrementTime =
  1340.             getLongUciValue(command, "binc", 0);
  1341.          status.timecontrolData[WHITE].numberOfMovesPlayed =
  1342.             status.timecontrolData[BLACK].numberOfMovesPlayed =
  1343.             numMovesPlayed;
  1344.  
  1345.          if (movesToGo > 0)
  1346.          {
  1347.             status.timecontrolData[WHITE].movesToGo =
  1348.                status.timecontrolData[BLACK].movesToGo = movesToGo;
  1349.          }
  1350.          else
  1351.          {
  1352.             status.timecontrolData[WHITE].movesToGo =
  1353.                status.timecontrolData[BLACK].movesToGo = 0;
  1354.          }
  1355.       }
  1356.  
  1357.       if (getUciToken(command, "ponder") == 0)
  1358.       {
  1359.          status.engineIsPondering = variation.ponderMode = FALSE;
  1360.       }
  1361.       else
  1362.       {
  1363.          status.engineIsPondering = variation.ponderMode = TRUE;
  1364.          variation.terminateSearchOnPonderhit = FALSE;  /* avoid premature search aborts */
  1365.       }
  1366.  
  1367.       startCalculation();
  1368.       releaseGuiSearchMutex();
  1369.  
  1370.       return TRUE;
  1371.    }
  1372.  
  1373.    if (strcmp(buffer, "settransentry") == 0)
  1374.    {
  1375.       char value[256];
  1376.       Hashentry entry;
  1377.       bool entryIsValid = TRUE;
  1378.  
  1379.       getStringUciValue(command, "key", value);
  1380.  
  1381.       if (strlen(value) > 0)
  1382.       {
  1383.          /* printf("key value: %s\n",value); */
  1384.  
  1385.          entry.key = getUnsignedLongLongFromHexString(value);
  1386.       }
  1387.       else
  1388.       {
  1389.          entryIsValid = FALSE;
  1390.       }
  1391.  
  1392.       getStringUciValue(command, "data", value);
  1393.  
  1394.       if (strlen(value) > 0)
  1395.       {
  1396.          /* printf("data value: %s\n",value); */
  1397.  
  1398.          entry.data = getUnsignedLongLongFromHexString(value);
  1399.       }
  1400.       else
  1401.       {
  1402.          entryIsValid = FALSE;
  1403.       }
  1404.  
  1405.       if (entryIsValid)
  1406.       {
  1407.          addHashentry(&entry);
  1408.       }
  1409.    }
  1410.  
  1411.    if (strcmp(buffer, "quit") == 0)
  1412.    {
  1413.       status.engineIsPondering = FALSE;
  1414.       prepareSearchAbort();
  1415.  
  1416.       return FALSE;
  1417.    }
  1418.  
  1419.    return TRUE;
  1420. }
  1421.  
  1422. /******************************************************************************
  1423.  *
  1424.  * Read xboard's commands from stdin and handle them.
  1425.  *
  1426.  ******************************************************************************/
  1427. void acceptGuiCommands()
  1428. {
  1429.    bool finished = FALSE;
  1430.    char command[8192];
  1431.  
  1432.    /* signal(SIGINT, SIG_IGN); */
  1433.  
  1434.    while (finished == FALSE)
  1435.    {
  1436.       if (fgets(command, sizeof(command), stdin) == NULL)
  1437.       {
  1438.          finished = TRUE;
  1439.       }
  1440.       else
  1441.       {
  1442.          trim(command);
  1443.  
  1444. #ifdef DEBUG_GUI_CONVERSATION
  1445.          logDebug("\n### gui command: >%s<\n", command);
  1446. #endif
  1447.  
  1448.          finished = (bool) (processUciCommand(command) == FALSE);
  1449.  
  1450. #ifdef DEBUG_GUI_CONVERSATION
  1451.          logDebug(">%s< processed.\n\n", command);
  1452. #endif
  1453.       }
  1454.    }
  1455.  
  1456. #ifdef   DEBUG_GUI_PROTOCOL
  1457.    logDebug("GUI command processing terminated.\n");
  1458. #endif
  1459. }
  1460.  
  1461. /******************************************************************************
  1462.  *
  1463.  * (See the header file comment for this function.)
  1464.  *
  1465.  ******************************************************************************/
  1466. int initializeModuleXboard()
  1467. {
  1468.    status.operationMode = XBOARD_OPERATIONMODE_USERGAME;
  1469.    status.engineColor = WHITE;
  1470.    status.pondering = TRUE;
  1471.    status.ponderingMove = NO_MOVE;
  1472.    status.engineIsPondering = FALSE;
  1473.    status.engineIsActive = FALSE;
  1474.    status.bestMoveWasSent = TRUE;
  1475.    status.maxPlies = 0;
  1476.    status.timecontrolData[WHITE].movesToGo = 0;
  1477.    status.timecontrolData[WHITE].incrementTime = 0;
  1478.    status.timecontrolData[WHITE].numberOfMovesPlayed = 0;
  1479.    status.timecontrolData[WHITE].restTime = 300 * 1000;
  1480.    status.timecontrolData[BLACK].movesToGo = 0;
  1481.    status.timecontrolData[BLACK].incrementTime =
  1482.       status.timecontrolData[WHITE].incrementTime;
  1483.    status.timecontrolData[BLACK].numberOfMovesPlayed = 0;
  1484.    status.timecontrolData[BLACK].restTime =
  1485.       status.timecontrolData[WHITE].restTime;
  1486.    deletePonderResult();
  1487.  
  1488.    initializePGNGame(&game);
  1489.    variation.timeLimit = 5000;
  1490.    variation.ponderMode = FALSE;
  1491.    variation.handleSearchEvent = &handleSearchEvent;
  1492.    task.variation = &variation;
  1493.    task.type = TASKTYPE_BEST_MOVE;
  1494.  
  1495.    return 0;
  1496. }
  1497.  
  1498. #ifndef NDEBUG
  1499.  
  1500. /******************************************************************************
  1501.  *
  1502.  * Test parameter parsing.
  1503.  *
  1504.  ******************************************************************************/
  1505. static int testParameterParsing()
  1506. {
  1507.    char buffer[1024];
  1508.  
  1509.    getTokenByNumber("protover 2", 1, buffer);
  1510.    assert(strcmp("2", buffer) == 0);
  1511.  
  1512.    getTokenByNumber("result 1-0 {White mates}", 2, buffer);
  1513.    assert(strcmp("{White mates}", buffer) == 0);
  1514.  
  1515.    return 0;
  1516. }
  1517.  
  1518. /******************************************************************************
  1519.  *
  1520.  * Test time calculation.
  1521.  *
  1522.  ******************************************************************************/
  1523. static int testTimeCalculation()
  1524. {
  1525.    return 0;
  1526. }
  1527.  
  1528. /******************************************************************************
  1529.  *
  1530.  * Test the uci tokenizer.
  1531.  *
  1532.  ******************************************************************************/
  1533. static int testUciTokenizer()
  1534. {
  1535.    char buffer[64], name[64], value[64];
  1536.    const char *uciString =
  1537.       "setoption name\tNalimovPath    value  \t  C:\\chess\\tablebases   time  641273423";
  1538.    const char *trickyUciString =
  1539.       "setoption name\tNalimovPathvalue    value  \t  C:\\chess\\tablebases   time  641273423 tablebases";
  1540.    const char *token1 = getUciToken(uciString, "NalimovPath");
  1541.    const char *token2 = getUciToken(uciString, "tablebases");
  1542.    const char *token3 = getUciToken(uciString, "name");
  1543.    const char *token4 = getUciToken(uciString, "value");
  1544.    const char *token5 = getUciToken(trickyUciString, "tablebases");
  1545.  
  1546.    assert(strstr(token1, "NalimovPath") == token1);
  1547.    assert(token2 == 0);
  1548.    assert(strstr(token3, "name") == token3);
  1549.  
  1550.    getNextUciToken(token3 + 4, buffer);
  1551.    assert(strcmp(buffer, "NalimovPath") == 0);
  1552.  
  1553.    getNextUciToken(token4 + 5, buffer);
  1554.    assert(strcmp(buffer, "C:\\chess\\tablebases") == 0);
  1555.  
  1556.    assert(getLongUciValue(uciString, "time", 0) == 641273423);
  1557.  
  1558.    assert(strstr(trickyUciString, "423 tablebases") == token5 - 4);
  1559.  
  1560.    getUciNamedValue(uciString, name, value);
  1561.    assert(strcmp(name, "NalimovPath") == 0);
  1562.    assert(strcmp(value, "C:\\chess\\tablebases") == 0);
  1563.  
  1564.    getUciNamedValue(trickyUciString, name, value);
  1565.    assert(strcmp(name, "NalimovPathvalue") == 0);
  1566.    assert(strcmp(value, "C:\\chess\\tablebases") == 0);
  1567.  
  1568.    return 0;
  1569. }
  1570.  
  1571. static int testHashUpdate()
  1572. {
  1573.    Hashtable *hashtable = getSharedHashtable();
  1574.    Hashentry *tableHit;
  1575.    char *transEntryStringFormat = "settransentry key %llx data %llx";
  1576.    char commandBuffer[256];
  1577.    UINT64 hashKey = 15021965ull;
  1578.    INT16 value = 2004;
  1579.    INT16 staticValue = 2008;
  1580.    UINT8 importance = 12;
  1581.    UINT16 bestMove = NO_MOVE;
  1582.    UINT8 date = 12;
  1583.    UINT8 flag = HASHVALUE_EXACT;
  1584.    Hashentry entry =
  1585.       constructHashEntry(hashKey, value, staticValue, importance,
  1586.                          bestMove, date, flag);
  1587.  
  1588.    sprintf(commandBuffer, transEntryStringFormat, entry.key, entry.data);
  1589.    processUciCommand(commandBuffer);
  1590.    tableHit = getHashentry(hashtable, hashKey);
  1591.  
  1592.    assert(tableHit != 0);
  1593.    assert(getHashentryValue(tableHit) == value);
  1594.    assert(getHashentryStaticValue(tableHit) == staticValue);
  1595.    assert(getHashentryImportance(tableHit) == importance);
  1596.    assert(getHashentryMove(tableHit) == bestMove);
  1597.    assert(getHashentryDate(tableHit) == hashtable->date);
  1598.    assert(getHashentryFlag(tableHit) == flag);
  1599.  
  1600.    return 0;
  1601. }
  1602.  
  1603. #endif
  1604.  
  1605. /******************************************************************************
  1606.  *
  1607.  * (See the header file comment for this function.)
  1608.  *
  1609.  ******************************************************************************/
  1610. int testModuleXboard()
  1611. {
  1612. #ifndef NDEBUG
  1613.    int result;
  1614.  
  1615.    if ((result = testParameterParsing()) != 0)
  1616.    {
  1617.       return result;
  1618.    }
  1619.  
  1620.    if ((result = testTimeCalculation()) != 0)
  1621.    {
  1622.       return result;
  1623.    }
  1624.  
  1625.    if ((result = testUciTokenizer()) != 0)
  1626.    {
  1627.       return result;
  1628.    }
  1629.  
  1630.    if ((result = testHashUpdate()) != 0)
  1631.    {
  1632.       return result;
  1633.    }
  1634. #endif
  1635.  
  1636.    return 0;
  1637. }
  1638.