Fix crash on use of dialog Browse buttons GTK
[xboard.git] / backend.c
index 7c96808..b7c3246 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -1564,6 +1564,23 @@ InitBackEnd3 P((void))
     char buf[MSG_SIZ];
     int err, len;
 
+    if(!appData.icsActive && !appData.noChessProgram && !appData.matchMode &&                         // mode involves only first engine
+       !strcmp(appData.variant, "normal") &&                                                          // no explicit variant request
+        appData.NrRanks == -1 && appData.NrFiles == -1 && appData.holdingsSize == -1 &&               // no size overrides requested
+       !SupportedVariant(first.variants, VariantNormal, 8, 8, 0, first.protocolVersion, "") &&        // but 'normal' won't work with engine
+       !SupportedVariant(first.variants, VariantFischeRandom, 8, 8, 0, first.protocolVersion, "") ) { // nor will Chess960
+       char c, *q = first.variants, *p = strchr(q, ',');
+       if(p) *p = NULLCHAR;
+       if(StringToVariant(q) != VariantUnknown) { // the engine can play a recognized variant, however
+           int w, h, s;
+           if(sscanf(q, "%dx%d+%d_%c", &w, &h, &s, &c) == 4) // get size overrides the engine needs with it (if any)
+               appData.NrFiles = w, appData.NrRanks = h, appData.holdingsSize = s, q = strchr(q, '_') + 1;
+           ASSIGN(appData.variant, q); // fake user requested the first variant played by the engine
+           Reset(TRUE, FALSE);         // and re-initialize
+       }
+       if(p) *p = ',';
+    }
+
     InitChessProgram(&first, startedFromSetupPosition);
 
     if(!appData.noChessProgram) {  /* [HGM] tidy: redo program version to use name from myname feature */
@@ -4796,7 +4813,7 @@ ParseBoard12 (char *string)
              default:
                break;
              case MT_CHECK:
-                if(gameInfo.variant != VariantShogi)
+                if(!IS_SHOGI(gameInfo.variant))
                     strcat(parseList[moveNum - 1], "+");
                break;
              case MT_CHECKMATE:
@@ -5298,25 +5315,39 @@ static int lastX, lastY, lastLeftX, lastLeftY, selectFlag;
 int dragging;
 static ClickType lastClickType;
 
+int
+Partner (ChessSquare *p)
+{ // change piece into promotion partner if one shogi-promotes to the other
+  int stride = gameInfo.variant == VariantChu ? 22 : 11;
+  ChessSquare partner;
+  partner = (*p/stride & 1 ? *p - stride : *p + stride);
+  if(PieceToChar(*p) != '+' && PieceToChar(partner) != '+') return 0;
+  *p = partner;
+  return 1;
+}
+
 void
 Sweep (int step)
 {
     ChessSquare king = WhiteKing, pawn = WhitePawn, last = promoSweep;
+    static int toggleFlag;
     if(gameInfo.variant == VariantKnightmate) king = WhiteUnicorn;
     if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) king = EmptySquare;
     if(promoSweep >= BlackPawn) king = WHITE_TO_BLACK king, pawn = WHITE_TO_BLACK pawn;
     if(gameInfo.variant == VariantSpartan && pawn == BlackPawn) pawn = BlackLance, king = EmptySquare;
-    if(fromY != BOARD_HEIGHT-2 && fromY != 1) pawn = EmptySquare;
+    if(fromY != BOARD_HEIGHT-2 && fromY != 1 && gameInfo.variant != VariantChuChess) pawn = EmptySquare;
+    if(!step) toggleFlag = Partner(&last); // piece has shogi-promotion
     do {
-       promoSweep -= step;
+       if(step && !(toggleFlag && Partner(&promoSweep))) promoSweep -= step;
        if(promoSweep == EmptySquare) promoSweep = BlackPawn; // wrap
        else if((int)promoSweep == -1) promoSweep = WhiteKing;
        else if(promoSweep == BlackPawn && step < 0) promoSweep = WhitePawn;
        else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing;
        if(!step) step = -1;
     } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn ||
-           appData.testLegality && (promoSweep == king || promoSweep == WhiteLion || promoSweep == BlackLion) ||
-           IS_SHOGI(gameInfo.variant) && promoSweep != CHUPROMOTED last && last != CHUPROMOTED promoSweep && last != promoSweep);
+           !toggleFlag && PieceToChar(promoSweep) == '+' || // skip promoted versions of other
+           appData.testLegality && (promoSweep == king || gameInfo.variant != VariantChuChess &&
+            (promoSweep == WhiteLion || promoSweep == BlackLion)));
     if(toX >= 0) {
        int victim = boards[currentMove][toY][toX];
        boards[currentMove][toY][toX] = promoSweep;
@@ -6447,7 +6478,7 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i
     /* [HGM] rewritten IsPromotion to only flag promotions that offer a choice */
     /* [HGM] add Shogi promotions */
     int promotionZoneSize=1, highestPromotingPiece = (int)WhitePawn;
-    ChessSquare piece;
+    ChessSquare piece, partner;
     ChessMove moveType;
     Boolean premove;
 
@@ -6522,7 +6553,11 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i
     // we either have a choice what to promote to, or (in Shogi) whether to promote
     if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
        gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) {
-       *promoChoice = PieceToChar(BlackFerz);  // no choice
+       ChessSquare p=BlackFerz;  // no choice
+       while(p < EmptySquare) {  //but make sure we use piece that exists
+           *promoChoice = PieceToChar(p++);
+           if(*promoChoice != '.') break;
+       }
        return FALSE;
     }
     // no sense asking what we must promote to if it is going to explode...
