Fix omission in castling refactoring for ICS received boards
[xboard.git] / backend.c
index 9cfb1a2..8c76afd 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -249,6 +249,7 @@ int lastSavedGame; /* [HGM] save: ID of game */
 char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */
 extern int chatCount;
 int chattingPartner;
+char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */
 
 /* States for ics_getting_history */
 #define H_FALSE 0
@@ -756,7 +757,7 @@ InitBackEnd1()
     /* [HGM] time odds: set factor for each machine */
     first.timeOdds  = appData.firstTimeOdds;
     second.timeOdds = appData.secondTimeOdds;
-    { int norm = 1;
+    { float norm = 1;
         if(appData.timeOddsMode) {
             norm = first.timeOdds;
             if(norm > second.timeOdds) norm = second.timeOdds;
@@ -2167,7 +2168,8 @@ read_from_ics(isr, closure, data, count, error)
     }
 
        buf[buf_len] = NULLCHAR;
-       next_out = leftover_len;
+//     next_out = leftover_len; // [HGM] should we set this to 0, and not print it in advance?
+       next_out = 0;
        leftover_start = 0;
        
        i = 0;
@@ -2322,16 +2324,16 @@ read_from_ics(isr, closure, data, count, error)
                    if(!suppressKibitz) // [HGM] kibitz
                        AppendComment(forwardMostMove, StripHighlight(parse), TRUE);
                    else { // [HGM kibitz: divert memorized engine kibitz to engine-output window
-                       int nrDigit = 0, nrAlph = 0, i;
+                       int nrDigit = 0, nrAlph = 0, j;
                        if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input
                        { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }
                        parse[parse_pos] = NULLCHAR;
                        // try to be smart: if it does not look like search info, it should go to
                        // ICS interaction window after all, not to engine-output window.
-                       for(i=0; i<parse_pos; i++) { // count letters and digits
-                           nrDigit += (parse[i] >= '0' && parse[i] <= '9');
-                           nrAlph  += (parse[i] >= 'a' && parse[i] <= 'z');
-                           nrAlph  += (parse[i] >= 'A' && parse[i] <= 'Z');
+                       for(j=0; j<parse_pos; j++) { // count letters and digits
+                           nrDigit += (parse[j] >= '0' && parse[j] <= '9');
+                           nrAlph  += (parse[j] >= 'a' && parse[j] <= 'z');
+                           nrAlph  += (parse[j] >= 'A' && parse[j] <= 'Z');
                        }
                        if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info
                            int depth=0; float score;
@@ -2341,6 +2343,7 @@ read_from_ics(isr, closure, data, count, error)
                                pvInfoList[forwardMostMove-1].score = 100*score;
                            }
                            OutputKibitz(suppressKibitz, parse);
+                           next_out = i+1; // [HGM] suppress printing in ICS window
                        } else {
                            char tmp[MSG_SIZ];
                            sprintf(tmp, _("your opponent kibitzes: %s"), parse);
@@ -2349,7 +2352,7 @@ read_from_ics(isr, closure, data, count, error)
                    }
                    started = STARTED_NONE;
                } else {
-                   /* Don't match patterns against characters in chatter */
+                   /* Don't match patterns against characters in comment */
                    i++;
                    continue;
                }
@@ -2449,9 +2452,11 @@ read_from_ics(isr, closure, data, count, error)
                        } 
                        continue;
                } else
-               if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz
-                   started = STARTED_CHATTER;
-                   suppressKibitz = TRUE;
+               if(looking_at(buf, &i, "kibitzed to *\n") && atoi(star_match[0])) {
+                   // suppress the acknowledgements of our own autoKibitz
+                   SendToPlayer(star_match[0], strlen(star_match[0]));
+                   looking_at(buf, &i, "*% "); // eat prompt
+                   next_out = i;
                }
            } // [HGM] kibitz: end of patch
 
