Implement Makruk counting rules
[xboard.git] / backend.c
index b7f36b2..6d29691 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -4741,7 +4741,13 @@ ParseBoard12 (char *string)
     boards[moveNum][EP_STATUS] = EP_NONE;
     if(str[0] == 'P') boards[moveNum][EP_STATUS] = EP_PAWN_MOVE;
     if(strchr(move_str, 'x')) boards[moveNum][EP_STATUS] = EP_CAPTURE;
-    if(double_push !=  -1) boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
+    if(double_push !=  -1) {
+       int dir = WhiteOnMove(moveNum) ? 1 : -1, last = BOARD_HEIGHT-1;
+       boards[moveNum][EP_FILE] = // also set new e.p. variables
+       boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
+       boards[moveNum][EP_RANK] = (last + 3*dir)/2;
+       boards[moveNum][LAST_TO] = 128*(last + dir) + boards[moveNum][EP_FILE];
+    } else boards[moveNum][EP_FILE] = boards[moveNum][EP_RANK] = 100;
 
 
     if (ics_getting_history == H_GOT_REQ_HEADER ||
@@ -4907,7 +4913,7 @@ ParseBoard12 (char *string)
                && gameInfo.variant != VariantGrand&& gameInfo.variant != VariantSChess) // inherit info that ICS does not give from previous board
               for(k=0; k<ranks; k++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++) {
                 ChessSquare old, new = boards[moveNum][k][j];
-                  if(fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
+                  if(new == EmptySquare || fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
                   old = (k==toY && j==toX) ? boards[moveNum-1][fromY][fromX] : boards[moveNum-1][k][j]; // trace back mover
                   if(old == new) continue;
                   if(old == PROMOTED(new)) boards[moveNum][k][j] = old;// prevent promoted pieces to revert to primordial ones
@@ -4915,7 +4921,7 @@ ParseBoard12 (char *string)
                       if(old < WhiteCannon || old >= BlackPawn && old < BlackCannon)
                            boards[moveNum][k][j] = PROMOTED(old); // choose correct type of Gold in promotion
                       else boards[moveNum][k][j] = old; // preserve type of Gold
-                  } else if((old == WhitePawn || old == BlackPawn) && new != EmptySquare) // Pawn promotions (but not e.p.capture!)
+                  } else if(old == WhitePawn || old == BlackPawn) // Pawn promotions (but not e.p.capture!)
                       boards[moveNum][k][j] = PROMOTED(new); // use non-primordial representation of chosen piece
               }
          } else {
@@ -6342,10 +6348,11 @@ InitPosition (int redraw)
       shuffleOpenings = 1;
       break;
     case VariantNoCastle:
-      pieces = FIDEArray;
-      nrCastlingRights = 0;
       /* !!?unconstrained back-rank shuffle */
       shuffleOpenings = 1;
+    case VariantSuicide:
+      pieces = FIDEArray;
+      nrCastlingRights = 0;
       break;
     }
 
@@ -7581,6 +7588,7 @@ void ReportClick(char *action, int x, int y)
 
 Boolean right; // instructs front-end to use button-1 events as if they were button 3
 Boolean deferChoice;
+int createX = -1, createY = -1; // square where we last created a piece in EditPosition mode
 
 void
 LeftClick (ClickType clickType, int xPix, int yPix)
@@ -7618,13 +7626,16 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        } else if(y == BOARD_HEIGHT-1) { handOffsets &= ~2; DrawPosition(TRUE, boards[currentMove]); return; }
     }
 
-    if(appData.monoMouse && gameMode == EditPosition && fromX < 0 && clickType == Press && boards[currentMove][y][x] == EmptySquare) {
+    if(appData.monoMouse && gameMode == EditPosition && fromX < 0 && clickType == Press && 
+       (boards[currentMove][y][x] == EmptySquare || x == createX && y == createY) ) {
        static int dummy;
        RightClick(clickType, xPix, yPix, &dummy, &dummy);
        right = TRUE;
        return;
     }
 
+    createX = createY = -1;
+
     if(SeekGraphClick(clickType, xPix, yPix, 0)) return;
 
     prevClickTime = lastClickTime; GetTimeMark(&lastClickTime);
@@ -8055,9 +8066,15 @@ RightClick (ClickType action, int x, int y, int *fromX, int *fromY)
        if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1;
        if (xSqr < 0 || ySqr < 0) return -1;
        if(appData.pieceMenu) { whichMenu = 0; break; } // edit-position menu
+       if(flipView) xSqr = BOARD_WIDTH - 1 - xSqr; else ySqr = BOARD_HEIGHT - 1 - ySqr;
+       if(xSqr == createX && ySqr == createY && xSqr != BOARD_LEFT-2 && xSqr != BOARD_RGHT+1) {
+           ChessSquare p = boards[currentMove][ySqr][xSqr];
+           do { if(++p == EmptySquare) p = WhitePawn; } while(PieceToChar(p) == '.');
+           boards[currentMove][ySqr][xSqr] = p; DrawPosition(FALSE, boards[currentMove]);
+           return -2;
+        }
        pieceSweep = shiftKey ? BlackPawn : WhitePawn;  // [HGM] sweep: prepare selecting piece by mouse sweep
-       toX = xSqr; toY = ySqr; lastX = x, lastY = y;
-       if(flipView) toX = BOARD_WIDTH - 1 - toX; else toY = BOARD_HEIGHT - 1 - toY;
+       createX = toX = xSqr; createY = toY = ySqr; lastX = x, lastY = y;
        NextPiece(0);
        return 2; // grab
       case IcsObserving:
@@ -8490,6 +8507,32 @@ Adjudicate (ChessProgramState *cps)
                           return 1;
                      }
                 } else moveCount = 6;