@@ -6532,8 +6567,9 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i
     }
     // give caller the default choice even if we will not make it
     *promoChoice = ToLower(PieceToChar(defaultPromoChoice));
-    if(IS_SHOGI(gameInfo.variant)) *promoChoice = (defaultPromoChoice == piece ? '=' : '+');
-    if(gameInfo.variant == VariantChuChess) *promoChoice = (piece == WhitePawn || piece == BlackPawn ? 'q' : '+');
+    partner = piece; // pieces can promote if the pieceToCharTable says so
+    if(IS_SHOGI(gameInfo.variant)) *promoChoice = (defaultPromoChoice == piece && sweepSelect ? '=' : '+'); // obsolete?
+    else if(Partner(&partner))     *promoChoice = (defaultPromoChoice == piece && sweepSelect ? NULLCHAR : '+');
     if(        sweepSelect && gameInfo.variant != VariantGreat
                           && gameInfo.variant != VariantGrand
                           && gameInfo.variant != VariantSuper) return FALSE;
@@ -6545,6 +6581,7 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i
     if(appData.testLegality && !premove) {
        moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
                        fromY, fromX, toY, toX, IS_SHOGI(gameInfo.variant) || gameInfo.variant == VariantChuChess ? '+' : NULLCHAR);
+        if(moveType == IllegalMove) *promoChoice = NULLCHAR; // could be the fact we promoted was illegal
        if(moveType != WhitePromotion && moveType  != BlackPromotion)
            return FALSE;
     }
@@ -7159,12 +7196,15 @@ Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VO
     else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3;
 }
 
+static int hoverSavedValid;
+
 void
 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] = baseMarker[y][x] = 0;
