removed trailing whitespace
[xboard.git] / backend.c
index 13afbc5..34ffd0a 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -5,7 +5,7 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
@@ -224,6 +224,7 @@ FILE *WriteTourneyFile P((char *results, FILE *f));
 void DisplayTwoMachinesTitle P(());
 static void ExcludeClick P((int index));
 void ToggleSecond P((void));
+void PauseEngine P((ChessProgramState *cps));
 
 #ifdef WIN32
        extern void ConsoleCreate();
@@ -875,7 +876,7 @@ ReplaceEngine (ChessProgramState *cps, int n)
 extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params;
 extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
 
-static char resetOptions[] = 
+static char resetOptions[] =
        "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
        "-firstInitString \"" INIT_STRING "\" -firstComputerString \"" COMPUTER_STRING "\" "
        "-firstFeatures \"\" -firstLogo \"\" -firstAccumulateTC 1 "
@@ -947,7 +948,7 @@ Load (ChessProgramState *cps, int i)
        if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
        quote = strchr(p, '"') ? '\'' : '"'; // use single quotes around engine command if it contains double quotes
        snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s\n",
-                       quote, p, quote, appData.directory[i], 
+                       quote, p, quote, appData.directory[i],
                        useNick ? " -fn \"" : "",
                        useNick ? nickName : "",
                        useNick ? "\"" : "",
@@ -1284,16 +1285,16 @@ ParseTimeControl (char *tc, float ti, int mps)
 
     if(mps)
       snprintf(buf, MSG_SIZ, ":%d/%s+%g", mps, mytc, ti);
-    else 
+    else
       snprintf(buf, MSG_SIZ, ":%s+%g", mytc, ti);
   } else {
     if(mps)
       snprintf(buf, MSG_SIZ, ":%d/%s", mps, mytc);
-    else 
+    else
       snprintf(buf, MSG_SIZ, ":%s", mytc);
   }
   fullTimeControlString = StrSave(buf); // this should now be in PGN format
-  
+
   if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
     return FALSE;
   }
@@ -5415,7 +5416,8 @@ ParsePV (char *pv, Boolean storeComments, Boolean atEnd)
   Boolean valid;
   int nr = 0;
 
-  if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
+  lastParseAttempt = pv; if(!*pv) return;    // turns out we crash when we parse an empty PV
+  if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && currentMove < forwardMostMove) {
     PushInner(currentMove, forwardMostMove); // [HGM] engine might not be thinking on forwardMost position!
     pushed = TRUE;
   }
@@ -5520,7 +5522,7 @@ PvToSAN (char *pv)
        static char buf[10*MSG_SIZ];
        int i, k=0, savedEnd=endPV, saveFMM = forwardMostMove;
        *buf = NULLCHAR;
-       if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV);
+       if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV); // shelve PV of PV-walk
        ParsePV(pv, FALSE, 2); // this appends PV to game, suppressing any display of it
        for(i = forwardMostMove; i<endPV; i++){
            if(i&1) snprintf(buf+k, 10*MSG_SIZ-k, "%s ", parseList[i]);
@@ -5528,6 +5530,7 @@ PvToSAN (char *pv)
            k += strlen(buf+k);
        }
        snprintf(buf+k, 10*MSG_SIZ-k, "%s", lastParseAttempt); // if we ran into stuff that could not be parsed, print it verbatim
+       if(pushed) { PopInner(0); pushed = FALSE; } // restore game continuation shelved by ParsePV
        if(forwardMostMove < savedEnd) { PopInner(0); forwardMostMove = saveFMM; } // PopInner would set fmm to endPV!
        endPV = savedEnd;
        return buf;
@@ -6079,7 +6082,7 @@ InitPosition (int redraw)
       initialPosition[2][0] = BlackAngel;
       initialPosition[6][BOARD_WIDTH-1] = WhiteMarshall;
       initialPosition[5][BOARD_WIDTH-1] = WhiteAngel;
-      initialPosition[1][1] = initialPosition[2][1] = 
+      initialPosition[1][1] = initialPosition[2][1] =
       initialPosition[6][BOARD_WIDTH-2] = initialPosition[5][BOARD_WIDTH-2] = 1;
      }
   if (appData.debugMode) {
@@ -6534,7 +6537,7 @@ OKToStartUserMove (int x, int y)
 }
 
 Boolean
