Improve XBetza e.p. system
authorH.G.Muller <hgm@hgm-xboard.(none)>
Sun, 15 Dec 2019 11:25:46 +0000 (12:25 +0100)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Sun, 15 Dec 2019 11:25:46 +0000 (12:25 +0100)
The 'n' modifier can now be used on non-final legs of a multi-leg move
to indicate the target square of that leg becomes e.p. square. Pieces
that define an XBetza 'e' move will be able to use that move to e.p. capture
the last-moved piece when that moves goes to the e.p. square. To implement
this a routine was added to test whether this is the case (or whether the
e.p. capable piece used another move to go to the e.p. square).
  The limitation that there can only be more than a single e.p. square
when there is a pair of vertically adjacent ones still applies.

backend.c
moves.c
moves.h

index b6088f6..f2dede6 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -10409,7 +10409,8 @@ 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, lastFile, lastRank, berolina = 0;
+  ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2;
+  int p, rookX, oldEP, epRank, epFile, lastFile, lastRank, berolina = 0, isEP = 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 */
@@ -10418,7 +10419,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
       if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;
       oldEP = (signed char)board[EP_STATUS]; epRank = board[EP_RANK]; epFile = board[EP_FILE]; lastFile = board[LAST_TO] & 255,lastRank = board[LAST_TO] >> 8;
       board[EP_STATUS] = EP_NONE;
