Adapt WinBoard front-end to Mighty Lion
[xboard.git] / backend.c
index 0e0f80e..4c9968f 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -566,6 +566,13 @@ ChessSquare aseanArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj
         BlackKing, BlackMan, BlackKnight, BlackRook }
 };
 
+ChessSquare  lionArray[2][BOARD_FILES] = {
+    { WhiteRook, WhiteLion, WhiteBishop, WhiteQueen,
+       WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
+    { BlackRook, BlackLion, BlackBishop, BlackQueen,
+       BlackKing, BlackBishop, BlackKnight, BlackRook }
+};
+
 
 #if (BOARD_FILES>=10)
 ChessSquare ShogiArray[2][BOARD_FILES] = {
@@ -1195,6 +1202,7 @@ InitBackEnd1 ()
       case VariantSChess:     /* S-Chess, should work */
       case VariantGrand:      /* should work */
       case VariantSpartan:    /* should work */
+      case VariantLion:       /* should work */
        break;
       }
     }
@@ -5008,6 +5016,11 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps)
     char buf[MSG_SIZ];
 
     if(moveList[moveNum][1] == '@' && moveList[moveNum][0] == '@') {
+       if(gameInfo.variant == VariantLion || gameInfo.variant == VariantChu) {
+           sprintf(buf, "%s@@@@\n", cps->useUsermove ? "usermove " : "");
+           SendToProgram(buf, cps);
+           return;
+       }
        // null move in variant where engine does not understand it (for analysis purposes)
        SendBoard(cps, moveNum + 1); // send position after move in stead.
        return;
@@ -5050,16 +5063,18 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps)
        }
        else SendToProgram(moveList[moveNum], cps);
       } else
+      if(moveList[moveNum][4] == ';') { // [HGM] lion: move is double-step over intermediate square
+         snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", moveList[moveNum][0], moveList[moveNum][1] - '0', // convert to two moves
+                                              moveList[moveNum][5], moveList[moveNum][6] - '0',
+                                              moveList[moveNum][5], moveList[moveNum][6] - '0',
+                                              moveList[moveNum][2], moveList[moveNum][3] - '0');
+         SendToProgram(buf, cps);
+      } else
       if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed
        if(moveList[moveNum][1] == '@' && (BOARD_HEIGHT < 16 || moveList[moveNum][0] <= 'Z')) { // drop move
          if(moveList[moveNum][0]== '@') snprintf(buf, MSG_SIZ, "@@@@\n"); else
          snprintf(buf, MSG_SIZ, "%c@%c%d%s", moveList[moveNum][0],
                                              moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4);
-       } else if(moveList[moveNum][4] == ';') { // [HGM] lion: move is double-step over intermediate square
-         snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", moveList[moveNum][0], moveList[moveNum][1] - '0', // convert to two moves
-                                              moveList[moveNum][5], moveList[moveNum][6] - '0',
-                                              moveList[moveNum][5], moveList[moveNum][6] - '0',
-                                              moveList[moveNum][2], moveList[moveNum][3] - '0');
        } else
          snprintf(buf, MSG_SIZ, "%c%d%c%d%s", moveList[moveNum][0], moveList[moveNum][1] - '0',
                                               moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4);
@@ -5235,7 +5250,7 @@ UploadGameEvent ()
     SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n");
 }
 
-static int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove
+int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove
 
 void
 CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[7])
@@ -5269,7 +5284,8 @@ ProcessICSInitScript (FILE *f)
 }
 
 
-static int lastX, lastY, lastLeftX, lastLeftY, selectFlag, dragging;
+static int lastX, lastY, lastLeftX, lastLeftY, selectFlag;
+int dragging;
 static ClickType lastClickType;
 
 void
@@ -5289,8 +5305,8 @@ Sweep (int step)
        else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing;
        if(!step) step = -1;
     } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn ||