-OnlyMove (int *x, int *y, Boolean captures) 
+OnlyMove (int *x, int *y, Boolean captures)
 {
     DisambiguateClosure cl;
     if (appData.zippyPlay || !appData.testLegality) return FALSE;
@@ -6747,12 +6750,12 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar)
     if( (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) && fromY != DROP_RANK ) {
          if( pup != EmptySquare ) return;
          moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
-          if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n", 
+          if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n",
                moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]);
           // holdings might not be sent yet in ICS play; we have to figure out which piece belongs here
           if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down
           fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings
-          while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++; 
+          while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
          fromY = DROP_RANK;
     }
 
@@ -7127,7 +7130,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        return;
       }
       doubleClick = FALSE;
-      if(gameMode == AnalyzeMode && pausing && first.excludeMoves) { // use pause state to exclude moves
+      if(gameMode == AnalyzeMode && (pausing || controlKey) && first.excludeMoves) { // use pause state to exclude moves
        doubleClick = TRUE; gatingPiece = boards[currentMove][y][x];
       }
       fromX = x; fromY = y; toX = toY = -1;
@@ -7209,7 +7212,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
            }
           }
           if(x == fromX && y == fromY) return; // if OnlyMove altered (x,y) we go on
-          second = FALSE; 
+          second = FALSE;
        }
        // ignore clicks on holdings
        if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
@@ -7611,6 +7614,9 @@ MatingPotential (int pCnt[], int side, int nMine, int nHis, int stale, int bisCo
                        || majors + (12*pCnt[BlackFerz-side] | 6*pCnt[BlackAlfil-side]) > 16; // KCKAA, KCKAX, KCKEEX, KCKEXX (XX!=HH), KCKXXX
                // TO DO: cases wih an unpromoted f-Pawn acting as platform for an opponent Cannon
 
+       } else if(v == VariantKnightmate) {
+               if(nMine == 1) return FALSE;
+               if(nMine == 2 && nHis == 1 && pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side] + pCnt[WhiteKnight+side]) return FALSE; // KBK is only draw
        } else if(pCnt[WhiteKing] == 1 && pCnt[BlackKing] == 1) { // other variants with orthodox Kings
                int nBishops = pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side];
 
@@ -7663,7 +7669,7 @@ Adjudicate (ChessProgramState *cps)
        //       In any case it determnes if the game is a claimable draw (filling in EP_STATUS).
        //       Actually ending the game is now based on the additional internal condition canAdjudicate.
        //       Only when the game is ended, and the opponent is a computer, this opponent gets the move relayed.
-       int k, count = 0; static int bare = 1;
+       int k, drop, count = 0; static int bare = 1;
        ChessProgramState *engineOpponent = (gameMode == TwoMachinesPlay ? cps->other : (cps ? NULL : &first));
        Boolean canAdjudicate = !appData.icsActive;
 
@@ -7818,10 +7824,12 @@ Adjudicate (ChessProgramState *cps)
 
                 /* Check for rep-draws */
                 count = 0;