+    for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) sum += marker[y][x], marker[y][x] = 0;
+    hoverSavedValid = 0;
     if(!sum) return; // nothing was cleared,no redraw needed
   } else {
     int capt = 0;
@@ -7199,14 +7239,15 @@ ChessSquare gatingPiece = EmptySquare; // exported to front-end, for dragging
 int
 CanPromote (ChessSquare piece, int y)
 {
+        int zone = (gameInfo.variant == VariantChuChess ? 3 : 1);
        if(gameMode == EditPosition) return FALSE; // no promotions when editing position
        // some variants have fixed promotion piece, no promotion at all, or another selection mechanism
        if(IS_SHOGI(gameInfo.variant)          || gameInfo.variant == VariantXiangqi ||
           gameInfo.variant == VariantSuper    || gameInfo.variant == VariantGreat   ||
           gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
          gameInfo.variant == VariantMakruk   || gameInfo.variant == VariantASEAN) return FALSE;
-       return (piece == BlackPawn && y == 1 ||
-               piece == WhitePawn && y == BOARD_HEIGHT-2 ||
+       return (piece == BlackPawn && y <= zone ||
+               piece == WhitePawn && y >= BOARD_HEIGHT-1-zone ||
                piece == BlackLance && y == 1 ||
                piece == WhiteLance && y == BOARD_HEIGHT-2 );
 }
@@ -7220,11 +7261,13 @@ HoverEvent (int xPix, int yPix, int x, int y)
        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
        oldFromX = fromX; oldFromY = fromY;
-       if(oldX == -1 && oldY == -1 && x == fromX && y == fromY) // record markings after from-change
+       if(oldX == -1 && oldY == -1 && x == fromX && y == fromY) { // record markings after from-change
          for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
            baseMarker[r][f] = marker[r][f], baseLegal[r][f] = legal[r][f];
-       else if(oldX != x || oldY != y) {
+         hoverSavedValid = 1;
+       } else if(oldX != x || oldY != y) {
          // [HGM] lift: entered new to-square; redraw arrow, and inform engine
+         if(hoverSavedValid) // don't restore markers that are supposed to be cleared
          for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
            marker[r][f] = baseMarker[r][f], legal[r][f] = baseLegal[r][f];
          if((marker[y][x] == 2 || marker[y][x] == 6) && legal[y][x]) {
@@ -7477,8 +7520,11 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        toY = y;
     }
 
+    piece = boards[currentMove][fromY][fromX];
+
     saveAnimate = appData.animate;
     if (clickType == Press) {
+       if(gameInfo.variant == VariantChuChess && piece != WhitePawn && piece != BlackPawn) defaultPromoChoice = piece;
        if(gameMode == EditPosition && boards[currentMove][fromY][fromX] == EmptySquare) {
            // must be Edit Position mode with empty-square selected
            fromX = x; fromY = y; DragPieceBegin(xPix, yPix, FALSE); dragging = 1; // consider this a new attempt to drag
@@ -7494,9 +7540,8 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        if(marker[y][x] == 5) return; // [HGM] lion: to-click on cyan square; defer action to release
        if(legal[y][x] == 2 || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) {
          if(appData.sweepSelect) {
-           ChessSquare piece = boards[currentMove][fromY][fromX];
            promoSweep = defaultPromoChoice;
-           if(PieceToChar(CHUPROMOTED piece) == '+') promoSweep = CHUPROMOTED piece;
+           if(gameInfo.variant != VariantChuChess && PieceToChar(CHUPROMOTED piece) == '+') promoSweep = CHUPROMOTED piece;
            selectFlag = 0; lastX = xPix; lastY = yPix;
            Sweep(0); // Pawn that is going to promote: preview promotion piece
            sweepSelecting = 1;
@@ -7512,7 +7557,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
            ClearHighlights();
        }
     } else if(sweepSelecting) { // this must be the up-click corresponding to the down-click that started the sweep
-       sweepSelecting = 0;
+       sweepSelecting = 0; appData.animate = FALSE; // do not animate, a selected piece already on to-square
        if (appData.animate || appData.highlightLastMove) {
            SetHighlights(fromX, fromY, toX, toY);
        } else {
@@ -7528,6 +7573,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
            ClearHighlights();
        }
 #endif
+       if(gameInfo.variant == VariantChuChess && piece != WhitePawn && piece != BlackPawn) defaultPromoChoice = piece;
        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);
@@ -7594,7 +7640,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
            DisplayMessage("Click in holdings to choose piece", "");
            return;
        }
-       PromotionPopUp();
+       PromotionPopUp(promoChoice);
     } else {
        int oldMove = currentMove;
        UserMoveEvent(fromX, fromY, toX, toY, promoChoice);
@@ -9663,7 +9709,7 @@ ParseGameHistory (char *game)
          default:
            break;
          case MT_CHECK:
-            if(gameInfo.variant != VariantShogi)
+            if(!IS_SHOGI(gameInfo.variant))
                 strcat(parseList[boardIndex - 1], "+");
            break;
          case MT_CHECKMATE:
@@ -9800,7 +9846,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
         board[toY][toX+1] = board[fromY][BOARD_LEFT];
         board[fromY][BOARD_LEFT] = EmptySquare;
     } else if ((board[fromY][fromX] == WhitePawn && gameInfo.variant != VariantXiangqi ||
-                board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi)
+                board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantChu)
                && toY >= BOARD_HEIGHT-promoRank && promoChar // defaulting to Q is done elsewhere
                ) {
        /* white pawn promotion */
@@ -9861,7 +9907,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
        board[fromY][0] = EmptySquare;
        board[toY][2] = BlackRook;
     } else if ((board[fromY][fromX] == BlackPawn && gameInfo.variant != VariantXiangqi ||
-                board[fromY][fromX] == BlackLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi)
+                board[fromY][fromX] == BlackLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantChu)
               && toY < promoRank && promoChar
                ) {
        /* black pawn promotion */
@@ -9974,6 +10020,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
        board[toY][toX] = EmptySquare;
       }
     }
+
     if(gameInfo.variant == VariantSChess && promoChar != NULLCHAR && promoChar != '=' && piece != WhitePawn && piece != BlackPawn) {
         board[fromY][fromX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); // S-Chess gating
     } else
@@ -10104,7 +10151,7 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar)
       default:
        break;
       case MT_CHECK:
-        if(gameInfo.variant != VariantShogi)
+        if(!IS_SHOGI(gameInfo.variant))
             strcat(parseList[forwardMostMove - 1], "+");
        break;
       case MT_CHECKMATE: