Alter assignment method for castling rights in EditPosition
[xboard.git] / backend.c
index 763b0fc..35cc84d 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -242,6 +242,7 @@ int endPV = -1;
 static int exiting = 0; /* [HGM] moved to top */
 static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;
 int startedFromPositionFile = FALSE; Board filePosition;       /* [HGM] loadPos */
+int rightsBoard[BOARD_RANKS][BOARD_FILES];                  /* [HGM] editrights */
 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 */
@@ -5791,6 +5792,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn)
           click-click move is possible */
        if (toX == -2 || toY == -2) {
            boards[0][fromY][fromX] = EmptySquare;
+           rightsBoard[fromY][fromX] = 0;
            return AmbiguousMove;
        } else if (toX >= 0 && toY >= 0) {
            boards[0][toY][toX] = boards[0][fromY][fromX];
@@ -5807,6 +5809,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn)
                }
            } else
            boards[0][fromY][fromX] = EmptySquare;
+           rightsBoard[fromY][fromX] = rightsBoard[toY][toX] = 0;
            return AmbiguousMove;
        }
         return ImpossibleMove;
@@ -6096,6 +6099,68 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
     FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
 }
 
+int hTab[(int)EmptySquare/2+1] = { 1,1,1,1,1,1,2,1,2,3,2,3,3,3,2,3,4,3,3,4,4,3,4 };
+int wTab[(int)EmptySquare/2+1] = { 1,1,2,3,4,5,3,7,4,3,5,4,4,5,7,5,4,6,6,5,5,7,6 };
+Board promoBoard;
+int promotionChoice = 0;
+
+void
+PiecePopUp(int x, int y)
+{
+    int i, j, h, w, nWhite=0, nBlack=0;
+    ChessSquare list[EmptySquare];
+    for(i=0; i<EmptySquare/2; i++) {
+       if(PieceToChar(i) != '.') list[nWhite++] = i;
+       if(PieceToChar(i+EmptySquare/2) != '.') list[EmptySquare - ++nBlack] = i + EmptySquare/2;
+    }
+    CopyBoard(promoBoard, boards[currentMove]);
+    for(i=0; i<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++) promoBoard[i][j] = EmptySquare;
+    j = nWhite + nBlack + 1;
+    h = sqrt((j+1)/2 + 1.); w = (j+h-1)/h;
+    if(w>BOARD_RGHT-BOARD_LEFT) { w = BOARD_RGHT - BOARD_LEFT; h = (j+w-1)/w; }
+    for(i=0; i<nWhite; i++) promoBoard[i/w][BOARD_LEFT+i%w] = list[nWhite-1-i];
+    if(h==2 && nWhite == nBlack)
+       for(i=0; i<nWhite; i++) promoBoard[1][BOARD_LEFT+i%w] = list[EmptySquare-nBlack+i];
+    else
+       for(i=0; i<nBlack; i++) promoBoard[h-1-i/w][BOARD_LEFT+w-1-i%w] = list[EmptySquare-nBlack+i];
+    promotionChoice = 3;
+    ClearHighlights();
+    PromoDialog(h, w, promoBoard, TRUE, _("Select piece:"), x, y);
+}
+
+void
+PromoPopUp(ChessSquare piece)
+{   // determine the layout of the piece-choice dialog
+    int w, h, i, j, nr;
+    ChessSquare list[EmptySquare];
+
+    for(i=0; i<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++) promoBoard[i][j] = EmptySquare;
+    if(gameInfo.variant == VariantShogi) {
+       // non-Pawn promotes; must be shogi
+       h = 1; w = 1; promoBoard[0][BOARD_LEFT+0] = piece;
+       if(PieceToChar(PROMOTED piece) != '.') {
+           // promoted version is enabled
+           w = 2; promoBoard[0][BOARD_LEFT+1] = PROMOTED piece;
+       }
+    } else {
+       // Pawn, promotes to any enabled other piece
+       h = 1; w = nr = 0;
+       for(i=1; i<EmptySquare/2; i++) {
+           if(PieceToChar(piece+i) != '.' 
+          && PieceToChar(piece + i) != '~' // suppress bughouse true pieces
+                                                       ) {
+               list[w++] = piece+i; nr++;
+           }
+       }
+       if(appData.testLegality && gameInfo.variant != VariantSuicide
+                       && gameInfo.variant != VariantGiveaway) nr--,w--; // remove King
+       h = hTab[nr]; w = wTab[nr]; // factorize with nice ratio
+       for(i=0; i < nr; i++) promoBoard[i/w][BOARD_LEFT+i%w] = list[i]; // layout
+    }
+    promotionChoice = 2; // wait for click on board
+    PromoDialog(h, w, promoBoard, FALSE, _("Promote to:"), -1, -1);
+}
+
 void
 Mark(board, flags, kind, rf, ff, rt, ft, closure)
      Board board;