+                drop = gameInfo.holdingsSize && (gameInfo.variant != VariantSuper && gameInfo.variant != VariantSChess
+                                              && gameInfo.variant != VariantGreat && gameInfo.variant != VariantGrand);
                 for(k = forwardMostMove-2;
-                    k>=backwardMostMove && k>=forwardMostMove-100 &&
+                    k>=backwardMostMove && k>=forwardMostMove-100 && (drop ||
                         (signed char)boards[k][EP_STATUS] < EP_UNKNOWN &&
-                        (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE;
+                        (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE);
                     k-=2)
                 {   int rights=0;
                     if(CompareBoards(boards[k], boards[forwardMostMove])) {
@@ -8013,7 +8021,7 @@ LoadError (char *errmess, ChessProgramState *cps)
     if(cps->initDone) return FALSE;
     cps->isr = NULL; // this should suppress further error popups from breaking pipes
     DestroyChildProcess(cps->pr, 9 ); // just to be sure
-    cps->pr = NoProc; 
+    cps->pr = NoProc;
     if(cps == &first) {
        appData.noChessProgram = TRUE;
        gameMode = MachinePlaysBlack; ModeHighlight(); // kludge to unmark Machine Black menu
@@ -8038,6 +8046,8 @@ DeferredBookMove (void)
 }
 
 static int savedWhitePlayer, savedBlackPlayer, pairingReceived;
+static ChessProgramState *stalledEngine;
+static char stashedInputMove[MSG_SIZ];
 
 void
 HandleMachineMove (char *message, ChessProgramState *cps)
@@ -8103,6 +8113,22 @@ 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 opponent out of ponder
+               if(gameMode == TwoMachinesPlay) {
+                   if(cps->other->pause)
+                       PauseEngine(cps->other);
+                   else
+                       SendToProgram("easy\n", cps->other);
+               }
+           }
+           StopClocks();
+           return;
+       }
+
         /* This method is only useful on engines that support ping */
         if (cps->lastPing != cps->lastPong) {
          if (gameMode == BeginningOfGame) {
@@ -8370,7 +8396,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
       return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands!
     }
 
-    if ((!appData.testLegality || gameInfo.variant == VariantFairy) && 
+    if ((!appData.testLegality || gameInfo.variant == VariantFairy) &&
                                        !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position
       int dummy, s=6; char buf[MSG_SIZ];
       if(appData.icsActive || forwardMostMove != 0 || cps != &first) return;
@@ -9426,8 +9452,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                ) {
        /* white pawn promotion */
         board[toY][toX] = CharToPiece(ToUpper(promoChar));
-        if(gameInfo.variant==VariantBughouse ||
-           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
+        if((gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse)
+           && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
             board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
        board[fromY][fromX] = EmptySquare;
     } else if ((fromY >= BOARD_HEIGHT>>1)
@@ -9487,8 +9513,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                ) {
        /* black pawn promotion */
        board[toY][toX] = CharToPiece(ToLower(promoChar));
-        if(gameInfo.variant==VariantBughouse ||
-           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
+        if((gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse)
+           && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
             board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
        board[fromY][fromX] = EmptySquare;
     } else if ((fromY < BOARD_HEIGHT>>1)
@@ -9748,12 +9774,12 @@ ShowMove (int fromX, int fromY, int toX, int toY)
     if (instant) return;
 
     DisplayMove(currentMove - 1);
-    DrawPosition(FALSE, boards[currentMove]);
     if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
            if (appData.highlightLastMove) { // [HGM] moved to after DrawPosition, as with arrow it could redraw old board
                SetHighlights(fromX, fromY, toX, toY);
            }
     }
+    DrawPosition(FALSE, boards[currentMove]);
     DisplayBothClocks();
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
 }
@@ -10331,7 +10357,7 @@ Pairing (int nr, int nPlayers, int *whitePlayer, int *blackPlayer, int *syncInte
        *blackPlayer = curRound + appData.tourneyType;
     }
 
-    // take care of white/black alternation per round. 
+    // take care of white/black alternation per round.
     // For cycles and games this is already taken care of by default, derived from matchGame!
     return curRound & 1;
 }
@@ -10380,7 +10406,7 @@ NextTourneyGame (int nr, int *swapColors)
            SendToProgram(buf, &pairing);
            return 0; // wait for pairing engine to answer (which causes NextTourneyGame to be called again...
        }
-       pairingReceived = 0;                              // ... so we continue here 
+       pairingReceived = 0;                              // ... so we continue here
        *swapColors = 0;
        appData.matchGames = appData.tourneyCycles * syncInterval - 1;
        whitePlayer = savedWhitePlayer-1; blackPlayer = savedBlackPlayer-1;
@@ -10504,6 +10530,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
@@ -10647,6 +10675,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
                    if (*appData.savePositionFile != NULLCHAR) {
                        SavePositionToFile(appData.savePositionFile);
                    }
+                   AddGameToBook(FALSE); // Only does something during Monte-Carlo book building
                }
            }
 
@@ -11752,12 +11781,12 @@ InitSearch ()
        if(piece < BlackPawn) piece += BlackPawn; else if(piece < EmptySquare) piece -= BlackPawn; // color-flip
        reverseBoard[r][f] = piece;
     }
-    reverseBoard[EP_STATUS-1] = soughtBoard[EP_STATUS-1] ^ 3; 
+    reverseBoard[EP_STATUS-1] = soughtBoard[EP_STATUS-1] ^ 3;
     for(r=0; r<6; r++) reverseBoard[CASTLING][r] = boards[currentMove][CASTLING][(r+3)%6];
     if(appData.findMirror && appData.searchMode <= 3 && (!nrCastlingRights
-                || (boards[currentMove][CASTLING][2] == NoRights || 
+                || (boards[currentMove][CASTLING][2] == NoRights ||
                     boards[currentMove][CASTLING][0] == NoRights && boards[currentMove][CASTLING][1] == NoRights )
-                && (boards[currentMove][CASTLING][5] == NoRights || 
+                && (boards[currentMove][CASTLING][5] == NoRights ||
                     boards[currentMove][CASTLING][3] == NoRights && boards[currentMove][CASTLING][4] == NoRights ) )
       ) {
        flipSearch = TRUE;
@@ -11779,6 +11808,7 @@ InitSearch ()
 }
 
 GameInfo dummyInfo;
+static int creatingBook;
 
 int
 GameContainsPosition (FILE *f, ListGame *lg)
@@ -12242,6 +12272,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
        cm = (ChessMove) Myylex();
     }
 
+  if(!creatingBook) {
     if (first.pr == NoProc) {
        StartChessProgram(&first);
     }
@@ -12254,6 +12285,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
     }
        DisplayBothClocks();
     }
+  }
 
     /* [HGM] server: flag to write setup moves in broadcast file as one */
     loadFlag = appData.suppressLoadMoves;
@@ -12318,11 +12350,10 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
     if (oldGameMode == AnalyzeFile ||
        oldGameMode == AnalyzeMode) {
       appData.loadGameIndex = -1; // [HGM] order auto-stepping through games
-      keepInfo = 1;
       AnalyzeFileEvent();
-      keepInfo = 0;
     }
 
+    if(creatingBook) return TRUE;
     if (!matchMode && pos > 0) {
        ToNrEvent(pos); // [HGM] no autoplay if selected on position
     } else
@@ -13357,6 +13388,20 @@ ExitEvent (int 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 ()
 {
     if (appData.debugMode)
@@ -13364,8 +13409,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 == 2) 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();
@@ -13377,7 +13438,7 @@ PauseEvent ()
            Reset(FALSE, TRUE);
            SendToICS(ics_prefix);
            SendToICS("refresh\n");
-       } else if (currentMove < forwardMostMove) {
+       } else if (currentMove < forwardMostMove && gameMode != AnalyzeMode) {
            ForwardInner(forwardMostMove);
        }
        pauseExamInvalid = FALSE;
@@ -13408,12 +13469,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(appData.ponderNextMove) SendToProgram("easy\n", onMove); // pre-emptively bring out of ponder
+           } else if(gameMode == (WhiteOnMove(forwardMostMove) ? MachinePlaysWhite : MachinePlaysBlack)) { // engine on move
+               if(first.pause) {
+                   PauseEngine(&first);
+                   StopClocks();
+               } else if(appData.ponderNextMove) SendToProgram("easy\n", &first); // pre-emptively bring out of ponder
+           } else { // human on move, pause pondering by either method
+               if(first.pause)
+                   PauseEngine(&first);
+               else if(appData.ponderNextMove)
+                   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();
@@ -13549,7 +13627,9 @@ AnalyzeFileEvent ()
     }
 
     if (gameMode != AnalyzeMode) {
+       keepInfo = 1; // mere annotating should not alter PGN tags
        EditGameEvent();
+       keepInfo = 0;
        if (gameMode != EditGame) return;
        if (!appData.showThinking) ToggleShowThinking();
        ResurrectChessProgram();
@@ -13562,7 +13642,6 @@ AnalyzeFileEvent ()
     gameMode = AnalyzeFile;
     pausing = FALSE;
     ModeHighlight();
-    SetGameInfo();
 
     StartAnalysisClock();
     GetTimeMark(&lastNodeCountTime);
@@ -13738,7 +13817,7 @@ DisplayTwoMachinesTitle ()
                   gameInfo.white, _("vs."), gameInfo.black,
                   nextGame+1, appData.matchGames+1,
                   appData.tourneyType>0 ? "gt" : appData.tourneyType<0 ? "sw" : "rr");
