Improve identification of e.p. victim
authorH.G.Muller <hgm@hgm-xboard.(none)>
Thu, 24 Nov 2016 18:47:46 +0000 (19:47 +0100)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Fri, 13 Jan 2017 15:39:23 +0000 (16:39 +0100)
The move generator now generates e.p. moves of default Pawns based on
the file of the skipped square of the previous multi-push, rather than
the e.p. rights (which were set only if ApplyMove suspects an e.p. capture
is actually possible, which might not be the case when opponent Pawns do
not capture as expected). A standard Pawn capturing to an empty square
will now always capture the previously multi-pushed Pawn. Only if there
is none it will guess where the victim should be based on FIDE rules,
and would not capture any non-Pawns in that location.
  This heuristic should work for default Pawns against any other kind
of Pawn. The existence of e.p. rights might not be recognized when
comparing positions, however, so that rep draws could be declared after
a two-fold repetition.

backend.c
common.h
moves.c

index f465f95..c3a68e7 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -10213,16 +10213,16 @@ ParseGameHistory (char *game)
 void
 ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
 {
-  ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, epFile, berolina = 0;
+  ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, epFile, lastFile, lastRank, berolina = 0;
   int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
 
     /* [HGM] compute & store e.p. status and castling rights for new position */
     /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */
 
       if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;
-      oldEP = (signed char)board[EP_FILE]; epRank = board[EP_RANK]; epFile = board[EP_FILE];
+      oldEP = (signed char)board[EP_STATUS]; epRank = board[EP_RANK]; epFile = board[EP_FILE]; lastFile = board[LAST_FILE],lastRank = board[LAST_RANK];
       board[EP_STATUS] = EP_NONE;
-      board[EP_FILE] = board[EP_RANK] = 100;
+      board[EP_FILE] = board[EP_RANK] = board[LAST_FILE] = board[LAST_RANK] = 100;
 
   if (fromY == DROP_RANK) {
        /* must be first */
@@ -10256,8 +10256,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
       pawn = board[fromY][fromX];
       if(pieceDesc[pawn] && strchr(pieceDesc[pawn], 'e')) { // piece with user-defined e.p. capture
        if(captured == EmptySquare && toX == epFile && (toY == (epRank & 127) || toY + (pawn < BlackPawn ? -1 : 1) == epRank - 128)) {
-           captured = board[epRank + (pawn < BlackPawn ? -1 : 1)][oldEP & berolina-1]; // remove victim
-           board[epRank + (pawn < BlackPawn ? -1 : 1)][oldEP & berolina-1] = EmptySquare;
+           captured = board[lastRank][lastFile]; // remove victim
+           board[lastRank][lastFile] = EmptySquare;
            pawn = EmptySquare; // kludge to suppress old e.p. code
        }
       }
@@ -10278,6 +10278,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&
                        gameInfo.variant != VariantBerolina || toX > fromX)
                      board[EP_STATUS] = toX;
+              board[LAST_FILE] = toX; board[LAST_RANK] = toY;
           }
       } else
       if( pawn == BlackPawn ) {
@@ -10291,6 +10292,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&
                        gameInfo.variant != VariantBerolina || toX > fromX)
                      board[EP_STATUS] = toX;
+              board[LAST_FILE] = toX; board[LAST_RANK] = toY;
           }
        }
 