@@ -6137,7 +6202,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
 {
     int x, y;
     Boolean saveAnimate;
-    static int second = 0, promotionChoice = 0;
+    static int second = 0;
     char promoChoice = NULLCHAR;
 
     if(appData.seekGraph && appData.icsActive && loggedOn &&
@@ -6159,22 +6224,33 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     }
 
     if(promotionChoice) { // we are waiting for a click to indicate promotion piece
+       ChessSquare p = EmptySquare; Boolean inHoldings;
+       if(promotionChoice == 3) {
+           if(clickType == Press) EditPositionMenuEvent(promoBoard[y][x], fromX, fromY);
+           else if(clickType == Release) promotionChoice = 0;
+           fromX = fromY = -1;
+           return;
+       }
        if(clickType == Release) return; // ignore upclick of click-click destination
-       promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel
        if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);
-       if(gameInfo.holdingsWidth && 
+       inHoldings = gameInfo.holdingsWidth &&
                (WhiteOnMove(currentMove) 
                        ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0
-                       : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {
+                       : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1);
            // click in right holdings, for determining promotion piece
-           ChessSquare p = boards[currentMove][y][x];
+       if(promotionChoice == 1 && inHoldings || promotionChoice == 2 && x >= BOARD_LEFT && x < BOARD_RGHT) {
+           p = promoBoard[y][x];
            if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);
            if(p != EmptySquare) {
-               FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));
+               char promoChar = PieceToChar(p);
+               if(gameInfo.variant == VariantShogi && promoChar != '+') promoChar = '=';
+               FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(promoChar));
                fromX = fromY = -1;
+               promotionChoice = 0;
                return;
            }
        }
+       promotionChoice = 0; // only one chance: if click not OK it is interpreted as cancel
        DrawPosition(FALSE, boards[currentMove]);
        return;
     }
@@ -6323,18 +6399,18 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
        SetHighlights(fromX, fromY, toX, toY);
        if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
            // [HGM] super: promotion to captured piece selected from holdings
-           ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];
-           promotionChoice = TRUE;
+           ChessSquare p = boards[currentMove][fromY][fromX];
+           promotionChoice = 1;
+           CopyBoard(promoBoard, boards[currentMove]);
            // kludge follows to temporarily execute move on display, without promoting yet
-           boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank
-           boards[currentMove][toY][toX] = p;
-           DrawPosition(FALSE, boards[currentMove]);
-           boards[currentMove][fromY][fromX] = p; // take back, but display stays
-           boards[currentMove][toY][toX] = q;
+           promoBoard[fromY][fromX] = EmptySquare; // move Pawn to 8th rank
+           promoBoard[toY][toX] = p;
+           DrawPosition(FALSE, promoBoard);
            DisplayMessage("Click in holdings to choose piece", "");
            return;
        }
-       PromotionPopUp();
+       CopyBoard(promoBoard, boards[currentMove]);
+       PromoPopUp(boards[currentMove][fromY][fromX]);
     } else {
        UserMoveEvent(fromX, fromY, toX, toY, promoChoice);
        if (!appData.highlightLastMove || gotPremove) ClearHighlights();
@@ -6348,7 +6424,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     }
 }
 
-int RightClick(ClickType action, int x, int y, int *fromX, int *fromY)
+int RightClick(ClickType action, int x, int y, int *xx, int *yy)
 {   // front-end-free part taken out of PieceMenuPopup
     int whichMenu; int xSqr, ySqr;
 
@@ -6360,6 +6436,16 @@ int RightClick(ClickType action, int x, int y, int *fromX, int *fromY)
 
     xSqr = EventToSquare(x, BOARD_WIDTH);
     ySqr = EventToSquare(y, BOARD_HEIGHT);
+    if (flipView)
+      xSqr = BOARD_WIDTH - 1 - xSqr;
+    else
+      ySqr = BOARD_HEIGHT - 1 - ySqr;
+    if(promotionChoice == 3 && action == Release) {
+       EditPositionMenuEvent(promoBoard[ySqr][xSqr], fromX, fromY);
+       fromX = fromY = -1;
+       promotionChoice = 0;
+       return -1;
+    }
     if (action == Release) UnLoadPV(); // [HGM] pv
     if (action != Press) return -2; // return code to be ignored
     switch (gameMode) {
@@ -6398,15 +6484,14 @@ int RightClick(ClickType action, int x, int y, int *fromX, int *fromY)
        return -1;
     }
 
-    if (((*fromX = xSqr) < 0) ||
-       ((*fromY = ySqr) < 0)) {
-       *fromX = *fromY = -1;
+    if (((*xx = xSqr) < 0) ||
+       ((*yy = ySqr) < 0)) {
+       *xx = *yy = -1;
        return -1;
     }
-    if (flipView)
-      *fromX = BOARD_WIDTH - 1 - *fromX;
-    else
-      *fromY = BOARD_HEIGHT - 1 - *fromY;
+
+    fromX = *xx; fromY = *yy;
+    if(whichMenu == 0) { PiecePopUp(x, y); return -1; } // suppress EditPosition menu
 
     return whichMenu;
 }
@@ -11878,7 +11963,6 @@ EditGameEvent()
     SetGameInfo();
 }
 
