Prevent transmission of spurious promo char to other engine
[xboard.git] / backend.c
index a76adef..b124be6 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -131,9 +131,16 @@ extern int gettimeofday(struct timeval *, struct timezone *);
 #ifdef ENABLE_NLS 
 # define _(s) gettext (s) 
 # define N_(s) gettext_noop (s) 
+# define T_(s) gettext(s)
 #else 
-# define _(s) (s) 
-# define N_(s) s 
+# ifdef WIN32
+#   define _(s) T_(s)
+#   define N_(s) s
+# else
+#   define _(s) (s) 
+#   define N_(s) s 
+#   define T_(s) s
+# endif
 #endif 
 
 
@@ -234,6 +241,7 @@ char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move
 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
 void ics_update_width P((int new_width));
 extern char installDir[MSG_SIZ];
+VariantClass startVariant; /* [HGM] nicks: initial variant */
 
 extern int tinyLayout, smallLayout;
 ChessProgramStats programStats;
@@ -637,6 +645,7 @@ InitBackEnd1()
     int matched, min, sec;
 
     ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
+    startVariant = StringToVariant(appData.variant); // [HGM] nicks: remember original variant
 
     GetTimeMark(&programStartTime);
     srandom((programStartTime.ms + 1000*programStartTime.sec)*0x1001001); // [HGM] book: makes sure random is unpredictabe to msec level
@@ -712,8 +721,8 @@ InitBackEnd1()
     /* [AS] Adjudication threshold */
     adjudicateLossThreshold = appData.adjudicateLossThreshold;
     
-    first.which = "first";
-    second.which = "second";
+    first.which = _("first");
+    second.which = _("second");
     first.maybeThinking = second.maybeThinking = FALSE;
     first.pr = second.pr = NoProc;
     first.isr = second.isr = NULL;
@@ -2204,8 +2213,8 @@ MatchSoughtLine(char *line)
 int
 DrawSeekGraph()
 {
-    if(!seekGraphUp) return FALSE;
     int i;
+    if(!seekGraphUp) return FALSE;
     h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
     w = BOARD_WIDTH  * (squareSize + lineGap) + lineGap;
 
@@ -2836,18 +2845,9 @@ read_from_ics(isr, closure, data, count, error)
                 /* [DM] Backup address for color zippy lines */
                 backup = i;
 #if ZIPPY
-       #ifdef WIN32
                if (loggedOn == TRUE)
                        if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||
                           (appData.zippyPlay && ZippyMatch(buf, &backup)));