@@ -10385,18 +10387,16 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
             board[toY][toX] = (ChessSquare) (PROMOTED(board[toY][toX]));
        board[fromY][fromX] = EmptySquare;
     } else if ((fromY >= BOARD_HEIGHT>>1)
-              && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
+              && (epFile == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
               && (toX != fromX)
                && gameInfo.variant != VariantXiangqi
                && gameInfo.variant != VariantBerolina
               && (pawn == WhitePawn)
               && (board[toY][toX] == EmptySquare)) {
+       if(lastFile == 100) lastFile = (board[fromY][toX] == BlackPawn ? toX : fromX), lastRank = fromY; // assume FIDE e.p. if victim present
        board[fromY][fromX] = EmptySquare;
        board[toY][toX] = piece;
-       if(toY == epRank - 128 + 1)
-           captured = board[toY - 2][toX], board[toY - 2][toX] = EmptySquare;
-       else
-           captured = board[toY - 1][toX], board[toY - 1][toX] = EmptySquare;
+       captured = board[lastRank][lastFile], board[lastRank][lastFile] = EmptySquare;
     } else if ((fromY == BOARD_HEIGHT-4)
               && (toX == fromX)
                && gameInfo.variant == VariantBerolina
@@ -10452,18 +10452,16 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
             board[toY][toX] = (ChessSquare) (PROMOTED(board[toY][toX]));
        board[fromY][fromX] = EmptySquare;
     } else if ((fromY < BOARD_HEIGHT>>1)
-              && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
+              && (epFile == toX && epRank == toY || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
               && (toX != fromX)
                && gameInfo.variant != VariantXiangqi
                && gameInfo.variant != VariantBerolina
               && (pawn == BlackPawn)
               && (board[toY][toX] == EmptySquare)) {
+       if(lastFile == 100) lastFile = (board[fromY][toX] == WhitePawn ? toX : fromX), lastRank = fromY;
        board[fromY][fromX] = EmptySquare;
        board[toY][toX] = piece;
-       if(toY == epRank - 128 - 1)
-           captured = board[toY + 2][toX], board[toY + 2][toX] = EmptySquare;
-       else
-           captured = board[toY + 1][toX], board[toY + 1][toX] = EmptySquare;
+       captured = board[lastRank][lastFile], board[lastRank][lastFile] = EmptySquare;
     } else if ((fromY == 3)
               && (toX == fromX)
                && gameInfo.variant == VariantBerolina
index 9a4c2c4..2aff360 100644 (file)
--- a/common.h
+++ b/common.h
@@ -181,6 +181,8 @@ typedef char *String;
 #define BOARD_RGHT   (gameInfo.boardWidth + gameInfo.holdingsWidth)
 #define CASTLING     (BOARD_RANKS-1)           /* [HGM] hide in upper rank   */
 #define VIRGIN       (BOARD_RANKS-2)           /* [HGM] pieces not moved     */
+#define LAST_RANK    CASTLING][(BOARD_FILES-8) /* [HGM] in upper rank        */
+#define LAST_FILE    CASTLING][(BOARD_FILES-7) /* [HGM] in upper rank        */
 #define TOUCHED_W    CASTLING][(BOARD_FILES-6) /* [HGM] in upper rank        */
 #define TOUCHED_B    CASTLING][(BOARD_FILES-5) /* [HGM] in upper rank        */
 #define EP_RANK      CASTLING][(BOARD_FILES-4) /* [HGM] in upper rank        */
diff --git a/moves.c b/moves.c
index 2f6a706..876564c 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -809,9 +809,10 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                               rf, ff, rf + 1, ff + s, closure);
                  }
                  if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
+                     int victimFile = (board[LAST_FILE] == 100 ? ff + s : board[LAST_FILE]);
                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
-                         (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
-                          board[rf][ff + s] == BlackPawn &&
+                         (board[EP_FILE] == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
+                          (board[rf][victimFile] == BlackPawn || board[rf][victimFile] == BlackLance) &&
                           board[rf+1][ff + s] == EmptySquare) {
                          callback(board, flags, WhiteCapturesEnPassant,
                                   rf, ff, rf+1, ff + s, closure);
@@ -859,9 +860,10 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                               rf, ff, rf - 1, ff + s, closure);
                  }
                  if (rf < BOARD_HEIGHT>>1) {
+                     int victimFile = (board[LAST_FILE] == 100 ? ff + s : board[LAST_FILE]);
                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
-                         (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
-                         board[rf][ff + s] == WhitePawn &&
+                         (board[EP_FILE] == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
+                         (board[rf][victimFile] == WhitePawn || board[rf][victimFile] == WhiteLance) &&
                          board[rf-1][ff + s] == EmptySquare) {
                          callback(board, flags, BlackCapturesEnPassant,
                                   rf, ff, rf-1, ff + s, closure);