@@ -2851,7 +2856,9 @@ read_from_ics(isr, closure, data, count, error)
            if (looking_at(buf, &i, "% ") ||
                ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
                 && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
+               if(suppressKibitz) next_out = i;
                savingComment = FALSE;
+               suppressKibitz = 0;
                switch (started) {
                  case STARTED_MOVES:
                  case STARTED_MOVES_NOHIDE:
@@ -3148,6 +3155,7 @@ read_from_ics(isr, closure, data, count, error)
                    strncmp(why, "Continuing ", 11) == 0) {
                    gs_gamenum = gamenum;
                    strcpy(gs_kind, strchr(why, ' ') + 1);
+                   VariantSwitch(boards[currentMove], StringToVariant(gs_kind)); // [HGM] variantswitch: even before we get first board
 #if ZIPPY
                    if (appData.zippyPlay) {
                        ZippyGameStart(whitename, blackname);
@@ -3277,6 +3285,7 @@ read_from_ics(isr, closure, data, count, error)
                        while(looking_at(buf, &i, "\n")); // [HGM] skip empty lines
                        if (looking_at(buf, &i, "*% ")) {
                            savingComment = FALSE;
+                           suppressKibitz = 0;
                        }
                    }
                    next_out = i;
@@ -3346,6 +3355,7 @@ read_from_ics(isr, closure, data, count, error)
                    if (looking_at(buf, &i, "*% ")) {
                        if(strchr(star_match[0], 7)) SendToPlayer("\007", 1); // Bell(); // FICS fuses bell for next board with prompt in zh captures
                        savingComment = FALSE;
+                       suppressKibitz = 0;
                    }
                    next_out = i;
                }
@@ -3355,12 +3365,13 @@ read_from_ics(isr, closure, data, count, error)
            i++;                /* skip unparsed character and loop back */
        }
        
-       if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window
-           started != STARTED_HOLDINGS && i > next_out) {
-           SendToPlayer(&buf[next_out], i - next_out);
+       if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz
+//         started != STARTED_HOLDINGS && i > next_out) { // [HGM] should we compare to leftover_start in stead of i?
+//         SendToPlayer(&buf[next_out], i - next_out);
+           started != STARTED_HOLDINGS && leftover_start > next_out) {
+           SendToPlayer(&buf[next_out], leftover_start - next_out);
            next_out = i;
        }
