Subversion Repositories Games.Chess Giants

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
112 pmbaty 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
}