Fix bug in sending cores command to engine
[xboard.git] / backend.c
index c58c091..bb52007 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -1351,6 +1351,19 @@ establish()
     }
 }
 
+void EscapeExpand(char *p, char *q)
+{      // [HGM] initstring: routine to shape up string arguments
+       while(*p++ = *q++) if(p[-1] == '\\')
+           switch(*q++) {
+               case 'n': p[-1] = '\n'; break;
+               case 'r': p[-1] = '\r'; break;
+               case 't': p[-1] = '\t'; break;
+               case '\\': p[-1] = '\\'; break;
+               case 0: *p = 0; return;
+               default: p[-1] = q[-1]; break;
+           }
+}
+
 void
 show_bytes(fp, buf, count)
      FILE *fp;
@@ -3842,7 +3855,7 @@ ParseBoard12(string)
     }
     
     if((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)
-        && newGameMode == IcsObserving && appData.bgObserve) {
+        && newGameMode == IcsObserving && gamenum != ics_gamenum && appData.bgObserve) {
       // [HGM] bughouse: don't act on alien boards while we play. Just parse the board and save it */
       char *toSqr;
       for (k = 0; k < ranks; k++) {
@@ -6253,7 +6266,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
 {
     int x, y;
     Boolean saveAnimate;
-    static int second = 0, promotionChoice = 0;
+    static int second = 0, promotionChoice = 0, dragging = 0;
     char promoChoice = NULLCHAR;
 
     if(appData.seekGraph && appData.icsActive && loggedOn &&
@@ -6313,11 +6326,14 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
                fromY = y;
                second = 0;
                MarkTargetSquares(0);
-               DragPieceBegin(xPix, yPix);
+               DragPieceBegin(xPix, yPix); dragging = 1;
                if (appData.highlightDragging) {
                    SetHighlights(x, y, -1, -1);
                }
            }
+       } else if(dragging) { // [HGM] from-square must have been reset due to game end since last press
+           DragPieceEnd(xPix, yPix); dragging = 0;
+           DrawPosition(FALSE, NULL);
        }
        return;
       }
@@ -6346,7 +6362,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
             !(fromP == BlackKing && toP == BlackRook && frc))) {
            /* Clicked again on same color piece -- changed his mind */
            second = (x == fromX && y == fromY);
-          if(!second || !OnlyMove(&x, &y, TRUE)) {
+          if(!second || appData.oneClick && !OnlyMove(&x, &y, TRUE)) {
            if (appData.highlightDragging) {
                SetHighlights(x, y, -1, -1);
            } else {
@@ -6354,7 +6370,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
            }
            if (OKToStartUserMove(x, y)) {
                fromX = x;
-               fromY = y;
+               fromY = y; dragging = 1;
                MarkTargetSquares(0);
                DragPieceBegin(xPix, yPix);
            }
@@ -6366,7 +6382,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     }
 
     if (clickType == Release && x == fromX && y == fromY) {
-       DragPieceEnd(xPix, yPix);
+       DragPieceEnd(xPix, yPix); dragging = 0;
        if (appData.animateDragging) {
            /* Undo animation damage if any */
            DrawPosition(FALSE, NULL);
@@ -6404,7 +6420,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
        } else {
            ClearHighlights();
        }
-       DragPieceEnd(xPix, yPix);
+       DragPieceEnd(xPix, yPix); dragging = 0;
        /* Don't animate move and drag both */
        appData.animate = FALSE;
     }
@@ -6624,8 +6640,10 @@ Adjudicate(ChessProgramState *cps)
                         case BlackRook:
                              NrBR++; break;
                         case WhiteQueen:
+                        case WhiteCannon:
                              NrWQ++; break;
                         case BlackQueen:
+                        case BlackCannon:
                              NrBQ++; break;
                         case EmptySquare: 
                              break;
@@ -6747,7 +6765,10 @@ Adjudicate(ChessProgramState *cps)
                 if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && 
                                     gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible
                        (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 ||
-                        NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color
+                        NrPieces == NrBB+NrWB+2 && bishopsColor != 3) // [HGM] all Bishops (Ferz!) same color
+                                 || gameInfo.variant == VariantXiangqi &&
+                       (NrPieces == 3 && (NrWQ==1 && NrWB==0 && NrBB<2 || NrBQ==1 && NrBB==0 && NrWB<2) ||
+                        NrPieces == 4 && NrWQ==1 && NrBQ==1 && NrWB==0 && NrBB==0))
                 {    /* KBK, KNK, KK of KBKB with like Bishops */
 
                      /* always flag draws, for judging claims */
@@ -6772,7 +6793,7 @@ Adjudicate(ChessProgramState *cps)
                    || NrWN==2 || NrBN==2     /* KNNK */
                    || NrWN+NrWB == 1 && NrBN+NrBB == 1 /* KBKN, KBKB, KNKN */
                   ) ) {
-                     if(canAdjudicate && --moveCount < 0 && appData.trivialDraws)
+                     if(--moveCount < 0 && appData.trivialDraws && canAdjudicate)
                      {    /* if the first 3 moves do not show a tactical win, declare draw */
                          if(engineOpponent) {
                            SendToProgram("force\n", engineOpponent); // suppress reply
@@ -6823,14 +6844,11 @@ Adjudicate(ChessProgramState *cps)
                                 boards[forwardMostMove][CASTLING][4] != boards[k][CASTLING][4] )
                                    rights++;
                         }
-                        if( canAdjudicate && rights == 0 && ++count > appData.drawRepeats-2
+                        if( rights == 0 && ++count > appData.drawRepeats-2 && canAdjudicate
                             && appData.drawRepeats > 1) {
                              /* adjudicate after user-specified nr of repeats */
-                            if(engineOpponent) {
-                              SendToProgram("force\n", engineOpponent); // suppress reply
-                              SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
-                            }
-                             ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+                            int result = GameIsDrawn;
+                            char *details = "XBoard adjudication: repetition draw";
                             if(gameInfo.variant == VariantXiangqi && appData.testLegality) { 
                                // [HGM] xiangqi: check for forbidden perpetuals
                                int m, ourPerpetual = 1, hisPerpetual = 1;
@@ -6843,27 +6861,31 @@ Adjudicate(ChessProgramState *cps)
                                if(appData.debugMode) fprintf(debugFP, "XQ perpetual test, our=%d, his=%d\n",
                                                                        ourPerpetual, hisPerpetual);
                                if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit
-                                   GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, 
-                                          "Xboard adjudication: perpetual checking", GE_XBOARD );
-                                   return 1;
-                               }
-                               if(hisPerpetual && !ourPerpetual)   // he is checking us, but did not repeat yet
+                                   result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;
+                                   details = "Xboard adjudication: perpetual checking";
+                               } else
+                               if(hisPerpetual && !ourPerpetual) { // he is checking us, but did not repeat yet
                                    break; // (or we would have caught him before). Abort repetition-checking loop.
+                               } else
                                // Now check for perpetual chases
                                if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase
                                    hisPerpetual = PerpetualChase(k, forwardMostMove);
                                    ourPerpetual = PerpetualChase(k+1, forwardMostMove);
                                    if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit
-                                       GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, 
-                                                     "Xboard adjudication: perpetual chasing", GE_XBOARD );
-                                       return 1;
-                                   }
+                                       result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;
+                                       details = "Xboard adjudication: perpetual chasing";
+                                   } else
                                    if(hisPerpetual && !ourPerpetual)   // he is chasing us, but did not repeat yet
                                        break; // Abort repetition-checking loop.
                                }
                                // if neither of us is checking or chasing all the time, or both are, it is draw
                             }
-                             GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD );
+                            if(engineOpponent) {
+                              SendToProgram("force\n", engineOpponent); // suppress reply
+                              SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
+                            }
+                             ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+                             GameEnds( result, details, GE_XBOARD );
                              return 1;
                         }
                         if( rights == 0 && count > 1 ) /* occurred 2 or more times before */
@@ -6880,6 +6902,17 @@ Adjudicate(ChessProgramState *cps)
                 if( count == backwardMostMove )
                     count -= initialRulePlies;
                 count = forwardMostMove - count; 
+               if(gameInfo.variant == VariantXiangqi && ( count >= 100 || count >= 2*appData.ruleMoves ) ) {
+                       // adjust reversible move counter for checks in Xiangqi
+                       int i = forwardMostMove - count, inCheck = 0, lastCheck;
+                       if(i < backwardMostMove) i = backwardMostMove;
+                       while(i <= forwardMostMove) {
+                               lastCheck = inCheck; // check evasion does not count
+                               inCheck = (MateTest(boards[i], PosFlags(i)) == MT_CHECK);
+                               if(inCheck || lastCheck) count--; // check does not count
+                               i++;
+                       }
+               }
                 if( count >= 100)
                          boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
                          /* this is used to judge if draw claims are legal */
@@ -6948,8 +6981,7 @@ char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial)
        // after a book hit we never send 'go', and the code after the call to this routine
        // has '&& !bookHit' added to suppress potential sending there (based on 'firstMove').
        char buf[MSG_SIZ];