-      board[EP_FILE] = board[EP_RANK] = 100, board[LAST_TO] = 0x4040;
+      board[EP_FILE] = board[EP_RANK] = 100, board[LAST_TO] = toX + 256*toY;
 
   if (fromY == DROP_RANK) {
        /* must be first */
@@ -10451,7 +10452,10 @@ 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)) {
+       board[EP_RANK] = epRank; board[EP_FILE] = epFile;   // Ughh! cleared it too soon :-(
+       isEP = EnPassantTest(board, PosFlags(currentMove), fromY, fromX, toY, toX, promoChar);
+       if(isEP != 1) board[EP_RANK] = board[EP_FILE] = 100;// assumes only e.p. capturing pieces generate rights!
+       if(isEP == 2) {
            captured = board[lastRank][lastFile]; // remove victim
            board[lastRank][lastFile] = EmptySquare;
            pawn = EmptySquare; // kludge to suppress old e.p. code
@@ -10466,29 +10470,27 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
       if( pawn == WhitePawn ) {
            if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
               board[EP_STATUS] = EP_PAWN_MOVE;
-           if( toY-fromY>=2) {
-               board[EP_FILE] = (fromX + toX)/2; board[EP_RANK] = toY - 1 | 128*(toY - fromY > 2);
+           if(!isEP && toY-fromY>=2) {
+               board[EP_FILE] = (2*fromX + toX + 1)/3; board[EP_RANK] = toY - 1 | 128*(toY - fromY > 2);
                if(toX>BOARD_LEFT   && board[toY][toX-1] == BlackPawn &&
                        gameInfo.variant != VariantBerolina || toX < fromX)
                      board[EP_STATUS] = toX | berolina;
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&
                        gameInfo.variant != VariantBerolina || toX > fromX)
                      board[EP_STATUS] = toX;
-              board[LAST_TO] = toX + 256*toY;
           }
       } else
       if( pawn == BlackPawn ) {
            if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
               board[EP_STATUS] = EP_PAWN_MOVE;
-           if( toY-fromY<= -2) {
-               board[EP_FILE] = (fromX + toX)/2; board[EP_RANK] = toY + 1 | 128*(fromY - toY > 2);
+           if(!isEP && toY-fromY<= -2) {
+               board[EP_FILE] = (2*fromX + toX + 1)/3; board[EP_RANK] = toY + 1 | 128*(fromY - toY > 2);
                if(toX>BOARD_LEFT   && board[toY][toX-1] == WhitePawn &&
                        gameInfo.variant != VariantBerolina || toX < fromX)
                      board[EP_STATUS] = toX | berolina;
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&
                        gameInfo.variant != VariantBerolina || toX > fromX)
                      board[EP_STATUS] = toX;
-              board[LAST_TO] = toX + 256*toY;
           }
        }
 
diff --git a/moves.c b/moves.c
index 013f0c1..50a049b 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -311,6 +311,8 @@ OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOID
     (*(int*)cl)++;
 }
 
+static int viaX = 100, viaY = 100, epFlag;
+
 void
 MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, int range, char *desc, MoveCallback cb, VOIDSTAR cl)
 {
@@ -409,6 +411,8 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
        if(isdigit(*++p)) expo = atoi(p++);           // read exponent
        if(expo > 9) p++;                             // allow double-digit
        desc = p;                                     // this is start of next move
+       epFlag = // flags initial orthogonal and diagonal pawn non-capture multi-pushes (which have legacy meaning)
+               (initial && promo != NormalMove && !cont && mode == 4 && (!dx || dx == dy) && (dy > 1 ? !jump : expo > 1));
        if(initial == 2) { if(board[r][f] != initialPosition[r-2*his+3][f]) continue; initial = 0; } else
        if(initial && !range) {
                if(   (board[r][f] != initialPosition[r][f] ||
@@ -450,7 +454,7 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
                if(x <  BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT, loop++; else break; }
                if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT, loop++; else break; }
-               if(j) { j--; continue; }              // skip irrespective of occupation
+               if(j > 0) { j--; continue; }          // skip irrespective of occupation
                if(board[y][x] == DarkSquare) break;  // black squares are supposed to be off board
                if(!jump    && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
                if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
@@ -461,12 +465,20 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                if(initial && expo - i + 1 != range) { if(occup == 4) continue; else break; }
                if(cont) {                            // non-final leg
                  if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode
+                 if(skip < 0) mode |= 4;             // 'n' = 'm' + rights creation in non-final step leg
                  if(occup & mode) {                  // valid intermediate square, do continuation
                    char origAtom = *atom;
-                   int rg = (expo != 1 ? expo - i + 1 : range);   // pass length of last *slider* leg
+                   int rg = (expo != 1 ? expo - i + 1 : range);  // pass length of last *slider* leg
                    if(!(bit & all)) *atom = rotate[*atom - 'A']; // orth-diag interconversion to make direction valid
-                   if(occup & mode & 0x104)          // no side effects, merge legs to one move
+                   if(occup & mode & 0x104) {        // no side effects, merge legs to one move
+                       if(skip < 0 && occup == 4) {  // create e.p. rights on this square
+                           if(viaX != 100) {         // second e.p. square!
+                               if(viaX == x && viaY == y - vy) viaY = y | 128; // flag it when we can handle it
+                           } else viaX = x, viaY = y;
+                       }
                        MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
+                       if(viaY & 128) viaY = y - vy; else viaX = viaY = 100;
+                   }
                    if(occup & mode & 3 && (killX < 0 || kill2X < 0 && (legNr > 1 || killX == x && killY == y) ||
                                            (legNr == 1 ? kill2X == x && kill2Y == y : killX == x && killY == y))) {     // destructive first leg
                        int cnt = 0;
@@ -1810,6 +1822,29 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant
     return cl.fking < BOARD_RGHT ? cl.check : (gameInfo.variant == VariantAtomic)*1000; // [HGM] atomic: return 1000 if we have no king
 }
 
+typedef struct { int rt, ft, ep; } EnPassantClosure;
+
+void
+EnPassantCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
+{
+    EnPassantClosure *cl = (EnPassantClosure *) closure;
+    if(rt == cl->rt && ft == cl->ft) {
+        if(kind = WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) cl->ep = 2; // is e.p. capture
+       if(epFlag) cl->ep = 0; // for backward compatibility lets any imn pawn multi-push handle by old code
+       if(viaX != 100) board[EP_FILE] = viaX, board[EP_RANK] = viaY, cl->ep = 1; // generates e.p. rights
+    }
+}
+
+int
+EnPassantTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
+{   // sets e.p. square in board if move generated rights, returns whether move is an e.p. capture
+    EnPassantClosure cl;
+    ChessSquare piece = board[rf][ff];
+    cl.rt = rt; cl.ft = ft; cl.ep = 0;
+    MovesFromString(board, flags, ff, rf, -1, -1, 0, 0, pieceDesc[piece], EnPassantCallback, (void *) &cl);
+    return cl.ep;
+}
+
 int
 HasLion (Board board, int flags)
 {
diff --git a/moves.h b/moves.h
index 2148269..b15a741 100644 (file)
--- a/moves.h
+++ b/moves.h
@@ -144,6 +144,13 @@ extern ChessMove LegalityTest P((Board board, int flags,
                                 int rf, int ff, int rt, int ft,
                                 int promoChar));
 
+/* Is a move from (rf, ff) to (rt, ft) en e.p. capture for the player
+   whom the flags say is on move, or does it create e.p. rights?
+   Returns 2 if rights are created (and stored in board as side effect) */
+extern int EnPassantTest P((Board board, int flags,
+                                int rf, int ff, int rt, int ft,
+                                int promoChar));
+
 #define MT_NONE 0
 #define MT_CHECK 1
 #define MT_CHECKMATE 2