added logo files for xboard
[xboard.git] / backend.c
index 424463d..01721f0 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -243,7 +243,11 @@ static int exiting = 0; /* [HGM] moved to top */
 static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;
 int startedFromPositionFile = FALSE; Board filePosition;       /* [HGM] loadPos */
 Board partnerBoard;     /* [HGM] bughouse: for peeking at partner game          */
+Boolean partnerBoardValid = 0;
+char partnerStatus[MSG_SIZ];
 Boolean partnerUp;
+Boolean originalFlip;
+Boolean twoBoards = 0;
 char endingGame = 0;    /* [HGM] crash: flag to prevent recursion of GameEnds() */
 int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS     */
 VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */
@@ -1103,6 +1107,11 @@ InitBackEnd3 P((void))
 
     InitChessProgram(&first, startedFromSetupPosition);
 
+    if(!appData.noChessProgram) {  /* [HGM] tidy: redo program version to use name from myname feature */
+       free(programVersion);
+       programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
+       sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
+    }
 
     if (appData.icsActive) {
 #ifdef WIN32
@@ -2701,7 +2710,7 @@ read_from_ics(isr, closure, data, count, error)
            if (appData.autoKibitz && started == STARTED_NONE && 
                 !appData.icsEngineAnalyze &&                     // [HGM] [DM] ICS analyze
                (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {
-               if(looking_at(buf, &i, "* kibitzes: ") &&
+               if((looking_at(buf, &i, "* kibitzes: ") || looking_at(buf, &i, "* whispers: ")) &&
                   (StrStr(star_match[0], gameInfo.white) == star_match[0] || 
                    StrStr(star_match[0], gameInfo.black) == star_match[0]   )) { // kibitz of self or opponent
                        suppressKibitz = TRUE;
@@ -2740,6 +2749,7 @@ read_from_ics(isr, closure, data, count, error)
            channel = -1;
            if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") || 
                                           looking_at(buf, &i, "* whispers:") ||
+                                          looking_at(buf, &i, "* kibitzes:") ||
                                           looking_at(buf, &i, "* shouts:") ||
                                           looking_at(buf, &i, "* c-shouts:") ||
                                           looking_at(buf, &i, "--> * ") ||
@@ -2759,6 +2769,13 @@ read_from_ics(isr, closure, data, count, error)
                    chattingPartner = p; break;
                    }
                } else
+               if(buf[i-3] == 'e') // kibitz; look if there is a KIBITZ chatbox
+               for(p=0; p<MAX_CHAT; p++) {
+                   if(!strcmp("kibitzes", chatPartner[p])) {
+                       talker[0] = '['; strcat(talker, "] ");
+                       chattingPartner = p; break;
+                   }
+               } else
                if(buf[i-3] == 'r') // whisper; look if there is a WHISPER chatbox
                for(p=0; p<MAX_CHAT; p++) {
                    if(!strcmp("whispers", chatPartner[p])) {
@@ -2766,14 +2783,23 @@ read_from_ics(isr, closure, data, count, error)
                        chattingPartner = p; break;
                    }
                } else
-               if(buf[i-3] == 't' || buf[oldi+2] == '>') // shout, c-shout or it; look if there is a 'shouts' chatbox
-               for(p=0; p<MAX_CHAT; p++) {
+               if(buf[i-3] == 't' || buf[oldi+2] == '>') {// shout, c-shout or it; look if there is a 'shouts' chatbox
+                 if(buf[i-8] == '-' && buf[i-3] == 't')
+                 for(p=0; p<MAX_CHAT; p++) { // c-shout; check if dedicatesd c-shout box exists
+                   if(!strcmp("c-shouts", chatPartner[p])) {
+                       talker[0] = '('; strcat(talker, ") "); Colorize(ColorSShout, FALSE);
+                       chattingPartner = p; break;
+                   }
+                 }
+                 if(chattingPartner < 0)
+                 for(p=0; p<MAX_CHAT; p++) {
                    if(!strcmp("shouts", chatPartner[p])) {
                        if(buf[oldi+2] == '>') { talker[0] = '<'; strcat(talker, "> "); Colorize(ColorShout, FALSE); }
                        else if(buf[i-8] == '-') { talker[0] = '('; strcat(talker, ") "); Colorize(ColorSShout, FALSE); }
                        else { talker[0] = '['; strcat(talker, "] "); Colorize(ColorShout, FALSE); }
                        chattingPartner = p; break;
                    }
+                 }
                }
                if(chattingPartner<0) // if not, look if there is a chatbox for this indivdual
                for(p=0; p<MAX_CHAT; p++) if(!StrCaseCmp(talker+1, chatPartner[p])) {
@@ -3461,6 +3487,7 @@ read_from_ics(isr, closure, data, count, error)
                        ZippyGameStart(whitename, blackname);
                    }
 #endif /*ZIPPY*/
+                   partnerBoardValid = FALSE; // [HGM] bughouse
                    continue;
                }
 
@@ -3501,6 +3528,7 @@ read_from_ics(isr, closure, data, count, error)
                    Reset(TRUE, TRUE);
                }
 #endif /*ZIPPY*/
+               if(appData.bgObserve && partnerBoardValid) DrawPosition(TRUE, partnerBoard);
                continue;
            }
 
@@ -3647,7 +3675,7 @@ read_from_ics(isr, closure, data, count, error)
                                    gameInfo.white, white_holding,
                                    gameInfo.black, black_holding);
                        }
