From 61d3d34c62ee83fc3c6439e1a6b32b0b2f4e1570 Mon Sep 17 00:00:00 2001 From: H.G.Muller Date: Sun, 15 Dec 2019 12:25:46 +0100 Subject: [PATCH] Improve XBetza e.p. system 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 | 20 +++++++++++--------- moves.c | 41 ++++++++++++++++++++++++++++++++++++++--- moves.h | 7 +++++++ 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/backend.c b/backend.c index b6088f6..f2dede6 100644 --- 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 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 fromX) board[EP_STATUS] = toX; - board[LAST_TO] = toX + 256*toY; } } diff --git a/moves.c b/moves.c index 013f0c1..50a049b 100644 --- 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 --- 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 -- 1.7.0.4