Implement use of pause / resume protocol commands
authorH.G. Muller <h.g.muller@hccnet.nl>
Wed, 5 Dec 2012 15:34:58 +0000 (16:34 +0100)
committerH.G. Muller <h.g.muller@hccnet.nl>
Fri, 7 Dec 2012 21:27:37 +0000 (22:27 +0100)
Engines that implement feature pause=1 will be paused during their move
on a pause event; pondering engines will be controlled either through
'pause' or (if they don't support that) 'easy', which both should work
instantaneously. If the thinking engine does not support 'pause', the pausing
will be deferred to when it produces its move, and is implemented by not
relaying the move, and switch off pondering for both.

backend.c
backend.h

index e9d10dc..7f9b664 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -8038,6 +8038,8 @@ DeferredBookMove (void)
 }
 
 static int savedWhitePlayer, savedBlackPlayer, pairingReceived;
+static ChessProgramState *stalledEngine;
+static char stashedInputMove[MSG_SIZ];
 
 void
 HandleMachineMove (char *message, ChessProgramState *cps)
@@ -8103,6 +8105,18 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||
        (sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0))
     {
+        if(pausing && !cps->pause) { // for pausing engine that does not support 'pause', we stash its move for processing when we resume.
+           if(appData.debugMode) fprintf(debugFP, "pause %s engine after move\n", cps->which);
+           safeStrCpy(stashedInputMove, message, MSG_SIZ);
+           stalledEngine = cps;
+           if(appData.ponderNextMove) { // bring both engines out of ponder
+               SendToProgram("easy\n", &first);
+               if(gameMode == TwoMachinesPlay) SendToProgram("easy\n", &second);
+           }
+           StopClocks();
+           return;
+       }
+
         /* This method is only useful on engines that support ping */
         if (cps->lastPing != cps->lastPong) {
          if (gameMode == BeginningOfGame) {
@@ -10504,6 +10518,8 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
 
     fromX = fromY = -1; // [HGM] abort any move the user is entering.
 
+    if(pausing) PauseEvent(); // can happen when we abort a paused game (New Game or Quit)
+
     if (appData.icsActive && (whosays == GE_ENGINE || whosays >= GE_ENGINE1)) {
        /* If we are playing on ICS, the server decides when the
           game is over, but the engine can offer to draw, claim
@@ -13361,6 +13377,20 @@ ExitEvent (int status)
     exit(status);
 }
 
+void
+PauseEngine (ChessProgramState *cps)
+{
+    SendToProgram("pause\n", cps);
+    cps->pause = 2;
+}
+
+void
+UnPauseEngine (ChessProgramState *cps)
+{
+    SendToProgram("resume\n", cps);
+    cps->pause = 1;
+}
+
 void
 PauseEvent ()
 {
@@ -13369,8 +13399,24 @@ PauseEvent ()
     if (pausing) {
        pausing = FALSE;
        ModeHighlight();
+       if(stalledEngine) { // [HGM] pause: resume game by releasing withheld move
+           StartClocks();
+           if(gameMode == TwoMachinesPlay) { // we might have to make the opponent resume pondering
+               if(stalledEngine->other->pause) UnPauseEngine(stalledEngine->other);
+               else if(appData.ponderNextMove) SendToProgram("hard\n", stalledEngine->other);
+           }
+           if(appData.ponderNextMove) SendToProgram("hard\n", stalledEngine);
+           HandleMachineMove(stashedInputMove, stalledEngine);
+           stalledEngine = NULL;
+           return;
+       }
        if (gameMode == MachinePlaysWhite ||
-           gameMode == MachinePlaysBlack) {
+           gameMode == TwoMachinesPlay   ||
+           gameMode == MachinePlaysBlack) { // the thinking engine must have used pause mode, or it would have been stalledEngine
+           if(first.pause)  UnPauseEngine(&first);
+           else if(appData.ponderNextMove) SendToProgram("hard\n", &first);
+           if(second.pause) UnPauseEngine(&second);
+           else if(gameMode == TwoMachinesPlay && appData.ponderNextMove) SendToProgram("hard\n", &second);
            StartClocks();
        } else {
            DisplayBothClocks();
@@ -13413,12 +13459,29 @@ PauseEvent ()
          case TwoMachinesPlay:
            if (forwardMostMove == 0)
              return;           /* don't pause if no one has moved */
-           if ((gameMode == MachinePlaysWhite &&
-                !WhiteOnMove(forwardMostMove)) ||
-               (gameMode == MachinePlaysBlack &&
-                WhiteOnMove(forwardMostMove))) {
+           if(gameMode == TwoMachinesPlay) { // [HGM] pause: stop clocks if engine can be paused immediately
+               ChessProgramState *onMove = (WhiteOnMove(forwardMostMove) == (first.twoMachinesColor[0] == 'w') ? &first : &second);
+               if(onMove->pause) {           // thinking engine can be paused
+                   PauseEngine(onMove);      // do it
+                   if(onMove->other->pause)  // pondering opponent can always be paused immediately
+                       PauseEngine(onMove->other);
+                   else
+                       SendToProgram("easy\n", onMove->other);
+                   StopClocks();
+               }
+           } else if(gameMode == (WhiteOnMove(forwardMostMove) ? MachinePlaysWhite : MachinePlaysBlack)) { // engine on move
+               if(first.pause) {
+                   PauseEngine(&first);
+                   StopClocks();
+               }
+           } else { // human on move, pause pondering by either method
+               if(first.pause) 
+                   PauseEngine(&first);
+               else
+                   SendToProgram("easy\n", &first);
                StopClocks();
            }
+           // if no immediate pausing is possible, wait for engine to move, and stop clocks then
          case AnalyzeMode:
            pausing = TRUE;
            ModeHighlight();
@@ -15949,7 +16012,7 @@ ParseFeatures (char *args, ChessProgramState *cps)
     if (BoolFeature(&p, "exclude", &cps->excludeMoves, cps)) continue;
     if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;
     if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;
-    if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */
+    if (BoolFeature(&p, "pause", &cps->pause, cps)) continue; // [HGM] pause
     if (IntFeature(&p, "done", &val, cps)) {
       FeatureDone(cps, val);
       continue;
index 2a3d3d8..7003bd0 100644 (file)
--- a/backend.h
+++ b/backend.h
@@ -395,6 +395,7 @@ typedef struct XB_CPS {
     int memSize;      /* [HGM] memsize: engine understands memory command   */
     char egtFormats[MSG_SIZ];     /* [HGM] EGT: supported tablebase formats */
     int bookSuspend;  /* [HGM] book: go was deferred because of book hit    */
+    int pause;        /* [HGM] pause: 1=supports it, 2=actually paused      */
     int nrOptions;    /* [HGM] options: remembered option="..." features    */
 #define MAX_OPTIONS 200
     Option option[MAX_OPTIONS];