-        } else 
+        } else
         if (first.twoMachinesColor[0] == 'w') {
          snprintf(buf, MSG_SIZ, "%s %s %s (%d-%d-%d)",
                   gameInfo.white, _("vs."),  gameInfo.black,
@@ -14813,7 +14892,7 @@ BackwardInner (int target)
                for(i=target; i>backwardMostMove; i--) { // seek back to start or previous null move
                    if(moveList[i-1][1] == '@' && moveList[i-1][0] == '@') break;
                }
-               SendBoard(&first, i); 
+               SendBoard(&first, i);
              if(second.analyzing) SendBoard(&second, i);
                for(currentMove=i; currentMove<target; currentMove++) {
                    SendMoveToProgram(currentMove, &first);
@@ -15037,6 +15116,40 @@ HintEvent ()
 }
 
 void
+CreateBookEvent ()
+{
+    ListGame * lg = (ListGame *) gameList.head;
+    FILE *f;
+    int nItem;
+    static int secondTime = FALSE;
+
+    if( !(f = GameFile()) || ((ListGame *) gameList.tailPred)->number <= 0 ) {
+        DisplayError(_("Game list not loaded or empty"), 0);
+        return;
+    }
+
+    if(!secondTime && (f = fopen(appData.polyglotBook, "r"))) {
+        fclose(f);
+       secondTime++;
+       DisplayNote(_("Book file exists! Try again for overwrite."));
+       return;
+    }
+
+    creatingBook = TRUE;
+    secondTime = FALSE;
+
+    /* Get list size */
+    for (nItem = 1; nItem <= ((ListGame *) gameList.tailPred)->number; nItem++){
+       LoadGame(f, nItem, "", TRUE);
+       AddGameToBook(TRUE);
+        lg = (ListGame *) lg->node.succ;
+    }
+
+    creatingBook = FALSE;
+    FlushBook();
+}
+
+void
 BookEvent ()
 {
     if (appData.noChessProgram) return;
@@ -15259,7 +15372,7 @@ ReplaceComment (int index, char *text)
     char *p;
     float score;
 
-    if(index && sscanf(text, "%f/%d", &score, &len) == 2 && 
+    if(index && sscanf(text, "%f/%d", &score, &len) == 2 &&
        pvInfoList[index-1].depth == len &&
        fabs(pvInfoList[index-1].score - score*100.) < 0.5 &&
        (p = strchr(text, '\n'))) text = p; // [HGM] strip off first line with PV info, if any
@@ -15572,7 +15685,7 @@ ReceiveFromProgram (InputSourceRef isr, VOIDSTAR closure, char *message, int cou
                   sscanf(message, "tell%c", &c)!=1   && sscanf(message, "0-1 %c", &c)!=1 &&
                   sscanf(message, "1-0 %c", &c)!=1   && sscanf(message, "1/2-1/2 %c", &c)!=1 &&
                   sscanf(message, "setboard %c", &c)!=1   && sscanf(message, "setup %c", &c)!=1 &&
-                  sscanf(message, "hint: %c", &c)!=1 && 
+                  sscanf(message, "hint: %c", &c)!=1 &&
                   sscanf(message, "pong %c", &c)!=1   && start != '#') {
                    quote = appData.engineComments == 2 ? "# " : "### NON-COMPLIANT! ### ";
                    print = (appData.engineComments >= 2);
@@ -15910,7 +16023,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;
@@ -16054,9 +16167,9 @@ AskQuestionEvent (char *title, char *question, char *replyPrefix, char *which)
 void
 TypeInEvent (char firstChar)
 {
-    if ((gameMode == BeginningOfGame && !appData.icsActive) || 
+    if ((gameMode == BeginningOfGame && !appData.icsActive) ||
         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
-       gameMode == AnalyzeMode || gameMode == EditGame || 
+       gameMode == AnalyzeMode || gameMode == EditGame ||
        gameMode == EditPosition || gameMode == IcsExamining ||
        gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
        isdigit(firstChar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
@@ -16092,16 +16205,16 @@ TypeInDoneEvent (char *move)
            return;
         }
 
-      if (gameMode != EditGame && currentMove != forwardMostMove && 
+      if (gameMode != EditGame && currentMove != forwardMostMove &&
        gameMode != Training) {
        DisplayMoveError(_("Displayed move is not current"));
       } else {
-       int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, 
+       int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
          &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
        if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
-       if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, 
+       if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
          &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
-         UserMoveEvent(fromX, fromY, toX, toY, promoChar);     
+         UserMoveEvent(fromX, fromY, toX, toY, promoChar);
        } else {
          DisplayMoveError(_("Could not parse move"));
        }
@@ -17519,7 +17632,7 @@ LoadTheme ()
        snprintf(buf, MSG_SIZ, "\"%s\"", nickName);
       if(appData.useBitmaps) {
        snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt true -lbtf \"%s\" -dbtf \"%s\" -lbtm %d -dbtm %d",
-               appData.liteBackTextureFile, appData.darkBackTextureFile, 
+               appData.liteBackTextureFile, appData.darkBackTextureFile,
                appData.liteBackTextureMode,
                appData.darkBackTextureMode );
       } else {