Improve virginity test for engine-defined pieces
[xboard.git] / backend.c
index 36eca33..749bac9 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -295,6 +295,7 @@ ChessSquare promoSweep = EmptySquare, defaultPromoChoice;
 int promoDefaultAltered;
 int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */
 static int initPing = -1;
+static Boolean pieceDefs;
 
 /* States for ics_getting_history */
 #define H_FALSE 0
@@ -954,7 +955,7 @@ extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
 static char resetOptions[] =
        "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
        "-firstInitString \"" INIT_STRING "\" -firstComputerString \"" COMPUTER_STRING "\" "
-       "-firstFeatures \"\" -firstLogo \"\" -firstAccumulateTC 1 "
+       "-firstFeatures \"\" -firstLogo \"\" -firstAccumulateTC 1 -fd \".\" "
        "-firstOptions \"\" -firstNPS -1 -fn \"\" -firstScoreAbs false";
 
 void
@@ -5680,7 +5681,7 @@ LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end, int pane)
        }
        ParsePV(buf+startPV, FALSE, gameMode != AnalyzeMode);
        *start = startPV; *end = index-1;
-       extendGame = (gameMode == AnalyzeMode && appData.autoExtend);
+       extendGame = (gameMode == AnalyzeMode && appData.autoExtend && origIndex - startPV < 5);
        return TRUE;
 }
 
@@ -6027,9 +6028,10 @@ InitPosition (int redraw)
     gameInfo.boardHeight   = 8;
     gameInfo.holdingsSize  = 0;
     nrCastlingRights = -1; /* [HGM] Kludge to indicate default should be used */
-    for(i=0; i<BOARD_FILES-2; i++)
+    for(i=0; i<BOARD_FILES-6; i++)
       initialPosition[CASTLING][i] = initialRights[i] = NoRights; /* but no rights yet */
     initialPosition[EP_STATUS] = EP_NONE;
+    initialPosition[TOUCHED_W] = initialPosition[TOUCHED_B] = 0;
     SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k");
     if(startVariant == gameInfo.variant) // [HGM] nicks: enable nicknames in original variant
          SetCharTable(pieceNickName, appData.pieceNickNames);
@@ -7260,8 +7262,8 @@ Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VO
     if(rf == fromY && ff == fromX && (killX < 0 && !(rt == rf && ft == ff) || abs(ft-killX) < 2 && abs(rt-killY) < 2))
        (*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare
                         || kind == WhiteCapturesEnPassant
-                        || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0);
-    else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3;
+                        || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0), legal[rt][ft] = 1;
+    else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3, legal[rt][ft] = 1;
 }
 
 static int hoverSavedValid;
@@ -7277,7 +7279,7 @@ MarkTargetSquares (int clear)
   } else {
     int capt = 0;
     if(!appData.markers || !appData.highlightDragging || appData.icsActive && gameInfo.variant < VariantShogi ||
-       !appData.testLegality || gameMode == EditPosition) return;
+       !appData.testLegality && !pieceDefs || gameMode == EditPosition) return;
     GenLegal(boards[currentMove], PosFlags(currentMove), Mark, (void*) marker, EmptySquare);
     if(PosFlags(0) & F_MANDATORY_CAPTURE) {
       for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x]>1) capt++;
@@ -7353,7 +7355,8 @@ void ReportClick(char *action, int x, int y)
        char buf[MSG_SIZ]; // Inform engine of what user does
        int r, f;
        if(action[0] == 'l') // mark any target square of a lifted piece as legal to-square, clear markers
-         for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) legal[r][f] = 1, marker[r][f] = 0;
+         for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
+           legal[r][f] = !pieceDefs || !appData.markers, marker[r][f] = 0;
        if(!first.highlight || gameMode == EditPosition) return;
        snprintf(buf, MSG_SIZ, "%s %c%d%s\n", action, x+AAA, y+ONE-'0', controlKey && action[0]=='p' ? "," : "");
        SendToProgram(buf, &first);
@@ -7574,7 +7577,8 @@ LeftClick (ClickType clickType, int xPix, int yPix)
 
     clearFlag = 0;
 
-    if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] && (x != killX || y != killY) && !sweepSelecting) {
+    if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] &&
+       fromX >= BOARD_LEFT && fromX < BOARD_RGHT && (x != killX || y != killY) && !sweepSelecting) {
        if(dragging) DragPieceEnd(xPix, yPix), dragging = 0;
        DisplayMessage(_("only marked squares are legal"),"");
        DrawPosition(TRUE, NULL);
@@ -8727,7 +8731,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                 if( count & 1 ) {
                     score = -score; /* Flip score for winning side */
                 }
-printf("score=%d count=%d\n",score,count);
+
                 if( score > appData.adjudicateLossThreshold ) {
                     break;
                 }
@@ -8886,6 +8890,19 @@ printf("score=%d count=%d\n",score,count);
       startedFromSetupPosition = TRUE;
       return;
     }
+    if(sscanf(message, "piece %s %s", buf2, buf1) == 2) {
+      ChessSquare piece = WhitePawn;
+      char *p=buf2;
+      if(cps != &first || appData.testLegality) return;
+      if(*p == '+') piece = CHUPROMOTED WhitePawn, p++;
+      piece += CharToPiece(*p) - WhitePawn;
+      if(piece < EmptySquare) {
+        pieceDefs = TRUE;
+        ASSIGN(pieceDesc[piece], buf1);
+        if(isupper(*p) && p[1] == '&') { ASSIGN(pieceDesc[WHITE_TO_BLACK piece], buf1); }
+      }
+      return;
+    }
     /* [HGM] Allow engine to set up a position. Don't ask me why one would
      * want this, I was asked to put it in, and obliged.
      */