-       #else
-                if (ZippyControl(buf, &i) ||
-                    ZippyConverse(buf, &i) ||
-                    (appData.zippyPlay && ZippyMatch(buf, &i))) {
-                     loggedOn = TRUE;
-                      if (!appData.colorize) continue;
-               }
-       #endif
 #endif
            } // [DM] 'else { ' deleted
                if (
@@ -4530,7 +4530,7 @@ SendMoveToProgram(moveNum, cps)
     /*       Send 'go' if we are in a mode where machine should play. */
     if( (moveNum == 0 && setboardSpoiledMachineBlack && cps == &first) &&
         (gameMode == TwoMachinesPlay   ||
-#ifdef ZIPPY
+#if ZIPPY
          gameMode == IcsPlayingBlack     || gameMode == IcsPlayingWhite ||
 #endif
          gameMode == MachinePlaysBlack || gameMode == MachinePlaysWhite) ) {
@@ -4575,6 +4575,10 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY)
       /* POP Fabien */
        sprintf(user_move, "o-o-o\n");
        break;
+      case WhiteNonPromotion:
+      case BlackNonPromotion:
+        sprintf(user_move, "%c%c%c%c=\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY);
+        break;
       case WhitePromotionQueen:
       case BlackPromotionQueen:
       case WhitePromotionRook:
@@ -4803,6 +4807,8 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
       case BlackPromotionKnight:
       case WhitePromotionKing:
       case BlackPromotionKing:
+      case WhiteNonPromotion:
+      case BlackNonPromotion:
       case NormalMove:
       case WhiteCapturesEnPassant:
       case BlackCapturesEnPassant:
@@ -5260,7 +5266,10 @@ InitPosition(redraw)
     for(i=0; i<BOARD_FILES-2; i++)
       initialPosition[CASTLING][i] = initialRights[i] = NoRights; /* but no rights yet */
     initialPosition[EP_STATUS] = EP_NONE;
-    SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k"); 
+    SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k");
+    if(startVariant == gameInfo.variant) // [HGM] nicks: enable nicknames in original variant
+         SetCharTable(pieceNickName, appData.pieceNickNames);
+    else SetCharTable(pieceNickName, "............");
 
     switch (gameInfo.variant) {
     case VariantFischeRandom:
@@ -6599,8 +6608,9 @@ Count(Board board, int pCnt[], int *nW, int *nB, int *wStale, int *bStale, int *
        for(p=WhitePawn; p<=EmptySquare; p++) pCnt[p] = 0;
        for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
                p = board[r][f];
+               pCnt[p]++;
                if(p == WhitePawn && r == BOARD_HEIGHT-1) (*wStale)++; else
-               if(p == BlackPawn && r == 0) (*bStale)++; else pCnt[p]++; // count last-Rank Pawns (XQ) separately
+               if(p == BlackPawn && r == 0) (*bStale)++; // count last-Rank Pawns (XQ) separately
                if(p <= WhiteKing) (*nW)++; else if(p <= BlackKing) (*nB)++;
                if(p == WhiteBishop || p == WhiteFerz || p == WhiteAlfil ||
                   p == BlackBishop || p == BlackFerz || p == BlackAlfil   )
@@ -6609,6 +6619,35 @@ Count(Board board, int pCnt[], int *nW, int *nB, int *wStale, int *bStale, int *
 }
 
 int
+SufficientDefence(int pCnt[], int side, int nMine, int nHis)
+{
+       int myPawns = pCnt[WhitePawn+side]; // my total Pawn count;
+       int majorDefense = pCnt[BlackRook-side] + pCnt[BlackCannon-side] + pCnt[BlackKnight-side];
+                  
+       nMine -= pCnt[WhiteFerz+side] + pCnt[WhiteAlfil+side]; // discount defenders
+       if(nMine - myPawns > 2) return FALSE; // no trivial draws with more than 1 major
+       if(myPawns == 2 && nMine == 3) // KPP
+           return majorDefense || pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] >= 3;
+       if(myPawns == 1 && nMine == 2) // KP
+           return majorDefense || pCnt[BlackFerz-side] + pCnt[BlackAlfil-side]  + pCnt[BlackPawn-side] >= 1;
+       if(myPawns == 1 && nMine == 3 && pCnt[WhiteKnight+side]) // KHP
+           return majorDefense || pCnt[BlackFerz-side] + pCnt[BlackAlfil-side]*2 >= 5;
+       if(myPawns) return FALSE;
+       if(pCnt[WhiteRook+side])
+           return pCnt[BlackRook-side] || 
+                  pCnt[BlackCannon-side] && (pCnt[BlackFerz-side] >= 2 || pCnt[BlackAlfil-side] >= 2) ||
+                  pCnt[BlackKnight-side] && pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] > 2 ||
+                  pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] >= 4;
+       if(pCnt[WhiteCannon+side]) {
+           if(pCnt[WhiteFerz+side] + myPawns == 0) return TRUE; // Cannon needs platform
+           return majorDefense || pCnt[BlackAlfil-side] >= 2;
+       }
+       if(pCnt[WhiteKnight+side])
+           return majorDefense || pCnt[BlackFerz-side] >= 2 || pCnt[BlackAlfil-side] + pCnt[BlackPawn-side] >= 1;
+       return FALSE;
+}
+
+int
 MatingPotential(int pCnt[], int side, int nMine, int nHis, int stale, int bisColor)
 {
        VariantClass v = gameInfo.variant;
@@ -6802,12 +6841,14 @@ Adjudicate(ChessProgramState *cps)
                 }
 
                 /* Then some trivial draws (only adjudicate, cannot be claimed) */
-                if(nrW + nrB == 4 && 
+                if(gameInfo.variant == VariantXiangqi ?
+                       SufficientDefence(nr, WhitePawn, nrW, nrB) && SufficientDefence(nr, BlackPawn, nrB, nrW)
+                 : nrW + nrB == 4 && 
                    (   nr[WhiteRook] == 1 && nr[BlackRook] == 1 /* KRKR */
                    || nr[WhiteQueen] && nr[BlackQueen]==1     /* KQKQ */
                    || nr[WhiteKnight]==2 || nr[BlackKnight]==2     /* KNNK */
                    || nr[WhiteKnight]+nr[WhiteBishop] == 1 && nr[BlackKnight]+nr[BlackBishop] == 1 /* KBKN, KBKB, KNKN */
-                  ) ) {
+                   ) ) {
                      if(--moveCount < 0 && appData.trivialDraws && canAdjudicate)
                      {    /* if the first 3 moves do not show a tactical win, declare draw */
                          if(engineOpponent) {
@@ -8135,6 +8176,8 @@ ParseGameHistory(game)
          case BlackPromotionKnight:
          case WhitePromotionKing:
          case BlackPromotionKing:
+         case WhiteNonPromotion:
+         case BlackNonPromotion:
          case NormalMove:
          case WhiteCapturesEnPassant:
          case BlackCapturesEnPassant:
@@ -8988,7 +9031,7 @@ GameEnds(result, resultDetails, whosays)
 {
     GameMode nextGameMode;
     int isIcsGame;
-    char buf[MSG_SIZ];
+    char buf[MSG_SIZ], popupRequested = 0;
 
     if(endingGame) return; /* [HGM] crash: forbid recursion */
     endingGame = 1;
@@ -9324,13 +9367,12 @@ GameEnds(result, resultDetails, whosays)
            endingGame = 0; /* [HGM] crash */
            return;
        } else {
-           char buf[MSG_SIZ];
            gameMode = nextGameMode;
            sprintf(buf, _("Match %s vs. %s: final score %d-%d-%d"),
                    first.tidy, second.tidy,
                    first.matchWins, second.matchWins,
                    appData.matchGames - (first.matchWins + second.matchWins));
-           DisplayFatalError(buf, 0, 0);
+           popupRequested++; // [HGM] crash: postpone to after resetting endingGame
        }
     }
     if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
@@ -9339,6 +9381,12 @@ GameEnds(result, resultDetails, whosays)
     gameMode = nextGameMode;
     ModeHighlight();
     endingGame = 0;  /* [HGM] crash */
+    if(popupRequested) { // [HGM] crash: this calls GameEnds recursively through ExitEvent! Make it a harmless tail recursion.
+      if(matchMode == TRUE) DisplayFatalError(buf, 0, 0); else {
+       matchMode = FALSE; appData.matchGames = matchGame = 0;
+       DisplayNote(buf);
+      }
+    }
 }
 
 /* Assumes program was just initialized (initString sent).
@@ -9606,6 +9654,8 @@ LoadGameOneMove(readAhead)
       case BlackPromotionKnight:
       case WhitePromotionKing:
       case BlackPromotionKing:
+      case WhiteNonPromotion:
+      case BlackNonPromotion:
       case NormalMove:
       case WhiteKingSideCastle:
       case WhiteQueenSideCastle:
@@ -13974,7 +14024,7 @@ DisplayMove(moveNumber)
            sprintf(res, " %s", PGNResult(gameInfo.result));
        } else {
            sprintf(res, " {%s} %s",
-                   gameInfo.resultDetails, PGNResult(gameInfo.result));
+                   T_(gameInfo.resultDetails), PGNResult(gameInfo.result));
        }
     } else {
        res[0] = NULLCHAR;
@@ -14766,7 +14816,7 @@ ParseFEN(board, blackPlaysFirst, fen)
      char *fen;
 {
     int i, j;
-    char *p;
+    char *p, c;
     int emptycount;
     ChessSquare piece;
 
@@ -14859,7 +14909,12 @@ ParseFEN(board, blackPlaysFirst, fen)
     while(*p == ' ') p++;
 
     /* Active color */
-    switch (*p++) {
+    c = *p++;
+    if(appData.colorNickNames) {
+      if( c == appData.colorNickNames[0] ) c = 'w'; else
+      if( c == appData.colorNickNames[1] ) c = 'b';
+    }
+    switch (c) {
       case 'w':
         *blackPlaysFirst = FALSE;
        break;