-
+                       if(!partnerUp) // [HGM] bughouse: when peeking at partner game we already know what he captured...
                         DrawPosition(FALSE, boards[currentMove]);
                        DisplayTitle(str);
                      } else if(appData.bgObserve) { // [HGM] bughouse: holdings of other game => background
@@ -3659,7 +3687,9 @@ read_from_ics(isr, closure, data, count, error)
                         /* [HGM] copy holdings to partner-board holdings area */
                         CopyHoldings(partnerBoard, white_holding, WhitePawn);
                         CopyHoldings(partnerBoard, black_holding, BlackPawn);
+                        if(twoBoards) { partnerUp = 1; flipView = !flipView; } // [HGM] dual: always draw
                         if(partnerUp) DrawPosition(FALSE, partnerBoard);
+                        if(twoBoards) { partnerUp = 0; flipView = !flipView; }
                      }
                    }
                    /* Suppress following prompt */
@@ -3736,7 +3766,7 @@ ParseBoard12(string)
     char promoChar;
     int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
     char *bookHit = NULL; // [HGM] book
-    Boolean weird = FALSE, reqFlag = FALSE;
+    Boolean weird = FALSE, reqFlag = FALSE, repaint = FALSE;
 
     fromX = fromY = toX = toY = -1;
     
@@ -3813,7 +3843,6 @@ ParseBoard12(string)
     if((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)
         && newGameMode == IcsObserving && appData.bgObserve) {
       // [HGM] bughouse: don't act on alien boards while we play. Just parse the board and save it */
-      char buf[MSG_SIZ];
       for (k = 0; k < ranks; k++) {
         for (j = 0; j < files; j++)
           board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(ranks-1-k)*(files+1) + j]);
@@ -3823,10 +3852,14 @@ ParseBoard12(string)
         }
       }
       CopyBoard(partnerBoard, board);
-      if(partnerUp) DrawPosition(FALSE, partnerBoard);
-      sprintf(buf, "W: %d:%d B: %d:%d (%d-%d) %c", white_time/60000, (white_time%60000)/1000,
+      if(appData.dualBoard && !twoBoards) { twoBoards = repaint = 1; InitDrawingSizes(-2,0); }
+      if(twoBoards) { partnerUp = 1; flipView = !flipView; } // [HGM] dual
+      if(partnerUp) DrawPosition(repaint, partnerBoard);
+      if(twoBoards) { partnerUp = 0; flipView = !flipView; } // [HGM] dual
+      sprintf(partnerStatus, "W: %d:%02d B: %d:%02d (%d-%d) %c", white_time/60000, (white_time%60000)/1000,
                 (black_time/60000), (black_time%60000)/1000, white_stren, black_stren, to_play);
-      DisplayMessage(buf, "");
+      DisplayMessage(partnerStatus, "");
+       partnerBoardValid = TRUE;
       return;
     }
 