+
+                if(gameInfo.variant == VariantMakruk && // Makruk counting rules
+                  (nrW == 1 || nrB == 1 || nr[WhitePawn] + nr[BlackPawn] == 0)) { // which only kick in when pawnless or bare King
+                    int maxcnt, his, mine, c, wom = WhiteOnMove(forwardMostMove);
+                    count = forwardMostMove;
+                    while(count >= backwardMostMove) {
+                        int np = nr[WhitePawn] + nr[BlackPawn];
+                        if(wom) mine = nrW, his = nrB, c = BlackPawn;
+                        else    mine = nrB, his = nrW, c = WhitePawn;
+                        if(mine > 1 && np) { count++; break; }
+                        if(mine > 1) maxcnt = 64; else
+                        maxcnt = (nr[WhiteRook+c] > 1 ? 8 : nr[WhiteRook+c] ? 16 : nr[WhiteMan+c] > 1 ? 22 :
+                                                            nr[WhiteKnight+c] > 1 ? 32 : nr[WhiteMan+c] ? 44 : 64) - his - 1;
+                        while(boards[count][EP_STATUS] != EP_CAPTURE && count > backwardMostMove) count--; // seek previous character
+                        if(count == backwardMostMove) break;
+                        if(forwardMostMove - count >= 2*maxcnt + 1 - (mine == 1)) break;
+                        Count(boards[--count], nr, &nrW, &nrB, &staleW, &staleB, &bishopColor);
+                    }
+                    if(forwardMostMove - count >= 2*maxcnt + 1 - (mine == 1)) {
+                        boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
+                        if(canAdjudicate && appData.ruleMoves >= 0) {
+                            GameEnds( GameIsDrawn, "Xboard adjudication: counting rule", GE_XBOARD );
+                            return 1;
+                        }
+                    }
+                }
            }
 
        // Repetition draws and 50-move rule can be applied independently of legality testing
@@ -11449,6 +11492,7 @@ RecentEngineEvent (int nr)
     if(mnemonic[n]) { // if somehow the engine with the selected nickname is no longer found in the list, we skip
        ReplaceEngine(&first, 0);
        FloatToFront(&appData.recentEngineList, command[n]);
+       ASSIGN(currentEngine[0], command[n]);
     }
 }
 
@@ -14784,18 +14828,25 @@ EditTagsEvent ()
 }
 
 void
-ToggleSecond ()
+StartSecond ()
 {
-  if(second.analyzing) {
-    SendToProgram("exit\n", &second);
-    second.analyzing = FALSE;
-  } else {
-    if (second.pr == NoProc) StartChessProgram(&second);
+    if(WaitForEngine(&second, StartSecond)) return;
     InitChessProgram(&second, FALSE);
     FeedMovesToProgram(&second, currentMove);
 
     SendToProgram("analyze\n", &second);
     second.analyzing = TRUE;
+    ThawUI();
+}
+
+void
+ToggleSecond ()
+{
+  if(second.analyzing) {
+    SendToProgram("exit\n", &second);
+    second.analyzing = FALSE;
+  } else {
+    StartSecond();
   }
 }
 
@@ -17362,6 +17413,8 @@ ParseOption (Option *opt, ChessProgramState *cps)
            opt->type = SaveButton;
        } else return FALSE;
        *p = 0; // terminate option name
+       *(int*) (opt->name + MSG_SIZ - 104) = opt->value; // hide default values somewhere
+       if(opt->target == &opt->textValue) strncpy(opt->name + MSG_SIZ - 100, opt->textValue, 99);
        // now look if the command-line options define a setting for this engine option.
        if(cps->optionSettings && cps->optionSettings[0])
            p = strstr(cps->optionSettings, opt->name); else p = NULL;
@@ -17374,6 +17427,8 @@ ParseOption (Option *opt, ChessProgramState *cps)
                            if(!strcmp(((char**)opt->textValue)[n], q+1)) opt->value = n;
                        break;
                    case TextBox:
+                   case FileName:
+                   case PathName:
                        safeStrCpy(opt->textValue, q+1, MSG_SIZ - (opt->textValue - opt->name));
                        break;
                    case Spin:
@@ -17385,8 +17440,6 @@ ParseOption (Option *opt, ChessProgramState *cps)
                strcat(buf, "\n");
                SendToProgram(buf, cps);
        }
-       *(int*) (opt->name + MSG_SIZ - 104) = opt->value; // hide default values somewhere
-       if(opt->target == &opt->textValue) strncpy(opt->name + MSG_SIZ - 100, opt->textValue, 99);
        return TRUE;
 }
 
@@ -17396,7 +17449,7 @@ FeatureDone (ChessProgramState *cps, int val)
   DelayedEventCallback cb = GetDelayedEvent();
   if ((cb == InitBackEnd3 && cps == &first) ||
       (cb == SettingsMenuIfReady && cps == &second) ||
-      (cb == LoadEngine) ||
+      (cb == LoadEngine) || (cb == StartSecond) ||
       (cb == TwoMachinesEventIfReady)) {
     CancelDelayedEvent();
     ScheduleDelayedEvent(cb, val ? 1 : 3600000);