-
 void
 EditPositionEvent()
 {
@@ -11895,7 +11979,14 @@ EditPositionEvent()
     SetGameInfo();
     if (currentMove > 0)
       CopyBoard(boards[0], boards[currentMove]);
-
+    { int i, r, f; // [HGM] editrights: take note of existing rights
+      for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) rightsBoard[r][f] = 0;
+      for(i=0; i<nrCastlingRights; i++) {
+       if(boards[0][CASTLING][i] != NoRights)
+         rightsBoard [castlingRank[i]] [boards[0][CASTLING][i]] = 1 + (i == 2 || i == 5);
+      }
+    }
+    
     blackPlaysFirst = !WhiteOnMove(currentMove);
     ResetClocks();
     currentMove = forwardMostMove = backwardMostMove = 0;
@@ -11927,7 +12018,22 @@ EditPositionDone(Boolean fakeRights)
     startedFromSetupPosition = TRUE;
     InitChessProgram(&first, FALSE);
     if(fakeRights) { // [HGM] suppress this if we just pasted a FEN.
+      int i, r, f, kf, err;
+      for(i=0; i<nrCastlingRights; i++) boards[0][CASTLING][i] = NoRights;
+      kf = NoRights; err = 0;
+      for(f=BOARD_RGHT-1; f>=0; f--)
+       if(rightsBoard[0][f] == 2) { if(kf != NoRights) err=10; boards[0][CASTLING][2] = kf = f; }
+      for(f=BOARD_RGHT-1; f>=0; f--)
+       if(rightsBoard[0][f] == 1) { err++; boards[0][CASTLING][f<kf] = f; }
+      kf = NoRights; err = 0;
+      for(f=BOARD_RGHT-1; f>=0; f--)
+       if(rightsBoard[BOARD_HEIGHT-1][f] == 2) { if(kf != NoRights) err=10; boards[0][CASTLING][5] = kf = f; }
+      for(f=BOARD_RGHT-1; f>=0; f--)
+       if(rightsBoard[BOARD_HEIGHT-1][f] == 1) { err++; boards[0][CASTLING][3+(f<kf)] = f; }
+      if(err + 2 > nrCastlingRights) DisplayError("unclear castling rights", 0);
+
       boards[0][EP_STATUS] = EP_NONE;
+#if 0
       boards[0][CASTLING][2] = boards[0][CASTLING][5] = BOARD_WIDTH>>1;
     if(boards[0][0][BOARD_WIDTH>>1] == king) {
        boards[0][CASTLING][1] = boards[0][0][BOARD_LEFT] == WhiteRook ? 0 : NoRights;
@@ -11937,6 +12043,7 @@ EditPositionDone(Boolean fakeRights)
        boards[0][CASTLING][4] = boards[0][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook ? 0 : NoRights;
        boards[0][CASTLING][3] = boards[0][BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook ? BOARD_RGHT-1 : NoRights;
       } else boards[0][CASTLING][5] = NoRights;
+#endif
     }
     SendToProgram("force\n", &first);
     if (blackPlaysFirst) {
@@ -12034,6 +12141,7 @@ EditPositionMenuEvent(selection, x, y)
 {
     char buf[MSG_SIZ];
     ChessSquare piece = boards[0][y][x];
+    int rights = 0; // [HGM] editrights: most new pieces get no castling rights
 
     if (gameMode != EditPosition && gameMode != IcsExamining) return;
 
@@ -12057,6 +12165,7 @@ EditPositionMenuEvent(selection, x, y)
                        }
                    } else {
                        boards[0][y][x] = p;
+                       rightsBoard[y][x] = 0; // [HGM] editrights: clear all castling rights
                    }
                }
            }
@@ -12120,12 +12229,24 @@ EditPositionMenuEvent(selection, x, y)
             selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz);
         goto defaultlabel;
 
+      case WhiteRook: // [HGM] editrights: corner Rooks get castling rights by default
+       if(y == 0 && (x == BOARD_LEFT || x == BOARD_RGHT-1)) rights = 1;
+       goto defaultlabel;
+
+      case BlackRook:
+       if(y == BOARD_HEIGHT-1 && (x == BOARD_LEFT || x == BOARD_RGHT-1)) rights = 1;
+       goto defaultlabel;
+
       case WhiteKing:
       case BlackKing:
         if(gameInfo.variant == VariantXiangqi)
             selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteWazir);
         if(gameInfo.variant == VariantKnightmate)
             selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteUnicorn);
+      case WhiteUnicorn:
+      case BlackUnicorn:
+       if(y == (selection <= WhiteKing ? 0 : BOARD_HEIGHT-1) && (x == BOARD_WIDTH>>1)) 
+           rights = 2; // [HGM] editrights: King on right-center file gets rights
       default:
         defaultlabel:
        if (gameMode == IcsExamining) {
@@ -12150,6 +12271,7 @@ EditPositionMenuEvent(selection, x, y)
                 }
             } else
            boards[0][y][x] = selection;
+           rightsBoard[y][x] = rights; // [HGM] editrights: set default rights of created piece
            DrawPosition(TRUE, boards[0]);
        }
        break;