Implement XBetza unload modifier
authorH.G.Muller <hgm@hgm-xboard.(none)>
Sun, 31 May 2020 11:13:13 +0000 (13:13 +0200)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Sun, 31 May 2020 11:31:04 +0000 (13:31 +0200)
Move legs containing a 'u' modifier will cause a piece captured
or destroyed by the entire move to reappear on the starting square of
that leg. This allows specification of swappers and pieces that push
other pieces elsewhere. The encoding of the corresponding moves uses
the same format as that for locust capture, and ApplyMove() distinguishes
those from the occupation of the specified 'kill square'. For now
this can only be used in a move without other (true) locust capture.
Notation (SAN), and reading back of such moves is still unsolved.

backend.c
common.h
moves.c

index b15b157..a370e7b 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -7302,6 +7302,7 @@ UserMoveEvent (int fromX, int fromY, int toX, int toY, int promoChar)
        }
     }
 
+    if(moveType == Swap && killX < 0) killX = fromX, killY = fromY, moveType = NormalMove;
     FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
 }
 
@@ -7541,11 +7542,12 @@ Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VO
     typedef char Markers[BOARD_RANKS][BOARD_FILES];
     Markers *m = (Markers *) closure;
     if(rf == fromY && ff == fromX && (killX < 0 ? !(rt == rf && ft == ff) && legNr & 1 :
-                                     kill2X < 0 ? rt == killY && ft == killX || legNr & 2 : rt == killY && ft == killX || legNr & 4))
+                                     kill2X < 0 ? rt == killY && ft == killX || legNr & 2 : rt == killY && ft == killX || legNr & 4)) {
        (*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare
                         || kind == WhiteCapturesEnPassant
-                        || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && (killX < 0 & legNr || legNr & 2 && kill2X < 0)), legal[rt][ft] = 3;
-    else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3, legal[rt][ft] = 3;
+                        || kind == BlackCapturesEnPassant), legal[rt][ft] = 3;
+       if(kind == FirstLeg && (killX < 0 & legNr || legNr & 2 && kill2X < 0)) (*m)[rt][ft] = 5;
+    } else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3, legal[rt][ft] = 3;
 }
 
 static int hoverSavedValid;
@@ -10509,6 +10511,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
            board[EP_STATUS] = EP_CAPTURE;
            if( kill2X >= 0 && kill2Y >= 0)
              killed2 = board[kill2Y][kill2X], board[kill2Y][kill2X] = EmptySquare;
+           if(killed == EmptySquare) // [HGM] unload: kill-square also used for push destination
+             board[killY][killX] = board[toY][toX], board[EP_STATUS] = EP_NONE;
       }
 
       if( board[toY][toX] != EmptySquare ) {
index 55daf29..5755e33 100644 (file)
--- a/common.h
+++ b/common.h
@@ -348,7 +348,7 @@ typedef enum {
     WhitePromotion, WhiteNonPromotion,
     BlackPromotion, BlackNonPromotion,
     WhiteCapturesEnPassant, BlackCapturesEnPassant,
-    WhiteDrop, BlackDrop, FirstLeg,
+    WhiteDrop, BlackDrop, FirstLeg, Swap,
     NormalMove, AmbiguousMove, IllegalMove, ImpossibleMove,
     WhiteWins, BlackWins, GameIsDrawn, GameUnfinished,
     GNUChessGame, XBoardGame, MoveNumberOne, Open, Close, Nothing,
diff --git a/moves.c b/moves.c
index 032b6b0..33fda55 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -324,8 +324,8 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
     if(pc == WhitePawn || pc == WhiteLance) promo = WhitePromotion, promoRank = BOARD_HEIGHT-1; else
     if(pc == BlackPawn || pc == BlackLance) promo = BlackPromotion, promoRank = 0;
     while(*p) {                  // more moves to go
-       int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0;
-       char *cont = NULL;
+       int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0, put = 0, u = 0;
+       char *cont = NULL, *q;
        while(*p == 'i') initial++, desc = ++p;
        while(islower(*p)) p++;  // skip prefixes
        if(!isupper(*p)) return; // syntax error: no atom
@@ -396,6 +396,7 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
        }
        if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255;   // invert black moves
        mode = 0;                // build mode mask
+       if(*desc == 'u') put++, desc++;               // unload stuff at start of leg
        if(*desc == 'm') mode |= 4, desc++;           // move to empty
        if(*desc == 'c') mode |= his, desc++;         // capture foe
        if(*desc == 'd') mode |= mine, desc++;        // destroy (capture friend)
@@ -429,6 +430,8 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
        } else {
            strncpy(buf, cont, 80); cont = buf;       // copy next leg(s), so we can modify
            atom = buf; while(islower(*atom)) atom++; // skip to atom
+           for(q=buf; q!=atom && *q != 'a'; q++)     // test whether next leg unloads
+              if(*q == 'u') u = 1;
            if(mode & 32) mode ^= 256 + 32;           // in non-final legs 'p' means 'pass through'
            if(mode & 64 + 512) {
                mode |= 256;                          // and 'g' too, but converts leaper <-> slider
@@ -469,8 +472,9 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                  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 transp = occup & mode & 0x104;            // no side effect on intermediate square
                    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(transp && !u) {                // 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
@@ -479,7 +483,7 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                        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) ||
+                   if((occup & mode & 3 || transp && u) && (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;
                        legNr <<= 1;
@@ -524,7 +528,9 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                    break;
                }
                if(mode & 16 && (board[y][x] == WhiteKing || board[y][x] == BlackKing)) break; // tame piece, cannot capture royal
-               if(occup & mode) cb(board, flags, y == promoRank ? promo : NormalMove, r, f, y, x, cl); // allowed, generate
+               if(occup & mode) {
+                 cb(board, flags, y == promoRank ? promo : put ? Swap : NormalMove, r, f, y, x, cl); // allowed, generate
+               }
                if(occup != 4) break; // not valid transit square
            } while(--i);
          }