-           appData.testLegality && (promoSweep == king ||
-           IS_SHOGI(gameInfo.variant) && promoSweep != CHUPROMOTED last && last != CHUPROMOTED promoSweep && last != promoSweep));
+           appData.testLegality && (promoSweep == king || promoSweep == WhiteLion || promoSweep == BlackLion) ||
+           IS_SHOGI(gameInfo.variant) && promoSweep != CHUPROMOTED last && last != CHUPROMOTED promoSweep && last != promoSweep);
     if(toX >= 0) {
        int victim = boards[currentMove][toY][toX];
        boards[currentMove][toY][toX] = promoSweep;
@@ -5394,6 +5410,7 @@ ParseOneMove (char *move, int moveNum, ChessMove *moveType, int *fromX, int *fro
       case WhiteNonPromotion:
       case BlackNonPromotion:
       case NormalMove:
+      case FirstLeg:
       case WhiteCapturesEnPassant:
       case BlackCapturesEnPassant:
       case WhiteKingSideCastle:
@@ -5427,7 +5444,8 @@ ParseOneMove (char *move, int moveNum, ChessMove *moveType, int *fromX, int *fro
        if (appData.testLegality) {
          return (*moveType != IllegalMove);
        } else {
-         return !(*fromX == *toX && *fromY == *toY) && boards[moveNum][*fromY][*fromX] != EmptySquare &&
+         return !(*fromX == *toX && *fromY == *toY && killX < 0) && boards[moveNum][*fromY][*fromX] != EmptySquare &&
+                        // [HGM] lion: if this is a double move we are less critical
                        WhiteOnMove(moveNum) == (boards[moveNum][*fromY][*fromX] < BlackPawn);
        }
 
@@ -6031,6 +6049,10 @@ InitPosition (int redraw)
       pieces = SpartanArray;
       SetCharTable(pieceToChar, "PNBRQ................K......lwg.....c...h..k");
       break;
+    case VariantLion:
+      pieces = lionArray;
+      SetCharTable(pieceToChar, "PNBRQ................LKpnbrq................lk");
+      break;
     case VariantFairy:
       pieces = fairyArray;
       SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk");
@@ -6422,7 +6444,7 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i
     if(gameInfo.variant == VariantChu) {
         int p = piece >= BlackPawn ? BLACK_TO_WHITE piece : piece;
         promotionZoneSize = BOARD_HEIGHT/3;
-        highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteKing;
+        highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion;
     } else if(gameInfo.variant == VariantShogi) {
         promotionZoneSize = BOARD_HEIGHT/3;
         highestPromotingPiece = (int)WhiteAlfil;
@@ -7101,15 +7123,17 @@ MarkByFEN(char *fen)
        DrawPosition(TRUE, NULL);
 }
 
+static char baseMarker[BOARD_RANKS][BOARD_FILES], baseLegal[BOARD_RANKS][BOARD_FILES];
+
 void
 Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {
     typedef char Markers[BOARD_RANKS][BOARD_FILES];
     Markers *m = (Markers *) closure;
-    if(rf == fromY && ff == fromX)
+    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);
+                        || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0);
     else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3;
 }
 
@@ -7118,7 +7142,7 @@ MarkTargetSquares (int clear)
 {
   int x, y, sum=0;
   if(clear) { // no reason to ever suppress clearing
-    for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) sum += marker[y][x], marker[y][x] = 0;
+    for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) sum += marker[y][x], marker[y][x] = baseMarker[y][x] = 0;
     if(!sum) return; // nothing was cleared,no redraw needed
   } else {
     int capt = 0;
@@ -7168,10 +7192,8 @@ CanPromote (ChessSquare piece, int y)
 void
 HoverEvent (int xPix, int yPix, int x, int y)
 {
-       static char baseMarker[BOARD_RANKS][BOARD_FILES], baseLegal[BOARD_RANKS][BOARD_FILES];
        static int oldX = -1, oldY = -1, oldFromX = -1, oldFromY = -1;
        int r, f;
-       if(dragging == 2) DragPieceMove(xPix, yPix); // [HGM] lion: drag without button for second leg
        if(!first.highlight) return;
        if(fromX != oldFromX || fromY != oldFromY)  oldX = oldY = -1; // kludge to fake entry on from-click
        if(x == oldX && y == oldY) return; // only do something if we enter new square
@@ -7297,7 +7319,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
       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;
+      fromX = x; fromY = y; toX = toY = killX = killY = -1;
       if(!appData.oneClick || !OnlyMove(&x, &y, FALSE) ||
         // even if only move, we treat as normal when this would trigger a promotion popup, to allow sweep selection
         appData.sweepSelect && CanPromote(boards[currentMove][fromY][fromX], fromY) && originalY != y) {
@@ -7325,7 +7347,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
     }
 
     /* fromX != -1 */
-    if (clickType == Press && gameMode != EditPosition && killX < 0) {
+    if (clickType == Press && gameMode != EditPosition) {
        ChessSquare fromP;
        ChessSquare toP;
        int frc;
@@ -7337,16 +7359,18 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        fromP = boards[currentMove][fromY][fromX];
        toP = boards[currentMove][y][x];
        frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom || gameInfo.variant == VariantSChess;
-       if ((WhitePawn <= fromP && fromP <= WhiteKing &&
+       if( (killX < 0 || x != fromX || y != fromY) && // [HGM] lion: do not interpret igui as deselect!
+          ((WhitePawn <= fromP && fromP <= WhiteKing &&
             WhitePawn <= toP && toP <= WhiteKing &&
             !(fromP == WhiteKing && toP == WhiteRook && frc) &&
             !(fromP == WhiteRook && toP == WhiteKing && frc)) ||
            (BlackPawn <= fromP && fromP <= BlackKing &&
             BlackPawn <= toP && toP <= BlackKing &&
             !(fromP == BlackRook && toP == BlackKing && frc) && // allow also RxK as FRC castling
-            !(fromP == BlackKing && toP == BlackRook && frc))) {
+            !(fromP == BlackKing && toP == BlackRook && frc)))) {
            /* Clicked again on same color piece -- changed his mind */
            second = (x == fromX && y == fromY);
+           killX = killY = -1;
            if(second && gameMode == AnalyzeMode && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) {
                second = FALSE; // first double-click rather than scond click
                doubleClick = first.excludeMoves; // used by UserMoveEvent to recognize exclude moves
@@ -7417,7 +7441,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
 
     clearFlag = 0;
 
-    if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] && (x != killX || y != killY)) {
+    if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] && (x != killX || y != killY) && !sweepSelecting) {
        if(dragging) DragPieceEnd(xPix, yPix), dragging = 0;
        DisplayMessage(_("only marked squares are legal"),"");
        DrawPosition(TRUE, NULL);
@@ -7429,7 +7453,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
     if(!sweepSelecting) {
        toX = x;
        toY = y;
-    } else sweepSelecting = 0; // this must be the up-click corresponding to the down-click that started the sweep
+    }
 
     saveAnimate = appData.animate;
     if (clickType == Press) {
@@ -7440,7 +7464,6 @@ LeftClick (ClickType clickType, int xPix, int yPix)
            return;
        }
        if(dragging == 2) {  // [HGM] lion: just turn buttonless drag into normal drag, and let release to the job
-           dragging = 1;
            return;
        }
        if(x == killX && y == killY) {              // second click on this square, which was selected as first-leg target
@@ -7466,6 +7489,13 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        } else {
            ClearHighlights();
        }
+    } else if(sweepSelecting) { // this must be the up-click corresponding to the down-click that started the sweep
+       sweepSelecting = 0;
+       if (appData.animate || appData.highlightLastMove) {
+           SetHighlights(fromX, fromY, toX, toY);
+       } else {
+           ClearHighlights();
+       }
     } else {
 #if 0
 // [HGM] this must be done after the move is made, as with arrow it could lead to a board redraw with piece still on from square
@@ -7476,13 +7506,14 @@ LeftClick (ClickType clickType, int xPix, int yPix)
            ClearHighlights();
        }
 #endif
-       if(!dragging || marker[y][x] == 5) { // [HGM] lion: this was the release of a to-click or drag on a cyan square
+       if(marker[y][x] == 5) { // [HGM] lion: this was the release of a to-click or drag on a cyan square
          dragging *= 2;            // flag button-less dragging if we are dragging
          MarkTargetSquares(1);
          if(x == killX && y == killY) killX = killY = -1; else {
            killX = x; killY = y;     //remeber this square as intermediate
            ReportClick("put", x, y); // and inform engine
            ReportClick("lift", x, y);
+           MarkTargetSquares(0);
            return;
          }
        }
@@ -8265,7 +8296,7 @@ HandleMachineMove (char *message, ChessProgramState *cps)
     char realname[MSG_SIZ];
     int fromX, fromY, toX, toY;
     ChessMove moveType;
-    char promoChar;
+    char promoChar, roar;
     char *p, *pv=buf1;
     int machineWhite, oldError;
     char *bookHit;
@@ -8427,8 +8458,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
          snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"),
                    machineMove, _(cps->which));
            DisplayMoveError(buf1);
-            snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d",
-                    machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType);
+            snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c) res=%d",
+                    machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, moveType);
            if (gameMode == TwoMachinesPlay) {
              GameEnds(machineWhite ? BlackWins : WhiteWins,
                        buf1, GE_XBOARD);
@@ -8581,10 +8612,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
            cps->other->maybeThinking = TRUE;
        }
 