-       suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above
        
        leftover_len = buf_len - leftover_start;
        /* if buffer ends with something we couldn't parse,
@@ -3694,16 +3705,16 @@ ParseBoard12(string)
     if(moveNum == 0 || gameInfo.variant != VariantFischeRandom)
     { int i, j; ChessSquare wKing = WhiteKing, bKing = BlackKing;
 
-        for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)
+        for(i=BOARD_LEFT, j=NoRights; i<BOARD_RGHT; i++)
             if(board[0][i] == WhiteRook) j = i;
         initialRights[0] = boards[moveNum][CASTLING][0] = (castle_ws == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
-        for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)
+        for(i=BOARD_RGHT-1, j=NoRights; i>=BOARD_LEFT; i--)
             if(board[0][i] == WhiteRook) j = i;
         initialRights[1] = boards[moveNum][CASTLING][1] = (castle_wl == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
-        for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)
+        for(i=BOARD_LEFT, j=NoRights; i<BOARD_RGHT; i++)
             if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;
         initialRights[3] = boards[moveNum][CASTLING][3] = (castle_bs == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
-        for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)
+        for(i=BOARD_RGHT-1, j=NoRights; i>=BOARD_LEFT; i--)
             if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;
         initialRights[4] = boards[moveNum][CASTLING][4] = (castle_bl == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
 
@@ -3713,6 +3724,11 @@ ParseBoard12(string)
         for(k=BOARD_LEFT; k<BOARD_RGHT; k++)
             if(board[BOARD_HEIGHT-1][k] == bKing)
                 initialRights[5] = boards[moveNum][CASTLING][5] = k;
+        if(gameInfo.variant == VariantTwoKings) {
+            // In TwoKings looking for a King does not work, so always give castling rights to a King on e1/e8
+            if(board[0][4] == wKing) initialRights[2] = boards[moveNum][CASTLING][2] = 4;
+            if(board[BOARD_HEIGHT-1][4] == bKing) initialRights[5] = boards[moveNum][CASTLING][5] = 4;
+        }
     } else { int r;
         r = boards[moveNum][CASTLING][0] = initialRights[0];
         if(board[0][r] != WhiteRook) boards[moveNum][CASTLING][0] = NoRights;
@@ -4433,21 +4449,39 @@ fprintf(debugFP,"parsePV: %d %c%c%c%c '%s'\n", valid, fromX+AAA, fromY+ONE, toX+
     moveList[endPV-1][3] = toY + ONE;
     parseList[endPV-1][0] = NULLCHAR;
   } while(valid);
+  currentMove = endPV;
+  if(currentMove == forwardMostMove) ClearPremoveHighlights(); else
+  SetPremoveHighlights(moveList[currentMove-1][0]-AAA, moveList[currentMove-1][1]-ONE,
+                       moveList[currentMove-1][2]-AAA, moveList[currentMove-1][3]-ONE);
+  DrawPosition(TRUE, boards[currentMove]);
 }
 
 static int lastX, lastY;
 
 Boolean
+LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
+{
+       int startPV;
+
+       if(index < 0 || index >= strlen(buf)) return FALSE; // sanity
+       lastX = x; lastY = y;
+       while(index > 0 && buf[index-1] != '\n') index--; // beginning of line
+       startPV = index;
+      while(buf[index] != '\n') if(buf[index++] == '\t') startPV = index;
+      index = startPV;
+       while(buf[index] && buf[index] != '\n') index++;
+       buf[index] = 0;
+       ParsePV(buf+startPV);
+       *start = startPV; *end = index-1;
+       return TRUE;
+}
+
+Boolean
 LoadPV(int x, int y)
 { // called on right mouse click to load PV
   int which = gameMode == TwoMachinesPlay && (WhiteOnMove(forwardMostMove) == (second.twoMachinesColor[0] == 'w'));
   lastX = x; lastY = y;
   ParsePV(lastPV[which]); // load the PV of the thinking engine in the boards array.
-  currentMove = endPV;
-  if(currentMove == forwardMostMove) ClearPremoveHighlights(); else
-  SetPremoveHighlights(moveList[currentMove-1][0]-AAA, moveList[currentMove-1][1]-ONE,
-                       moveList[currentMove-1][2]-AAA, moveList[currentMove-1][3]-ONE);
-  DrawPosition(TRUE, boards[currentMove]);
   return TRUE;
 }
 
@@ -4812,7 +4846,7 @@ InitPosition(redraw)
       break;
     case VariantFairy:
       pieces = fairyArray;
-      SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); 
+      SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk"); 
       break;
     case VariantGreat:
       pieces = GreatArray;
@@ -5364,6 +5398,18 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn)
            return AmbiguousMove;
        } else if (toX >= 0 && toY >= 0) {
            boards[0][toY][toX] = boards[0][fromY][fromX];
+           if(fromX == BOARD_LEFT-2) { // handle 'moves' out of holdings
+               if(boards[0][fromY][0] != EmptySquare) {
+                   if(boards[0][fromY][1]) boards[0][fromY][1]--;
+                   if(boards[0][fromY][1] == 0)  boards[0][fromY][0] = EmptySquare; 
+               }
+           } else
+           if(fromX == BOARD_RGHT+1) {
+               if(boards[0][fromY][BOARD_WIDTH-1] != EmptySquare) {
+                   if(boards[0][fromY][BOARD_WIDTH-2]) boards[0][fromY][BOARD_WIDTH-2]--;
+                   if(boards[0][fromY][BOARD_WIDTH-2] == 0)  boards[0][fromY][BOARD_WIDTH-1] = EmptySquare; 
+               }
+           } else
            boards[0][fromY][fromX] = EmptySquare;
            return AmbiguousMove;
        }
@@ -5616,6 +5662,43 @@ if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", move
         FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
 }
 
+void
+Mark(board, flags, kind, rf, ff, rt, ft, closure)
+     Board board;
+     int flags;
+     ChessMove kind;
+     int rf, ff, rt, ft;
+     VOIDSTAR closure;
+{
+    typedef char Markers[BOARD_RANKS][BOARD_FILES];
+    Markers *m = (Markers *) closure;
+    if(rf == fromY && ff == fromX)
+       (*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare
+                        || kind == WhiteCapturesEnPassant
+                        || kind == BlackCapturesEnPassant);
+    else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3;
+}
+
+void
+MarkTargetSquares(int clear)
+{
+  int x, y;
+  if(!appData.markers || !appData.highlightDragging || 
+     !appData.testLegality || gameMode == EditPosition) return;
+  if(clear) {
+    for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) marker[y][x] = 0;
+  } else {
+    int capt = 0;
+    GenLegal(boards[currentMove], PosFlags(currentMove), Mark, (void*) marker);
+    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++;
+      if(capt)
+      for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x] == 1) marker[y][x] = 0;
+    }
+  }
+  DrawPosition(TRUE, NULL);
+}
+
 void LeftClick(ClickType clickType, int xPix, int yPix)
 {
     int x, y;
@@ -5624,6 +5707,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     char promoChoice = NULLCHAR;
 
     if (clickType == Press) ErrorPopDown();
+    MarkTargetSquares(1);
 
     x = EventToSquare(xPix, BOARD_WIDTH);
     y = EventToSquare(yPix, BOARD_HEIGHT);
@@ -5669,6 +5753,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
                fromX = x;
                fromY = y;
                second = 0;
+               MarkTargetSquares(0);
                DragPieceBegin(xPix, yPix);
                if (appData.highlightDragging) {
                    SetHighlights(x, y, -1, -1);
@@ -5709,6 +5794,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
            if (OKToStartUserMove(x, y)) {
                fromX = x;
                fromY = y;
+               MarkTargetSquares(0);
                DragPieceBegin(xPix, yPix);
            }
            return;
@@ -5761,11 +5847,30 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
        appData.animate = FALSE;
     }
 
-    // moves into holding are invalid for now (later perhaps allow in EditPosition)
+    // moves into holding are invalid for now (except in EditPosition, adapting to-square)
     if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) {
+       ChessSquare piece = boards[currentMove][fromY][fromX];
+       if(gameMode == EditPosition && piece != EmptySquare &&
+          fromX >= BOARD_LEFT && fromX < BOARD_RGHT) {
+           int n;
+            
+           if(x == BOARD_LEFT-2 && piece >= BlackPawn) {
+               n = PieceToNumber(piece - (int)BlackPawn);
+               if(n > gameInfo.holdingsSize) { n = 0; piece = BlackPawn; }
+               boards[currentMove][BOARD_HEIGHT-1 - n][0] = piece;
+               boards[currentMove][BOARD_HEIGHT-1 - n][1]++;
+           } else
+           if(x == BOARD_RGHT+1 && piece < BlackPawn) {
+               n = PieceToNumber(piece);
+               if(n > gameInfo.holdingsSize) { n = 0; piece = WhitePawn; }
+               boards[currentMove][n][BOARD_WIDTH-1] = piece;
+               boards[currentMove][n][BOARD_WIDTH-2]++;
+           }
+           boards[currentMove][fromY][fromX] = EmptySquare;
+       }
        ClearHighlights();
        fromX = fromY = -1;
-       DrawPosition(TRUE, NULL);
+       DrawPosition(TRUE, boards[currentMove]);
        return;
     }
 
@@ -11394,9 +11499,20 @@ EditPositionMenuEvent(selection, x, y)
 
       case EmptySquare:
        if (gameMode == IcsExamining) {
+            if (x < BOARD_LEFT || x >= BOARD_RGHT) break; // [HGM] holdings
             sprintf(buf, "%sx@%c%c\n", ics_prefix, AAA + x, ONE + y);
            SendToICS(buf);
        } else {
+            if(x < BOARD_LEFT || x >= BOARD_RGHT) {
+                if(x == BOARD_LEFT-2) {
+                    if(y < BOARD_HEIGHT-1-gameInfo.holdingsSize) break;
+                    boards[0][y][1] = 0;
+                } else
+                if(x == BOARD_RGHT+1) {
+                    if(y >= gameInfo.holdingsSize) break;
+                    boards[0][y][BOARD_WIDTH-2] = 0;
+                } else break;
+            }
            boards[0][y][x] = EmptySquare;
            DrawPosition(FALSE, boards[0]);
        }
@@ -11435,12 +11551,28 @@ EditPositionMenuEvent(selection, x, y)
       default:
         defaultlabel:
        if (gameMode == IcsExamining) {
+            if (x < BOARD_LEFT || x >= BOARD_RGHT) break; // [HGM] holdings
            sprintf(buf, "%s%c@%c%c\n", ics_prefix,
                     PieceToChar(selection), AAA + x, ONE + y);
            SendToICS(buf);
        } else {
+            if(x < BOARD_LEFT || x >= BOARD_RGHT) {
+                int n;
+                if(x == BOARD_LEFT-2 && selection >= BlackPawn) {
+                    n = PieceToNumber(selection - BlackPawn);
+                    if(n > gameInfo.holdingsSize) { n = 0; selection = BlackPawn; }
+                    boards[0][BOARD_HEIGHT-1-n][0] = selection;
+                    boards[0][BOARD_HEIGHT-1-n][1]++;
+                } else
+                if(x == BOARD_RGHT+1 && selection < BlackPawn) {
+                    n = PieceToNumber(selection);
+                    if(n > gameInfo.holdingsSize) { n = 0; selection = WhitePawn; }
+                    boards[0][n][BOARD_WIDTH-1] = selection;
+                    boards[0][n][BOARD_WIDTH-2]++;
+                }
+            } else
            boards[0][y][x] = selection;
-           DrawPosition(FALSE, boards[0]);
+           DrawPosition(TRUE, boards[0]);
        }
        break;
     }
@@ -12705,7 +12837,7 @@ SendTimeRemaining(cps, machineWhite)
     /* [HGM] translate opponent's time by time-odds factor */
     otime = (otime * cps->other->timeOdds) / cps->timeOdds;
     if (appData.debugMode) {
-        fprintf(debugFP, "time odds: %d %d \n", cps->timeOdds, cps->other->timeOdds);
+        fprintf(debugFP, "time odds: %f %f \n", cps->timeOdds, cps->other->timeOdds);
     }
 
     if (time <= 0) time = 1;