@@ -4359,6 +4392,7 @@ ParseBoard12(string)
              ClearPremoveHighlights();
 
       j = seekGraphUp; seekGraphUp = FALSE; // [HGM] seekgraph: when we draw a board, it overwrites the seek graph
+       if(partnerUp) { flipView = originalFlip; partnerUp = FALSE; j = TRUE; } // [HGM] bughouse: restore view
       DrawPosition(j, boards[currentMove]);
 
       DisplayMove(moveNum - 1);
@@ -4715,6 +4749,8 @@ AlphaRank(char *move, int n)
     }
 }
 
+char yy_textstr[8000];
+
 /* Parser for moves from gnuchess, ICS, or user typein box */
 Boolean
 ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
@@ -4727,7 +4763,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
     if (appData.debugMode) {
         fprintf(debugFP, "move to parse: %s\n", move);
     }
-    *moveType = yylexstr(moveNum, move);
+    *moveType = yylexstr(moveNum, move, yy_textstr, sizeof yy_textstr);
 
     switch (*moveType) {
       case WhitePromotionChancellor:
@@ -4821,15 +4857,15 @@ ParsePV(char *pv, Boolean storeComments)
   int fromX, fromY, toX, toY; char promoChar;
   ChessMove moveType;
   Boolean valid;
-  int nr = 0, dummy;
+  int nr = 0;
 
   endPV = forwardMostMove;
   do {
-    while(*pv == ' ') pv++;
-    if(nr == 0 && *pv == '(') pv++; // first (ponder) move can be in parentheses
+    while(*pv == ' ' || *pv == '\n' || *pv == '\t') pv++; // must still read away whitespace
+    if(nr == 0 && !storeComments && *pv == '(') pv++; // first (ponder) move can be in parentheses
     valid = ParseOneMove(pv, endPV, &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
 if(appData.debugMode){
-fprintf(debugFP,"parsePV: %d %c%c%c%c '%s'\n", valid, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, pv);
+fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, yy_textstr, pv);
 }
     if(!valid && nr == 0 &&
        ParseOneMove(pv, endPV-1, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)){ 
@@ -4848,30 +4884,13 @@ fprintf(debugFP,"parsePV: %d %c%c%c%c '%s'\n", valid, fromX+AAA, fromY+ONE, toX+
           strcpy(moveList[endPV-2], "_0_0"); // suppress premove highlight on takeback move
         }
       }
-    if(moveType == Comment) {
-       // [HGM] vari: try to skip comment
-       int level = 0; char c, *start = pv, wait = NULLCHAR;
-       do {
-           if(!wait) {
-               if(*pv == '(') level++; else
-               if(*pv == ')' && level) level--;
-           }
-           if(*pv == wait) wait = NULLCHAR; else
-           if(*pv == '{') wait = '}'; else
-           if(*pv == '[') wait = ']';
-           pv++;
-       } while(*pv && (wait || level));
-       if(storeComments) {
-         c = *pv; *pv = NULLCHAR;
-         AppendComment(endPV, start, FALSE);
-         *pv = c;
-       }
-      valid++; // allow comments in PV
+    pv = strstr(pv, yy_textstr) + strlen(yy_textstr); // skip what we parsed
+    if(nr == 0 && !storeComments && *pv == ')') pv++; // closing parenthesis of ponder move;
+    if(moveType == Comment && storeComments) AppendComment(endPV, yy_textstr, FALSE);
+    if(moveType == Comment || moveType == NAG || moveType == ElapsedTime) {
+       valid++; // allow comments in PV
        continue;
     }
-    if(sscanf(pv, "%d...", &dummy) == 1 || sscanf(pv, "%d.", &dummy) == 1)
-       while(*pv && *pv++ != ' '); // skip any move numbers
-    while(*pv && *pv++ != ' '); // skip what we parsed; assume space separators
     nr++;
     if(endPV+1 > framePtr) break; // no space, truncate
     if(!valid) break;
@@ -4903,14 +4922,17 @@ Boolean
 LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
 {
        int startPV;
+       char *p;
 
        if(index < 0 || index >= strlen(buf)) return FALSE; // sanity
        lastX = x; lastY = y;
        while(index > 0 && buf[index-1] != '\n') index--; // beginning of line
        startPV = index;
-      while(buf[index] != '\n') if(buf[index++] == '\t') startPV = index;
-      index = startPV;
-       while(buf[index] && buf[index] != '\n') index++;
+       while(buf[index] != '\n') if(buf[index++] == '\t') startPV = index;
+       if(index == startPV && (p = StrCaseStr(buf+index, "PV="))) startPV = p - buf + 3;
+       index = startPV;
+       do{ while(buf[index] && buf[index] != '\n') index++;
+       } while(buf[index] == '\n' && buf[index+1] == '\\' && buf[index+2] == ' ' && index++); // join kibitzed PV continuation line
        buf[index] = 0;
        ParsePV(buf+startPV, FALSE);
        *start = startPV; *end = index-1;
@@ -6448,8 +6470,19 @@ int RightClick(ClickType action, int x, int y, int *fromX, int *fromY)
 
     if((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)
         && !appData.zippyPlay && appData.bgObserve) { // [HGM] bughouse: show background game
-       if(action == Press)   { flipView = !flipView; DrawPosition(TRUE, partnerBoard); partnerUp = TRUE; } else
-       if(action == Release) { flipView = !flipView; DrawPosition(TRUE, boards[currentMove]); partnerUp = FALSE; }
+       if(!partnerBoardValid) return -2; // suppress display of uninitialized boards
+       if( appData.dualBoard) return -2; // [HGM] dual: is already displayed
+       if(action == Press)   {
+           originalFlip = flipView;
+           flipView = !flipView; // temporarily flip board to see game from partners perspective
+           DrawPosition(TRUE, partnerBoard);
+           DisplayMessage(partnerStatus, "");
+           partnerUp = TRUE;
+       } else if(action == Release) {
+           flipView = originalFlip;
+           DrawPosition(TRUE, boards[currentMove]);
+           partnerUp = FALSE;
+       }
        return -2;
     }
 
@@ -8894,7 +8927,10 @@ GameEnds(result, resultDetails, whosays)
 
     if(endingGame) return; /* [HGM] crash: forbid recursion */
     endingGame = 1;
-
+    if(twoBoards) { // [HGM] dual: switch back to one board
+       twoBoards = partnerUp = 0; InitDrawingSizes(-2, 0);
+       DrawPosition(TRUE, partnerBoard); // observed game becomes foreground
+    }
     if (appData.debugMode) {
       fprintf(debugFP, "GameEnds(%d, %s, %d)\n",
              result, resultDetails ? resultDetails : "(null)", whosays);
@@ -10164,7 +10200,9 @@ LoadGame(f, gameNumber, title, useList)
     if (numPGNTags > 0){
         char *tags;
        if (gameInfo.variant == VariantNormal) {
-         gameInfo.variant = StringToVariant(gameInfo.event);
+         VariantClass v = StringToVariant(gameInfo.event);
+         // [HGM] do not recognize variants from event tag that were introduced after supporting variant tag
+         if(v < VariantShogi) gameInfo.variant = v;
        }
        if (!matchMode) {
           if( appData.autoDisplayTags ) {
@@ -13351,8 +13389,10 @@ ReceiveFromProgram(isr, closure, message, count, error)
                   sscanf(message, "error %c", &c)!=1 && sscanf(message, "illegal %c", &c)!=1 &&
                   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, "pong %c", &c)!=1   && start != '#')
-                       { quote = "# "; print = (appData.engineComments == 2); }
+                  sscanf(message, "pong %c", &c)!=1   && start != '#') {
+                   quote = appData.engineComments == 2 ? "# " : "### NON-COMPLIANT! ### ";
+                   print = (appData.engineComments >= 2);
+               }
                message[0] = start; // restore original message
        }
        if(print) {