Implement playing by node count
authorH.G.Muller <hgm@hgm-xboard.(none)>
Thu, 12 Apr 2018 12:56:54 +0000 (14:56 +0200)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Thu, 12 Apr 2018 12:56:54 +0000 (14:56 +0200)
The CECP 'nps' command is supported, to allow conversion of the clock times
to a node budget. A new routine for reaing the CPU time that has been used
had to be added for this. ReadClock() got a second parameter for indicating
whether the time is requested for printing (CPU time) or for timing (nodes).

dropper.c

index d0a5ad5..ee8bc83 100644 (file)
--- a/dropper.c
+++ b/dropper.c
@@ -11,7 +11,7 @@
 
 #ifdef WIN32 
 #    include <windows.h>
-#    define CPUtime 1000.*clock
+#    define CPUtime (1000./CLOCKS_PER_SEC)*clock
      int Input()
      {  // checks for waiting input in pipe
        static int init; static HANDLE inp; DWORD cnt;
        gettimeofday(&t, NULL);
        return t.tv_sec*1000 + t.tv_usec/1000;
      }
+     double CPUtime()
+     {  // get CPU time used by process, converted to 'MILLICLOCKS'
+       struct tms cpuTimes;
+       static int cps = 0; static double ms_per_clk;
+       if(!cps) cps = sysconf(_SC_CLK_TCK), ms_per_clk = 1000./cps;
+       times(&cpuTimes);
+       return (cpuTimes.tms_utime + cpuTimes.tms_stime) * ms_per_clk;
+     }
      int Input()
      {
        int cnt;
 typedef long long int Key;
 
 int ply, nodeCount, forceMove, choice, rootMove, lastGameMove, rootScore, abortFlag, postThinking=1; // some frequently used data
-int maxDepth=MAXPLY-2, timeControl=3000, mps=40, inc, timePerMove, timeLeft=1000; // TC parameters
-int rootFirst, rootCurrent, rootLast, rootPly, margin;
+int maxDepth=MAXPLY-2, timeControl=3000, mps=40, inc, timePerMove, timeLeft=1000, nodeRate=-1; // TC parameters
+int rootFirst, rootCurrent, rootLast, rootPly, rootScore, margin;
 
 #define H_LOWER 1
 #define H_UPPER 2
 
-int ReadClock (int start);
+int ReadClock (int start, int print);
 char *MoveToText (int move);
 int TimeIsUp (int mode);
 
@@ -1514,8 +1522,8 @@ printf("%d:%d:%d %2d. %08x %c%d%c%d %6d %6d %6d\n",ply,depth,iterDepth,curMove,m
                    tail = pvPtr; pvPtr = pvStart; *pvPtr++ = moveStack[curMove]; // alpha < score < beta: move starts new PV
                    while(*pvPtr++ = *tail++); // copy PV of daughter node behind it (including 0 sentinel)
                    if(ply == 0) { // in root we print this PV
-                       int xbScore = (score > INF-100 ? 100000 + INF - score : score < 100-INF ? -100000 - score - INF : score);
-                       printf("%d %d %d %d", iterDepth, xbScore, ReadClock(0)/10, nodeCount);
+                       rootScore = (score > INF-100 ? 100000 + INF - score : score < 100-INF ? -100000 - score - INF : score);
+                       printf("%d %d %d %d", iterDepth, rootScore, ReadClock(0, 1)/10, nodeCount);
                        for(tail=pvStart; *tail; tail++) printf(" %s", MoveToText(*tail));
                        printf("\n"); fflush(stdout);
                        ff->move = moveStack[bestNr];
@@ -1605,7 +1613,7 @@ TimeIsUp (int mode)
   int t, targetTime, panicTime;
   if(Input() && DoCommand(1)) return 1; // abort if command waiting that cannot be handled during search
   if(engineSide == ANALYZE) return 0;    // no timing on analyze or ponder
-  t = ReadClock(0);
+  t = ReadClock(0, 0);
   if(timePerMove >= 0) {                               // fixed time per move
     targetTime = panicTime = 10*timeLeft - 30;
   } else if(mps) {                                     // classical TC
@@ -1754,11 +1762,12 @@ int resign;         // engine-defined option
 int contemptFactor; // likewise
 
 int
-ReadClock (int start)
+ReadClock (int start, int print)
 {
-  static int startTime;
-  int t = GetTickCount();
-  if(start) startTime = t;
+  static int startTime; int t;
+  if(nodeRate > 0 && !start && !print) return nodeCount*1000LL/nodeRate;
+  t = (nodeRate >= 0 ? (int)(CPUtime()) : GetTickCount());
+  if(start) startTime = t, nodeCount = 0;
   return t - startTime; // msec
 }
 
@@ -1860,7 +1869,7 @@ printf("# command: %s\n", inBuf);
     if(!strcmp(command, "nopost"))  { postThinking = OFF;return 0; }
     if(!strcmp(command, "random"))  { randomize = ON;    return 0; }
     if(!strcmp(command, "."))       { // periodic update request;
-      printf("stat01: %d %d %d %d %d %s\n", ReadClock(0)/10, nodeCount, rootPly,
+      printf("stat01: %d %d %d %d %d %s\n", ReadClock(0, 1)/10, nodeCount, rootPly,
             rootLast - rootCurrent + 1, rootLast - rootFirst, MoveToText(moveStack[rootCurrent]));
       fflush(stdout); return 0;
     }
@@ -1887,7 +1896,8 @@ printf("# command: %s\n", inBuf);
       return 1;
     }
     if(!strcmp(command, "protover")){
-      printf("feature ping=1 setboard=1 colors=0 usermove=1 memory=1 debug=1 reuse=0 sigint=0 sigterm=0 exclude=1 myname=\"CrazyWa " VERSION "\"\n");
+      printf("feature ping=1 setboard=1 colors=0 usermove=1 memory=1 debug=1 reuse=0 sigint=0 sigterm=0 "
+            "exclude=1 nps=1 myname=\"CrazyWa " VERSION "\"\n");
       printf("feature variants=\"crazyhouse,shogi,minishogi,judkinshogi,torishogi,euroshogi,crazywa,kyotoshogi,"
                                "5x5+4_shogi,5x5+5_shogi,6x6+6_shogi,7x7+6_shogi,11x17+16_chu\"\n");
       printf("feature option=\"Resign -check 0\"\n");           // example of an engine-defined option
@@ -1902,10 +1912,11 @@ printf("# command: %s\n", inBuf);
     }
     if(!strcmp(command, "sd"))      { sscanf(inBuf+2, "%d", &maxDepth);    return 1; }
     if(!strcmp(command, "st"))      { sscanf(inBuf+2, "%d", &timePerMove); return 1; }
+    if(!strcmp(command, "nps"))     { sscanf(inBuf+4, "%d", &nodeRate); return 1; }
     if(!strcmp(command, "memory"))  { if(SetMemorySize(atoi(inBuf+7))) printf("tellusererror Not enough memory\n"), exit(-1); return 1; }
     if(!strcmp(command, "ping"))    { printf("pong%s", inBuf+4); return 1; }
 //  if(!strcmp(command, ""))        { sscanf(inBuf, " %d", &); return 1; }
-    if(!strcmp(command, "new"))     { engineSide = BLACK; stm = WHITE; maxDepth = MAXPLY-2; randomize = OFF;
+    if(!strcmp(command, "new"))     { engineSide = BLACK; stm = WHITE; maxDepth = MAXPLY-2; randomize = OFF; nodeRate = -1;
                                      moveNr = 0; ranKey = GetTickCount() | 0x1001; MapClear(0); return 1; }
     if(!strcmp(command, "variant")) { GameInit(inBuf + 8); Setup(startPos); return 1; }
     if(!strcmp(command, "setboard")){ engineSide = NONE;  stm = Setup(inBuf+9); MapClear(0); return 1; }
@@ -1956,10 +1967,11 @@ main ()
 
     if(stm == engineSide) {         // if it is the engine's turn to move, set it thinking, and let it move
 {int i;for(i=0; i<=hashMask+3; i++) /*if(hashTable[i].score != hashTable[i].lim)*/ hashTable[i].lock = 0;}
-      nodeCount = forceMove = undoInfo.move = abortFlag = 0; ReadClock(1);
+      forceMove = undoInfo.move = abortFlag = 0; ReadClock(1, 0);
       for(i=0;i<1<<16;i++) history[i] = 0; //>>= 1;
       for(i=0;i<1<<17;i++) mateKillers[i] = 0;
       score = Search(stm^COLOR, -INF, INF, &undoInfo, maxDepth, 0, maxDepth);
+      if(nodeRate >= 0) printf("%d %d %d %d <done>\n", rootPly, rootScore, ReadClock(0, 1)/10, nodeCount); // make sure total time is printed
 
       if(!undoInfo.move) {             // no move, game apparently ended
         engineSide = NONE;             // so stop playing
@@ -1974,7 +1986,7 @@ main ()
 
     // now it is not our turn (anymore)
     if(engineSide == ANALYZE) {       // in analysis, we always ponder the position
-        nodeCount = forceMove = undoInfo.move = abortFlag = 0; ReadClock(1);
+        forceMove = undoInfo.move = abortFlag = 0; ReadClock(1, 0);
         Search(stm^COLOR, -INF, INF, &undoInfo, maxDepth, 0, maxDepth);
     }
 #if 0