+       roar = (killX >= 0 && IS_LION(boards[forwardMostMove][toY][toX]));
+
        ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
 
         if (!pausing && appData.ringBellAfterMoves) {
-           RingBell();
+           if(!roar) RingBell();
        }
 
        /*
@@ -9475,6 +9508,7 @@ ParseGameHistory (char *game)
          case WhiteNonPromotion:
          case BlackNonPromotion:
          case NormalMove:
+         case FirstLeg:
          case WhiteCapturesEnPassant:
          case BlackCapturesEnPassant:
          case WhiteKingSideCastle:
@@ -9632,14 +9666,22 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
        }
         piece = board[toY][toX] = (ChessSquare) fromX;
   } else {
+      ChessSquare victim;
       int i;
 
       if( killX >= 0 && killY >= 0 ) // [HGM] lion: Lion trampled over something
+           victim = board[killY][killX],
            board[killY][killX] = EmptySquare,
            board[EP_STATUS] = EP_CAPTURE;
 
-      if( board[toY][toX] != EmptySquare )
+      if( board[toY][toX] != EmptySquare ) {
            board[EP_STATUS] = EP_CAPTURE;
+           if( (fromX != toX || fromY != toY) && // not igui!
+               (captured == WhiteLion && board[fromY][fromX] != BlackLion ||
+                captured == BlackLion && board[fromY][fromX] != WhiteLion   ) ) { // [HGM] lion: Chu Lion-capture rules
+               board[EP_STATUS] = EP_IRON_LION; // non-Lion x Lion: no counter-strike allowed
+           }
+      }
 
       if( board[fromY][fromX] == WhiteLance || board[fromY][fromX] == BlackLance ) {
            if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi )
@@ -9925,7 +9967,6 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                board[BOARD_HEIGHT-1-k][0] = EmptySquare;
        }
     }
-
 }
 
 /* Updates forwardMostMove */
@@ -10038,8 +10079,6 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar)
        strcat(parseList[forwardMostMove - 1], "#");
        break;
     }
-
-    killX = killY = -1; // [HGM] lion: used up
 }
 
 /* Updates currentMove if not pausing */
@@ -10059,6 +10098,8 @@ ShowMove (int fromX, int fromY, int toX, int toY)
        currentMove = forwardMostMove;
     }
 
+    killX = killY = -1; // [HGM] lion: used up
+
     if (instant) return;
 
     DisplayMove(currentMove - 1);
@@ -10868,7 +10909,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
              result, resultDetails ? resultDetails : "(null)", whosays);
     }
 
-    fromX = fromY = -1; // [HGM] abort any move the user is entering.
+    fromX = fromY = killX = killY = -1; // [HGM] abort any move the user is entering. // [HGM] lion
 
     if(pausing) PauseEvent(); // can happen when we abort a paused game (New Game or Quit)
 
@@ -11364,6 +11405,7 @@ Reset (int redraw, int init)
     ClearPremoveHighlights();
     gotPremove = FALSE;
     alarmSounded = FALSE;
+    killX = killY = -1; // [HGM] lion
 
     GameEnds(EndOfFile, NULL, GE_PLAYER);
     if(appData.serverMovesName != NULL) {
@@ -11544,6 +11586,7 @@ LoadGameOneMove (ChessMove readAhead)
       case WhiteNonPromotion:
       case BlackNonPromotion:
       case NormalMove:
+      case FirstLeg:
       case WhiteKingSideCastle:
       case WhiteQueenSideCastle:
       case BlackKingSideCastle:
@@ -11565,6 +11608,7 @@ LoadGameOneMove (ChessMove readAhead)
         toX = currentMoveString[2] - AAA;
         toY = currentMoveString[3] - ONE;
        promoChar = currentMoveString[4];
+       if(promoChar == ';') promoChar = NULLCHAR;
        break;
 
       case WhiteDrop:
@@ -11731,6 +11775,7 @@ LoadGameOneMove (ChessMove readAhead)
 
        thinkOutput[0] = NULLCHAR;
        MakeMove(fromX, fromY, toX, toY, promoChar);
+       killX = killY = -1; // [HGM] lion: used up
        currentMove = forwardMostMove;
        return TRUE;
     }
@@ -12228,6 +12273,7 @@ GameContainsPosition (FILE *f, ListGame *lg)
            case WhiteNonPromotion:
            case BlackNonPromotion:
            case NormalMove:
+           case FirstLeg:
            case WhiteKingSideCastle:
            case WhiteQueenSideCastle:
            case BlackKingSideCastle:
@@ -12292,6 +12338,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
     if (gameMode != BeginningOfGame) {
       Reset(FALSE, TRUE);
     }
+    killX = killY = -1; // [HGM] lion: in case we did not Reset
 
     gameFileFP = f;
     if (lastLoadGameFP != NULL && lastLoadGameFP != f) {
@@ -12445,6 +12492,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
            break;
 
          case NormalMove:
+         case FirstLeg:
            /* Only a NormalMove can be at the start of a game
             * without a position diagram. */
            if (lastLoadGameStart == EndOfFile ) {