-       if (cps->useUsermove) sprintf(buf, "usermove "); // sorry, no SAN yet :(
-       sprintf(buf, "%s\n", bookHit); // force book move into program supposed to play it
+       sprintf(buf, "%s%s\n", (cps->useUsermove ? "usermove " : ""), bookHit); // force book move into program supposed to play it
        SendToProgram(buf, cps);
        if(!initial) firstMove = FALSE; // normally we would clear the firstMove condition after return & sending 'go'
     } else if(initial) { // 'go' was needed irrespective of firstMove, and it has to be done in this routine
@@ -7166,6 +7198,11 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
        strcat(machineMove, "\n");
        strcpy(moveList[forwardMostMove], machineMove);
 
+        /* [AS] Save move info*/
+        pvInfoList[ forwardMostMove ].score = programStats.score;
+        pvInfoList[ forwardMostMove ].depth = programStats.depth;
+        pvInfoList[ forwardMostMove ].time =  programStats.time; // [HGM] PGNtime: take time from engine stats
+
        MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
 
         /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */
@@ -7225,10 +7262,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
        }
 #endif
 
-        /* [AS] Save move info and clear stats for next move */
-        pvInfoList[ forwardMostMove-1 ].score = programStats.score;
-        pvInfoList[ forwardMostMove-1 ].depth = programStats.depth;
-        pvInfoList[ forwardMostMove-1 ].time =  programStats.time; // [HGM] PGNtime: take time from engine stats
+        /* [AS] Clear stats for next move */
         ClearProgramStats();
         thinkOutput[0] = NULLCHAR;
         hiddenThinkOutputState = 0;
@@ -7334,11 +7368,13 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
      * Look for communication commands
      */
     if (!strncmp(message, "telluser ", 9)) {
+       EscapeExpand(message+9, message+9); // [HGM] esc: allow escape sequences in popup box
        DisplayNote(message + 9);
        return;
     }
     if (!strncmp(message, "tellusererror ", 14)) {
        cps->userError = 1;
+       EscapeExpand(message+14, message+14); // [HGM] esc: allow escape sequences in popup box
        DisplayError(message + 14, 0);
        return;
     }
@@ -7792,6 +7828,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
        }
 
        if (!ignore) {
+           ChessProgramStats tempStats = programStats; // [HGM] info: filter out info lines
            buf1[0] = NULLCHAR;
            if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",
                       &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
@@ -7813,11 +7850,11 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
                 }
 
 