@@ -9867,6 +9884,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
       if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;
       oldEP = (signed char)board[EP_STATUS];
       board[EP_STATUS] = EP_NONE;
+      board[EP_FILE] = board[EP_RANK] = 100;
 
   if (fromY == DROP_RANK) {
        /* must be first */
@@ -9901,6 +9919,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
            if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
               board[EP_STATUS] = EP_PAWN_MOVE;
            if( toY-fromY==2) {
+               board[EP_FILE] = (fromX + toX)/2; board[EP_RANK] = (fromY + toY)/2;
                if(toX>BOARD_LEFT   && board[toY][toX-1] == BlackPawn &&
                        gameInfo.variant != VariantBerolina || toX < fromX)
                      board[EP_STATUS] = toX | berolina;
@@ -9913,6 +9932,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
            if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
               board[EP_STATUS] = EP_PAWN_MOVE;
            if( toY-fromY== -2) {
+               board[EP_FILE] = (fromX + toX)/2; board[EP_RANK] = (fromY + toY)/2;
                if(toX>BOARD_LEFT   && board[toY][toX-1] == WhitePawn &&
                        gameInfo.variant != VariantBerolina || toX < fromX)
                      board[EP_STATUS] = toX | berolina;
@@ -9922,6 +9942,11 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
           }
        }
 
+       if(fromY == 0) board[TOUCHED_W] |= 1<<fromX; else // new way to keep track of virginity
+       if(fromY == BOARD_HEIGHT-1) board[TOUCHED_B] |= 1<<fromX;
+       if(toY == 0) board[TOUCHED_W] |= 1<<toX; else
+       if(toY == BOARD_HEIGHT-1) board[TOUCHED_B] |= 1<<toX;
+
        for(i=0; i<nrCastlingRights; i++) {
            if(board[CASTLING][i] == fromX && castlingRank[i] == fromY ||
               board[CASTLING][i] == toX   && castlingRank[i] == toY
@@ -11607,6 +11632,8 @@ Reset (int redraw, int init)
        fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",
                redraw, init, gameMode);
     }
+    pieceDefs = FALSE; // [HGM] gen: reset engine-defined piece moves
+    for(i=0; i<EmptySquare; i++) { FREE(pieceDesc[i]); pieceDesc[i] = NULL; }
     CleanupTail(); // [HGM] vari: delete any stored variations
     CommentPopDown(); // [HGM] make sure no comments to the previous game keep hanging on
     pausing = pauseExamInvalid = FALSE;
@@ -11755,11 +11782,20 @@ AutoPlayOneMove ()
            SetHighlights(-1, -1, toX, toY);
        }
     } else {
+        int viaX = moveList[currentMove][5] - AAA;
+        int viaY = moveList[currentMove][6] - ONE;
         fromX = moveList[currentMove][0] - AAA;
         fromY = moveList[currentMove][1] - ONE;
 
         HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */
 
+        if(moveList[currentMove][4] == ';') { // multi-leg
+            ChessSquare piece = boards[currentMove][viaY][viaX];
+           AnimateMove(boards[currentMove], fromX, fromY, viaX, viaY);
+            boards[currentMove][viaY][viaX] = boards[currentMove][fromY][fromX];
+            AnimateMove(boards[currentMove], fromX=viaX, fromY=viaY, toX, toY);
+            boards[currentMove][viaY][viaX] = piece;
+        } else
        AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
 
        if (appData.highlightLastMove) {
@@ -15484,9 +15520,18 @@ ForwardInner (int target)
                SetHighlights(-1, -1, toX, toY);
            }
        } else {
+            int viaX = moveList[target - 1][5] - AAA;
+            int viaY = moveList[target - 1][6] - ONE;
             fromX = moveList[target - 1][0] - AAA;
             fromY = moveList[target - 1][1] - ONE;
            if (target == currentMove + 1) {
+               if(moveList[target - 1][4] == ';') { // multi-leg
+                   ChessSquare piece = boards[currentMove][viaY][viaX];
+                   AnimateMove(boards[currentMove], fromX, fromY, viaX, viaY);
+                   boards[currentMove][viaY][viaX] = boards[currentMove][fromY][fromX];
+                   AnimateMove(boards[currentMove], viaX, viaY, toX, toY);
+                   boards[currentMove][viaY][viaX] = piece;
+               } else
                AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
            }
            if (appData.highlightLastMove) {
@@ -16590,6 +16635,10 @@ EngineDefinedVariant (ChessProgramState *cps, int n)
       v = StringToVariant(s);
       if(v == VariantNormal && strcmp(s, "normal") && !strstr(s, "_normal")) v = VariantUnknown; // garbage is recognized as normal
        if(v == VariantUnknown) { // non-standard variant in list of engine-supported variants
+           if(!strcmp(s, "tenjiku") || !strcmp(s, "dai") || !strcmp(s, "dada") || // ignore Alien-Edition variants
+              !strcmp(s, "maka") || !strcmp(s, "tai") || !strcmp(s, "kyoku") ||
+              !strcmp(s, "checkers") || !strcmp(s, "go") || !strcmp(s, "reversi") ||
+              !strcmp(s, "dark") || !strcmp(s, "alien") || !strcmp(s, "multi") || !strcmp(s, "amazons") ) n++;
            if(--n < 0) safeStrCpy(buf, s, MSG_SIZ);
        }
        if(p) *p++ = ',';