-               programStats.depth = plylev;
-               programStats.nodes = nodes;
-               programStats.time = time;
-               programStats.score = curscore;
-               programStats.got_only_move = 0;
+               tempStats.depth = plylev;
+               tempStats.nodes = nodes;
+               tempStats.time = time;
+               tempStats.score = curscore;
+               tempStats.got_only_move = 0;
 
                if(cps->nps >= 0) { /* [HGM] nps: use engine nodes or time to decrement clock */
                        int ticklen;
@@ -7834,31 +7871,34 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
 
                /* Buffer overflow protection */
                if (buf1[0] != NULLCHAR) {
-                   if (strlen(buf1) >= sizeof(programStats.movelist)
+                   if (strlen(buf1) >= sizeof(tempStats.movelist)
                        && appData.debugMode) {
                        fprintf(debugFP,
                                "PV is too long; using the first %u bytes.\n",
-                               (unsigned) sizeof(programStats.movelist) - 1);
+                               (unsigned) sizeof(tempStats.movelist) - 1);
                    }
 
-                    safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );
+                    safeStrCpy( tempStats.movelist, buf1, sizeof(tempStats.movelist) );
                } else {
-                   sprintf(programStats.movelist, " no PV\n");
+                   sprintf(tempStats.movelist, " no PV\n");
                }
 
-               if (programStats.seen_stat) {
-                   programStats.ok_to_send = 1;
+               if (tempStats.seen_stat) {
+                   tempStats.ok_to_send = 1;
                }
 
-               if (strchr(programStats.movelist, '(') != NULL) {
-                   programStats.line_is_book = 1;
-                   programStats.nr_moves = 0;
-                   programStats.moves_left = 0;
+               if (strchr(tempStats.movelist, '(') != NULL) {
+                   tempStats.line_is_book = 1;
+                   tempStats.nr_moves = 0;
+                   tempStats.moves_left = 0;
                } else {
-                   programStats.line_is_book = 0;
+                   tempStats.line_is_book = 0;
                }
 
-                SendProgramStatsToFrontend( cps, &programStats );
+                   if(tempStats.score != 0 || tempStats.nodes != 0 || tempStats.time != 0)
+                       programStats = tempStats; // [HGM] info: only set stats if genuine PV and not an info line
+
+                SendProgramStatsToFrontend( cps, &tempStats );
 
                 /* 
                     [AS] Protect the thinkOutput buffer from overflow... this
@@ -8557,7 +8597,7 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
         if( (boards[forwardMostMove][fromY][fromX] == WhitePawn ||
              boards[forwardMostMove][fromY][fromX] == BlackPawn   ) &&
              boards[forwardMostMove][toY][toX] == EmptySquare
-             && fromX != toX )
+             && fromX != toX && fromY != toY)
                 fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY);
         // promotion suffix
         if(promoChar != NULLCHAR)
@@ -10148,6 +10188,7 @@ LoadGame(f, gameNumber, title, useList)
         /* [HGM] PGNvariant: automatically switch to variant given in PGN tag */
         if(gameInfo.variant != oldVariant) {
             startedFromPositionFile = FALSE; /* [HGM] loadPos: variant switch likely makes position invalid */
+           ResetFrontEnd(); // [HGM] might need other bitmaps. Cannot use Reset() because it clears gameInfo :-(
            InitPosition(TRUE);
             oldVariant = gameInfo.variant;
            if (appData.debugMode) 
@@ -13763,7 +13804,6 @@ ParseFeatures(args, cps)
        }
        continue;
     }
-    if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;
     /* End of additions by HGM */
 
     /* unknown feature: complain and skip */
@@ -13827,17 +13867,17 @@ PonderNextMoveEvent(newState)
 }
 
 void
-NewSettingEvent(option, command, value)
+NewSettingEvent(option, feature, command, value)
      char *command;
-     int option, value;
+     int option, value, *feature;
 {
     char buf[MSG_SIZ];
 
     if (gameMode == EditPosition) EditPositionDone(TRUE);
     sprintf(buf, "%s%s %d\n", (option ? "option ": ""), command, value);
-    SendToProgram(buf, &first);
+    if(feature == NULL || *feature) SendToProgram(buf, &first);
     if (gameMode == TwoMachinesPlay) {
-       SendToProgram(buf, &second);
+       if(feature == NULL || feature[(int*)&second - (int*)&first]) SendToProgram(buf, &second);
     }
 }