* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
#include "config.h"
#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
#if HAVE_STRING_H
# include <string.h>
#else /* not HAVE_STRING_H */
int PosFlags(int index);
extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
+int quickFlag;
+char *pieceDesc[EmptySquare];
-
-int WhitePiece(piece)
- ChessSquare piece;
+int
+WhitePiece (ChessSquare piece)
{
return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
}
-int BlackPiece(piece)
- ChessSquare piece;
+int
+BlackPiece (ChessSquare piece)
{
return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
}
-int SameColor(piece1, piece2)
- ChessSquare piece1, piece2;
+#if 0
+int
+SameColor (ChessSquare piece1, ChessSquare piece2)
{
return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
(int) piece1 < (int) BlackPawn &&
(int) piece2 >= (int) BlackPawn &&
(int) piece2 < (int) EmptySquare);
}
+#else
+#define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
+#endif
char pieceToChar[] = {
'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
'x' };
char pieceNickName[EmptySquare];
-char PieceToChar(p)
- ChessSquare p;
+char
+PieceToChar (ChessSquare p)
{
if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
return pieceToChar[(int) p];
}
-int PieceToNumber(p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
- ChessSquare p;
+int
+PieceToNumber (ChessSquare p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
{
int i=0;
ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
return i;
}
-ChessSquare CharToPiece(c)
- int c;
+ChessSquare
+CharToPiece (int c)
{
int i;
if(c == '.') return EmptySquare;
return EmptySquare;
}
-void CopyBoard(to, from)
- Board to, from;
+void
+CopyBoard (Board to, Board from)
{
int i, j;
for (i = 0; i < BOARD_HEIGHT; i++)
for (j = 0; j < BOARD_WIDTH; j++)
to[i][j] = from[i][j];
- for (j = 0; j < BOARD_FILES-1; j++) // [HGM] gamestate: copy castling rights and ep status
+ for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
+ to[VIRGIN][j] = from[VIRGIN][j],
to[CASTLING][j] = from[CASTLING][j];
to[HOLDINGS_SET] = 0; // flag used in ICS play
}
-int CompareBoards(board1, board2)
- Board board1, board2;
+int
+CompareBoards (Board board1, Board board2)
{
int i, j;
return TRUE;
}
+// [HGM] gen: configurable move generation from Betza notation sent by engine.
+
+// alphabet "abcdefghijklmnopqrstuvwxyz"
+char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N";
+char xStep[] = "2110.130.102.10.00....0..2";
+char yStep[] = "2132.133.313.20.11....1..3";
+char dirType[] = "01000104000200000260050000";
+// alphabet "a b c d e f g h i j k l m n o p q r s t u v w x y z "
+int dirs1[] = { 0,0x3C,0,0,0,0xC3,0,0, 0,0,0,0xF0,0,0,0,0,0,0x0F,0 ,0,0,0 ,0,0,0,0 };
+int dirs2[] = { 0,0x18,0,0,0,0x81,0,0xFF,0,0,0,0x60,0,0,0,0,0,0x06,0x66,0,0,0x99,0,0,0,0 };
+
+int rot[][4] = { // rotation matrices for each direction
+ { 1, 0, 0, 1 },
+ { 0, 1, 1, 0 },
+ { 0, 1,-1, 0 },
+ { 1, 0, 0,-1 },
+ {-1, 0, 0,-1 },
+ { 0,-1,-1, 0 },
+ { 0,-1, 1, 0 },
+ {-1, 0, 0, 1 }
+};
+
+void
+MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback cb, VOIDSTAR cl)
+{
+ char *p = desc;
+ int mine, his, dir, bit, occup, i;
+ if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
+ while(*p) { // more moves to go
+ int expo = 1, dx, dy, x, y, mode, dirSet, retry=0, initial=0, jump=1;
+ if(*p == 'i') initial = 1, desc = ++p;
+ while(islower(*p)) p++; // skip prefixes
+ if(!isupper(*p)) return; // syntax error: no atom
+ dirSet = 0; // build direction set based on atom symmetry
+ switch(symmetry[*p-'A']) {
+ case 'B': expo = 0; // bishop, slide
+ case 'F': // diagonal atom (degenerate 4-fold)
+ while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+ int b = dirs1[*desc-'a']; // use wide version
+ if( islower(desc[1]) &&
+ ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
+ b = dirs1[*desc-'a'] & dirs1[desc[1]-'a']; // intersect wide & perp wide
+ desc += 2;
+ } else desc++;
+ dirSet |= b;
+ }
+ dirSet &= 0x99; if(!dirSet) dirSet = 0x99;
+ break;
+ case 'R': expo = 0; // rook, slide
+ case 'W': // orthogonal atom (non-deg 4-fold)
+ while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
+ dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
+ break;
+ case 'N': // oblique atom (degenerate 8-fold)
+ while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+ int b = dirs2[*desc-'a']; // when alone, use narrow version
+ if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
+ else if(islower(desc[1]) && i < '4'
+ && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
+ b = dirs1[*desc-'a'] & dirs2[desc[1]-'a']; // intersect wide & perp narrow
+ desc += 2;
+ } else desc++;
+ dirSet |= b;
+ }
+ if(!dirSet) dirSet = 0xFF;
+ break;
+ case 'Q': expo = 0; // queen, slide
+ case 'K': // non-deg (pseudo) 8-fold
+ dirSet=0x55; // start with orthogonal moves
+ retry = 1; // and schedule the diagonal moves for later
+ break; // should not have direction indicators
+ default: return; // syntax error: invalid atom
+ }
+ if(mine == 2) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
+ mode = 0; // build mode mask
+ if(*desc == 'm') mode |= 4, desc++;
+ if(*desc == 'c') mode |= his, desc++;
+ if(*desc == 'd') mode |= mine, desc++;
+ if(*desc == 'e') mode |= 8, desc++;
+ if(!mode) mode = his + 4;// no mode spec, use default = mc
+ if(*desc == 'p') mode |= 32, desc++;
+ if(*desc == 'g') mode |= 64, desc++;
+ if(*desc == 'n') jump = 0, desc++;
+ while(*desc == 'j') jump++, desc++;
+ dx = xStep[*p-'A'] - '0'; // step vector of atom
+ dy = yStep[*p-'A'] - '0';
+ if(isdigit(*++p)) expo = atoi(p++); // read exponent
+ if(expo > 9) p++; // allow double-digit
+ desc = p; // this is start of next move
+ if(initial && (board[r][f] != initialPosition[r][f] ||
+ r == 0 && board[TOUCHED_W] & 1<<f ||
+ r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f ) ) continue;
+ if(expo > 1 && dx == 0 && dy == 0) { // castling indicated by O + number
+ mode |= 16; dy = 1;
+ }
+ do {
+ for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
+ int i = expo, hop = mode, vx, vy;
+ if(!(bit & dirSet)) continue; // does not move in this direction
+ vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector
+ vy = dx*rot[dir][2] + dy*rot[dir][3];
+ x = f; y = r; // start square
+ do {
+ x += vx; y += vy; // step to next square
+ if(y < 0 || y >= BOARD_HEIGHT || x < BOARD_LEFT || x >= BOARD_RGHT) break;
+ 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
+ if(board[y][x] < BlackPawn) occup = 1; else
+ if(board[y][x] < EmptySquare) occup = 2; else
+ occup = 4;
+ if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper
+ if(mode & 8 && y == board[EP_RANK] && occup == 4 && board[EP_FILE] == x) { // to e.p. square
+ cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
+ }
+ if(mode & 16) { // castling
+ i = 2; // kludge to elongate move indefinitely
+ if(occup == 4) continue; // skip empty squares
+ if(x == BOARD_LEFT && board[y][x] == initialPosition[y][x]) // reached initial corner piece
+ cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
+ if(x == BOARD_RGHT-1 && board[y][x] == initialPosition[y][x])
+ cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
+ break;
+ }
+ if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl); // allowed, generate
+ if(occup != 4) break; // not valid transit square
+ } while(--i);
+ }
+ dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q
+ } while(retry--); // and start doing them
+ }
+} // next atom
+
+// [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
+
+void
+SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int i, rt, ft = ff;
+ for (i = 1;; i++) {
+ rt = rf + i;
+ if (rt >= BOARD_HEIGHT) break;
+ if (SameColor(board[rf][ff], board[rt][ft])) break;
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ if (board[rt][ft] != EmptySquare) break;
+ }
+}
+
+void
+SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int i, rt, ft = ff;
+ for (i = 1;; i++) {
+ rt = rf - i;
+ if (rt < 0) break;
+ if (SameColor(board[rf][ff], board[rt][ft])) break;
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ if (board[rt][ft] != EmptySquare) break;
+ }
+}
+
+void
+SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ SlideForward(board, flags, rf, ff, callback, closure);
+ SlideBackward(board, flags, rf, ff, callback, closure);
+}
+
+void
+SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int i, s, rt = rf, ft;
+ for(s = -1; s <= 1; s+= 2) {
+ for (i = 1;; i++) {
+ ft = ff + i*s;
+ if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
+ if (SameColor(board[rf][ff], board[rt][ft])) break;
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ if (board[rt][ft] != EmptySquare) break;
+ }
+ }
+}
+
+void
+SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int i, s, rt, ft;
+ for(s = -1; s <= 1; s+= 2) {
+ for (i = 1;; i++) {
+ rt = rf + i;
+ ft = ff + i * s;
+ if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
+ if (SameColor(board[rf][ff], board[rt][ft])) break;
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ if (board[rt][ft] != EmptySquare) break;
+ }
+ }
+}
+
+void
+SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int i, s, rt, ft;
+ for(s = -1; s <= 1; s+= 2) {
+ for (i = 1;; i++) {
+ rt = rf - i;
+ ft = ff + i * s;
+ if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
+ if (SameColor(board[rf][ff], board[rt][ft])) break;
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ if (board[rt][ft] != EmptySquare) break;
+ }
+ }
+}
+
+void
+Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ SlideVertical(board, flags, rf, ff, callback, closure);
+ SlideSideways(board, flags, rf, ff, callback, closure);
+}
+
+void
+Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ SlideDiagForward(board, flags, rf, ff, callback, closure);
+ SlideDiagBackward(board, flags, rf, ff, callback, closure);
+}
+
+void
+Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
+{ // Lion-like move of Horned Falcon and Souring Eagle
+ int ft = ff + dx, rt = rf + dy;
+ if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
+ if (!SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
+ ft += dx; rt += dy;
+ if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
+ if (!SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int ft = ff, rt = rf + 1;
+ if (rt >= BOARD_HEIGHT) return;
+ if (SameColor(board[rf][ff], board[rt][ft])) return;
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int ft = ff, rt = rf - 1;
+ if (rt < 0) return;
+ if (SameColor(board[rf][ff], board[rt][ft])) return;
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int ft, rt = rf;
+ ft = ff + 1;
+ if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ ft = ff - 1;
+ if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int ft, rt = rf + 1;
+ if (rt >= BOARD_HEIGHT) return;
+ ft = ff + 1;
+ if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ ft = ff - 1;
+ if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int ft, rt = rf - 1;
+ if(rt < 0) return;
+ ft = ff + 1;
+ if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ ft = ff - 1;
+ if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ StepForward(board, flags, rf, ff, callback, closure);
+ StepBackward(board, flags, rf, ff, callback, closure);
+}
+
+void
+Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ StepDiagForward(board, flags, rf, ff, callback, closure);
+ StepDiagBackward(board, flags, rf, ff, callback, closure);
+}
+
+void
+Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ StepVertical(board, flags, rf, ff, callback, closure);
+ StepSideways(board, flags, rf, ff, callback, closure);
+}
+
+void
+Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int i, j, s, rt, ft;
+ for (i = -1; i <= 1; i += 2)
+ for (j = -1; j <= 1; j += 2)
+ for (s = 1; s <= 2; s++) {
+ rt = rf + i*s;
+ ft = ff + j*(3-s);
+ if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
+ && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
+ && !SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ }
+}
/* Call callback once for each pseudo-legal move in the given
position, except castling moves. A move is pseudo-legal if it is
EP_UNKNOWN if we don't know and want to allow all e.p. captures.
Promotion moves generated are to Queen only.
*/
-void GenPseudoLegal(board, flags, callback, closure)
- Board board;
- int flags;
- MoveCallback callback;
- VOIDSTAR closure;
+void
+GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
+// speed: only do moves with this piece type
{
int rf, ff;
int i, j, d, s, fs, rs, rt, ft, m;
int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
- int promoRank = gameInfo.variant == VariantMakruk ? 3 : 1;
+ int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
for (rf = 0; rf < BOARD_HEIGHT; rf++)
for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
ChessSquare piece;
- int rookRange = 1000;
- if (flags & F_WHITE_ON_MOVE) {
- if (!WhitePiece(board[rf][ff])) continue;
- } else {
- if (!BlackPiece(board[rf][ff])) continue;
- }
+ if(board[rf][ff] == EmptySquare) continue;
+ if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
m = 0; piece = board[rf][ff];
if(PieceToChar(piece) == '~')
piece = (ChessSquare) ( DEMOTED piece );
- if(gameInfo.variant == VariantShogi)
+ if(filter != EmptySquare && piece != filter) continue;
+ if(pieceDesc[piece]) { MovesFromString(board, flags, ff, rf, pieceDesc[piece], callback, closure); continue; } // [HGM] gen
+ if(IS_SHOGI(gameInfo.variant))
piece = (ChessSquare) ( SHOGI piece );
- switch (piece) {
+ switch ((int)piece) {
/* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
default:
/* can't happen ([HGM] except for faries...) */
rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
rf, ff, rf + 1, ff, closure);
}
- if (rf == 1 && board[2][ff] == EmptySquare &&
+ if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
gameInfo.variant != VariantShatranj && /* [HGM] */
gameInfo.variant != VariantCourier && /* [HGM] */
- board[3][ff] == EmptySquare ) {
+ board[rf+2][ff] == EmptySquare ) {
callback(board, flags, NormalMove,
- rf, ff, 3, ff, closure);
+ rf, ff, rf+2, ff, closure);
}
for (s = -1; s <= 1; s += 2) {
if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
rf, ff, rf + 1, ff + s, closure);
}
- if (rf == BOARD_HEIGHT-4) {
+ if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
- (epfile == ff + s || epfile == EP_UNKNOWN) &&
- board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
- board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
+ (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
+ board[rf][ff + s] == BlackPawn &&
+ board[rf+1][ff + s] == EmptySquare) {
callback(board, flags, WhiteCapturesEnPassant,
- rf, ff, 5, ff + s, closure);
+ rf, ff, rf+1, ff + s, closure);
}
}
}
rf <= promoRank ? BlackPromotion : NormalMove,
rf, ff, rf - 1, ff, closure);
}
- if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
+ if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
gameInfo.variant != VariantShatranj && /* [HGM] */
gameInfo.variant != VariantCourier && /* [HGM] */
- board[BOARD_HEIGHT-4][ff] == EmptySquare) {
+ board[rf-2][ff] == EmptySquare) {
callback(board, flags, NormalMove,
- rf, ff, BOARD_HEIGHT-4, ff, closure);
+ rf, ff, rf-2, ff, closure);
}
for (s = -1; s <= 1; s += 2) {
if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
rf <= promoRank ? BlackPromotion : NormalMove,
rf, ff, rf - 1, ff + s, closure);
}
- if (rf == 3) {
+ if (rf < BOARD_HEIGHT>>1) {
if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
- (epfile == ff + s || epfile == EP_UNKNOWN) &&
- board[3][ff + s] == WhitePawn &&
- board[2][ff + s] == EmptySquare) {
+ (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
+ board[rf][ff + s] == WhitePawn &&
+ board[rf-1][ff + s] == EmptySquare) {
callback(board, flags, BlackCapturesEnPassant,
- rf, ff, 2, ff + s, closure);
+ rf, ff, rf-1, ff + s, closure);
}
}
}
case BlackUnicorn:
case WhiteKnight:
case BlackKnight:
- mounted:
for (i = -1; i <= 1; i += 2)
for (j = -1; j <= 1; j += 2)
for (s = 1; s <= 2; s++) {
/* Gold General (and all its promoted versions) . First do the */
/* diagonal forward steps, then proceed as normal Wazir */
- case SHOGI WhiteWazir:
case SHOGI (PROMOTED WhitePawn):
+ if(gameInfo.variant == VariantShogi) goto WhiteGold;
+ case SHOGI (PROMOTED BlackPawn):
+ if(gameInfo.variant == VariantShogi) goto BlackGold;
+ SlideVertical(board, flags, rf, ff, callback, closure);
+ break;
+
case SHOGI (PROMOTED WhiteKnight):
- case SHOGI (PROMOTED WhiteQueen):
- case SHOGI (PROMOTED WhiteFerz):
- for (s = -1; s <= 1; s += 2) {
- if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
- !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
- callback(board, flags, NormalMove,
- rf, ff, rf + 1, ff + s, closure);
- }
- }
- goto finishGold;
+ if(gameInfo.variant == VariantShogi) goto WhiteGold;
+ case SHOGI BlackDrunk:
+ case SHOGI BlackAlfil:
+ Ferz(board, flags, rf, ff, callback, closure);
+ StepSideways(board, flags, rf, ff, callback, closure);
+ StepBackward(board, flags, rf, ff, callback, closure);
+ break;
- case SHOGI BlackWazir:
- case SHOGI (PROMOTED BlackPawn):
case SHOGI (PROMOTED BlackKnight):
+ if(gameInfo.variant == VariantShogi) goto BlackGold;
+ case SHOGI WhiteDrunk:
+ case SHOGI WhiteAlfil:
+ Ferz(board, flags, rf, ff, callback, closure);
+ StepSideways(board, flags, rf, ff, callback, closure);
+ StepForward(board, flags, rf, ff, callback, closure);
+ break;
+
+
+ case SHOGI WhiteStag:
+ case SHOGI BlackStag:
+ if(gameInfo.variant == VariantShogi) goto BlackGold;
+ SlideVertical(board, flags, rf, ff, callback, closure);
+ Ferz(board, flags, rf, ff, callback, closure);
+ StepSideways(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI (PROMOTED WhiteQueen):
+ case SHOGI WhiteTokin:
+ case SHOGI WhiteWazir:
+ WhiteGold:
+ StepDiagForward(board, flags, rf, ff, callback, closure);
+ Wazir(board, flags, rf, ff, callback, closure);
+ break;
+
case SHOGI (PROMOTED BlackQueen):
- case SHOGI (PROMOTED BlackFerz):
- for (s = -1; s <= 1; s += 2) {
- if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
- !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
- callback(board, flags, NormalMove,
- rf, ff, rf - 1, ff + s, closure);
- }
- }
+ case SHOGI BlackTokin:
+ case SHOGI BlackWazir:
+ BlackGold:
+ StepDiagBackward(board, flags, rf, ff, callback, closure);
+ Wazir(board, flags, rf, ff, callback, closure);
+ break;
case WhiteWazir:
case BlackWazir:
- finishGold:
- for (d = 0; d <= 1; d++)
- for (s = -1; s <= 1; s += 2) {
- rt = rf + s * d;
- ft = ff + s * (1 - d);
- if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
- && !SameColor(board[rf][ff], board[rt][ft]) &&
- (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
- callback(board, flags, NormalMove,
- rf, ff, rt, ft, closure);
- }
- break;
+ Wazir(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteMarshall:
+ case SHOGI BlackMarshall:
+ Ferz(board, flags, rf, ff, callback, closure);
+ for (d = 0; d <= 1; d++)
+ for (s = -2; s <= 2; s += 4) {
+ rt = rf + s * d;
+ ft = ff + s * (1 - d);
+ if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
+ if (!SameColor(board[rf][ff], board[rt][ft]) )
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ }
+ break;
+
+ case SHOGI WhiteAngel:
+ case SHOGI BlackAngel:
+ Wazir(board, flags, rf, ff, callback, closure);
case WhiteAlfil:
case BlackAlfil:
&& !SameColor(board[rf][ff], board[rt][ft]))
callback(board, flags, NormalMove,
rf, ff, rt, ft, closure);
- if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier
- || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
+ if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
+ gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
ft = ff + fs;
if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
/* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
case WhiteCardinal:
case BlackCardinal:
+ if(gameInfo.variant == VariantChuChess) goto DragonHorse;
for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
for (s = -2; s <= 2; s += 4) {
rt = rf + s * d;
/* Shogi Dragon Horse has to continue with Wazir after Bishop */
case SHOGI WhiteCardinal:
case SHOGI BlackCardinal:
- m++;
+ case SHOGI WhitePCardinal:
+ case SHOGI BlackPCardinal:
+ DragonHorse:
+ Bishop(board, flags, rf, ff, callback, closure);
+ Wazir(board, flags, rf, ff, callback, closure);
+ break;
/* Capablanca Archbishop continues as Knight */
case WhiteAngel:
case BlackAngel:
- m++;
+ Knight(board, flags, rf, ff, callback, closure);
/* Shogi Bishops are ordinary Bishops */
case SHOGI WhiteBishop:
case SHOGI BlackBishop:
+ case SHOGI WhitePBishop:
+ case SHOGI BlackPBishop:
case WhiteBishop:
case BlackBishop:
- for (rs = -1; rs <= 1; rs += 2)
- for (fs = -1; fs <= 1; fs += 2)
- for (i = 1;; i++) {
- rt = rf + (i * rs);
- ft = ff + (i * fs);
- if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
- if (SameColor(board[rf][ff], board[rt][ft])) break;
- callback(board, flags, NormalMove,
- rf, ff, rt, ft, closure);
- if (board[rt][ft] != EmptySquare) break;
- }
- if(m==1) goto mounted;
- if(m==2) goto finishGold;
- /* Bishop falls through */
- break;
+ Bishop(board, flags, rf, ff, callback, closure);
+ break;
/* Shogi Lance is unlike anything, and asymmetric at that */
case SHOGI WhiteQueen:
+ if(gameInfo.variant == VariantChu) goto doQueen;
for(i = 1;; i++) {
rt = rf + i;
ft = ff;
break;
case SHOGI BlackQueen:
+ if(gameInfo.variant == VariantChu) goto doQueen;
for(i = 1;; i++) {
rt = rf - i;
ft = ff;
/* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
case WhiteDragon:
case BlackDragon:
+ if(gameInfo.variant == VariantChuChess) goto DragonKing;
for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
for (s = -2; s <= 2; s += 4) {
rt = rf + s * d;
ft = ff + s * (1 - d);
- if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT || board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
+ if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
+ if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
if (SameColor(board[rf][ff], board[rt][ft])) continue;
callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
}
- if(gameInfo.variant == VariantSpartan) rookRange = 2; // in Spartan Chess restrict range to modern Dababba
- goto doRook;
-
+ if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
+ Wazir(board, flags, rf, ff, callback, closure);
+ else
+ Rook(board, flags, rf, ff, callback, closure);
+ break;
+
/* Shogi Dragon King has to continue as Ferz after Rook moves */
case SHOGI WhiteDragon:
case SHOGI BlackDragon:
+ case SHOGI WhitePDragon:
+ case SHOGI BlackPDragon:
+ DragonKing:
+ Rook(board, flags, rf, ff, callback, closure);
+ Ferz(board, flags, rf, ff, callback, closure);
+ break;
m++;
/* Capablanca Chancellor sets flag to continue as Knight */
case WhiteMarshall:
case BlackMarshall:
- m++;
- m += (gameInfo.variant == VariantSpartan); // in Spartan Chess Chancellor is used for Dragon King.
+ Rook(board, flags, rf, ff, callback, closure);
+ if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
+ Ferz(board, flags, rf, ff, callback, closure);
+ else
+ Knight(board, flags, rf, ff, callback, closure);
+ break;
/* Shogi Rooks are ordinary Rooks */
case SHOGI WhiteRook:
case SHOGI BlackRook:
+ case SHOGI WhitePRook:
+ case SHOGI BlackPRook:
case WhiteRook:
case BlackRook:
- doRook:
- for (d = 0; d <= 1; d++)
- for (s = -1; s <= 1; s += 2)
- for (i = 1;; i++) {
- rt = rf + (i * s) * d;
- ft = ff + (i * s) * (1 - d);
- if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
- if (SameColor(board[rf][ff], board[rt][ft])) break;
- callback(board, flags, NormalMove,
- rf, ff, rt, ft, closure);
- if (board[rt][ft] != EmptySquare || i == rookRange) break;
- }
- if(m==1) goto mounted;
- if(m==2) goto finishSilver;
- break;
+ Rook(board, flags, rf, ff, callback, closure);
+ break;
case WhiteQueen:
case BlackQueen:
- for (rs = -1; rs <= 1; rs++)
- for (fs = -1; fs <= 1; fs++) {
- if (rs == 0 && fs == 0) continue;
- for (i = 1;; i++) {
- rt = rf + (i * rs);
- ft = ff + (i * fs);
- if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
- if (SameColor(board[rf][ff], board[rt][ft])) break;
- callback(board, flags, NormalMove,
- rf, ff, rt, ft, closure);
- if (board[rt][ft] != EmptySquare) break;
- }
- }
- break;
+ case SHOGI WhiteMother:
+ case SHOGI BlackMother:
+ doQueen:
+ Rook(board, flags, rf, ff, callback, closure);
+ Bishop(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhitePawn:
+ StepForward(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI BlackPawn:
+ StepBackward(board, flags, rf, ff, callback, closure);
+ break;
- /* Shogi Pawn and Silver General: first the Pawn move, */
- /* then the General continues like a Ferz */
case WhiteMan:
- if(gameInfo.variant != VariantMakruk) goto commoner;
- case SHOGI WhitePawn:
+ if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
case SHOGI WhiteFerz:
- if (rf < BOARD_HEIGHT-1 &&
- !SameColor(board[rf][ff], board[rf + 1][ff]) )
- callback(board, flags, NormalMove,
- rf, ff, rf + 1, ff, closure);
- if(piece != SHOGI WhitePawn) goto finishSilver;
- break;
+ Ferz(board, flags, rf, ff, callback, closure);
+ StepForward(board, flags, rf, ff, callback, closure);
+ break;
case BlackMan:
- if(gameInfo.variant != VariantMakruk) goto commoner;
- case SHOGI BlackPawn:
+ if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
case SHOGI BlackFerz:
- if (rf > 0 &&
- !SameColor(board[rf][ff], board[rf - 1][ff]) )
- callback(board, flags, NormalMove,
- rf, ff, rf - 1, ff, closure);
- if(piece == SHOGI BlackPawn) break;
+ StepBackward(board, flags, rf, ff, callback, closure);
case WhiteFerz:
case BlackFerz:
- finishSilver:
/* [HGM] support Shatranj pieces */
- for (rs = -1; rs <= 1; rs += 2)
- for (fs = -1; fs <= 1; fs += 2) {
- rt = rf + rs;
- ft = ff + fs;
- if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
- if (!SameColor(board[rf][ff], board[rt][ft]) &&
- (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
- callback(board, flags, NormalMove,
- rf, ff, rt, ft, closure);
- }
- break;
+ Ferz(board, flags, rf, ff, callback, closure);
+ break;
case WhiteSilver:
case BlackSilver:
- m++; // [HGM] superchess: use for Centaur
+ Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
+
commoner:
+ case SHOGI WhiteMonarch:
+ case SHOGI BlackMonarch:
case SHOGI WhiteKing:
case SHOGI BlackKing:
case WhiteKing:
case BlackKing:
-// walking:
- for (i = -1; i <= 1; i++)
- for (j = -1; j <= 1; j++) {
- if (i == 0 && j == 0) continue;
- rt = rf + i;
- ft = ff + j;
- if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
- if (SameColor(board[rf][ff], board[rt][ft])) continue;
- callback(board, flags, NormalMove,
- rf, ff, rt, ft, closure);
- }
- if(m==1) goto mounted;
- break;
+ Ferz(board, flags, rf, ff, callback, closure);
+ Wazir(board, flags, rf, ff, callback, closure);
+ break;
case WhiteNightrider:
case BlackNightrider:
break;
Amazon:
- /* First do Bishop,then continue like Chancellor */
- for (rs = -1; rs <= 1; rs += 2)
- for (fs = -1; fs <= 1; fs += 2)
- for (i = 1;; i++) {
- rt = rf + (i * rs);
- ft = ff + (i * fs);
- if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
- if (SameColor(board[rf][ff], board[rt][ft])) break;
- callback(board, flags, NormalMove,
- rf, ff, rt, ft, closure);
- if (board[rt][ft] != EmptySquare) break;
- }
- m++;
- goto doRook;
+ Bishop(board, flags, rf, ff, callback, closure);
+ Rook(board, flags, rf, ff, callback, closure);
+ Knight(board, flags, rf, ff, callback, closure);
+ break;
// Use Lance as Berolina / Spartan Pawn.
case WhiteLance:
rf, ff, rf + 1, ff, closure);
for (s = -1; s <= 1; s += 2) {
if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
- callback(board, flags,
+ callback(board, flags,
rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
rf, ff, rf + 1, ff + s, closure);
if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
}
break;
-
+
case BlackLance:
if(gameInfo.variant == VariantSuper) goto Amazon;
if (rf > 0 && WhitePiece(board[rf - 1][ff]))
rf, ff, rf - 1, ff, closure);
for (s = -1; s <= 1; s += 2) {
if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
- callback(board, flags,
+ callback(board, flags,
rf <= promoRank ? BlackPromotion : NormalMove,
rf, ff, rf - 1, ff + s, closure);
if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
}
break;
+ case SHOGI WhiteNothing:
+ case SHOGI BlackNothing:
+ case SHOGI WhiteLion:
+ case SHOGI BlackLion:
+ case WhiteLion:
+ case BlackLion:
+ for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
+ if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
+ if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
+ callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
+ rf, ff, rt, ft, closure);
+ }
+ break;
+
+ case SHOGI WhiteFalcon:
+ case SHOGI BlackFalcon:
+ case SHOGI WhitePDagger:
+ case SHOGI BlackPDagger:
+ SlideSideways(board, flags, rf, ff, callback, closure);
+ StepVertical(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteCobra:
+ case SHOGI BlackCobra:
+ StepVertical(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI (PROMOTED WhiteFerz):
+ if(gameInfo.variant == VariantShogi) goto WhiteGold;
+ case SHOGI (PROMOTED BlackFerz):
+ if(gameInfo.variant == VariantShogi) goto BlackGold;
+ case SHOGI WhitePSword:
+ case SHOGI BlackPSword:
+ SlideVertical(board, flags, rf, ff, callback, closure);
+ StepSideways(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteUnicorn:
+ case SHOGI BlackUnicorn:
+ Ferz(board, flags, rf, ff, callback, closure);
+ StepVertical(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteMan:
+ StepDiagForward(board, flags, rf, ff, callback, closure);
+ StepVertical(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI BlackMan:
+ StepDiagBackward(board, flags, rf, ff, callback, closure);
+ StepVertical(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteHCrown:
+ case SHOGI BlackHCrown:
+ Bishop(board, flags, rf, ff, callback, closure);
+ SlideSideways(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteCrown:
+ case SHOGI BlackCrown:
+ Bishop(board, flags, rf, ff, callback, closure);
+ SlideVertical(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteHorned:
+ Sting(board, flags, rf, ff, 1, 0, callback, closure);
+ callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+ if(killX >= 0) break;
+ Bishop(board, flags, rf, ff, callback, closure);
+ SlideSideways(board, flags, rf, ff, callback, closure);
+ SlideBackward(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI BlackHorned:
+ Sting(board, flags, rf, ff, -1, 0, callback, closure);
+ callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+ if(killX >= 0) break;
+ Bishop(board, flags, rf, ff, callback, closure);
+ SlideSideways(board, flags, rf, ff, callback, closure);
+ SlideForward(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteEagle:
+ Sting(board, flags, rf, ff, 1, 1, callback, closure);
+ Sting(board, flags, rf, ff, 1, -1, callback, closure);
+ callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+ if(killX >= 0) break;
+ Rook(board, flags, rf, ff, callback, closure);
+ SlideDiagBackward(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI BlackEagle:
+ Sting(board, flags, rf, ff, -1, 1, callback, closure);
+ Sting(board, flags, rf, ff, -1, -1, callback, closure);
+ callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+ if(killX >= 0) break;
+ Rook(board, flags, rf, ff, callback, closure);
+ SlideDiagForward(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteDolphin:
+ case SHOGI BlackHorse:
+ SlideDiagBackward(board, flags, rf, ff, callback, closure);
+ SlideVertical(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI BlackDolphin:
+ case SHOGI WhiteHorse:
+ SlideDiagForward(board, flags, rf, ff, callback, closure);
+ SlideVertical(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI WhiteLance:
+ SlideForward(board, flags, rf, ff, callback, closure);
+ break;
+
+ case SHOGI BlackLance:
+ SlideBackward(board, flags, rf, ff, callback, closure);
+ break;
+
case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
case BlackFalcon:
case WhiteCobra:
VOIDSTAR cl;
} GenLegalClosure;
+int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
+Board xqCheckers, nullBoard;
+
extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
int rf, int ff, int rt, int ft,
VOIDSTAR closure));
-void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
- Board board;
- int flags;
- ChessMove kind;
- int rf, ff, rt, ft;
- VOIDSTAR closure;
+void
+GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
{
register GenLegalClosure *cl = (GenLegalClosure *) closure;
+ if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
+
+ if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
+
if (!(flags & F_IGNORE_CHECK) ) {
int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
- if(promo) board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
+ if(promo) {
+ int r, f, kings=0;
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
+ kings += (board[r][f] == BlackKing);
+ if(kings >= 2)
+ promo = 0;
+ else
+ board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
+ }
check = CheckTest(board, flags, rf, ff, rt, ft,
kind == WhiteCapturesEnPassant ||
kind == BlackCapturesEnPassant);
true if castling is not yet ruled out by a move of the king or
rook. Return TRUE if the player on move is currently in check and
F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
-int GenLegal(board, flags, callback, closure)
- Board board;
- int flags;
- MoveCallback callback;
- VOIDSTAR closure;
+int
+GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
{
GenLegalClosure cl;
int ff, ft, k, left, right, swap;
int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
+ int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
+ char *p;
cl.cb = callback;
cl.cl = closure;
- GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl);
+ xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
+ if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
+ GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
- if (!ignoreCheck &&
- CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
+ if (inCheck) return TRUE;
/* Generate castling moves */
if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
wKing = WhiteUnicorn; bKing = BlackUnicorn;
}
+ p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
+ if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
+
for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
if ((flags & F_WHITE_ON_MOVE) &&
(flags & F_WHITE_KCASTLE_OK) &&
if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
- if(ft != NoRights && board[0][ft] == WhiteRook)
- callback(board, flags, WhiteHSideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
+ if(ft != NoRights && board[0][ft] == WhiteRook) {
+ if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
+ if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
+ }
ft = castlingRights[1]; /* Rook file if we have A-side rights */
left = BOARD_LEFT+2;
if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
+ if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
if(ff > BOARD_LEFT+2)
for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
- if(ft != NoRights && board[0][ft] == WhiteRook)
- callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
+ if(ft != NoRights && board[0][ft] == WhiteRook) {
+ if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
+ if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
+ }
}
} else {
ff = castlingRights[5]; /* King file if we have any rights */
if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
- if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
- callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
+ if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
+ if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
+ if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
+ }
ft = castlingRights[4]; /* Rook file if we have A-side rights */
left = BOARD_LEFT+2;
if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
+ if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
if(ff > BOARD_LEFT+2)
for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
- if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
- callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
+ if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
+ if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
+ if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
+ }
}
}
VOIDSTAR closure));
-void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
- Board board;
- int flags;
- ChessMove kind;
- int rf, ff, rt, ft;
- VOIDSTAR closure;
+void
+CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
{
register CheckTestClosure *cl = (CheckTestClosure *) closure;
- if (rt == cl->rking && ft == cl->fking) cl->check++;
+ if (rt == cl->rking && ft == cl->fking) {
+ if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
+ cl->check++;
+ xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
+ }
+ if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
+ && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
+ cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
}
back rank is not accounted for (i.e., we still return nonzero), as
this is illegal anyway. Return value is the number of times the
king is in check. */
-int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
- Board board;
- int flags;
- int rf, ff, rt, ft, enPassant;
+int
+CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
{
CheckTestClosure cl;
ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
- ChessSquare captured = EmptySquare;
+ ChessSquare captured = EmptySquare, ep=0, trampled=0;
/* Suppress warnings on uninitialized variables */
if(gameInfo.variant == VariantXiangqi)
king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
if(gameInfo.variant == VariantKnightmate)
king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
+ if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check
+ int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+ if(board[r][f] == k || board[r][f] == prince) {
+ if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
+ king = board[r][f]; // remember hich one we had
+ }
+ }
+ }
- if (rf >= 0) {
+ if (rt >= 0) {
if (enPassant) {
captured = board[rf][ft];
board[rf][ft] = EmptySquare;
} else {
- captured = board[rt][ft];
+ captured = board[rt][ft];
+ if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
+ }
+ if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
+ board[rt][ft] = board[rf][ff];
+ board[rf][ff] = EmptySquare;
}
- board[rt][ft] = board[rf][ff];
- board[rf][ff] = EmptySquare;
- } else board[rt][ft] = ff; // [HGM] drop
+ ep = board[EP_STATUS];
+ if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
+ ChessSquare victim = killX < 0 ? EmptySquare : trampled;
+ if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
+ (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
+ (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
+ board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
+ }
+ }
/* For compatibility with ICS wild 9, we scan the board in the
order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
cl.check++;
}
- GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
+ GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
goto undo_move; /* 2-level break */
}
undo_move:
- if (rf >= 0) {
- board[rf][ff] = board[rt][ft];
+ if (rt >= 0) {
+ if(rf != DROP_RANK) // [HGM] drop
+ board[rf][ff] = board[rt][ft];
if (enPassant) {
board[rf][ft] = captured;
board[rt][ft] = EmptySquare;
} else {
+ if(killX >= 0) board[killY][killX] = trampled;
board[rt][ft] = captured;
}
- } else board[rt][ft] = EmptySquare; // [HGM] drop
+ board[EP_STATUS] = ep;
+ }
return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
}
-ChessMove LegalDrop(board, flags, piece, rt, ft)
- Board board;
- int flags;
- ChessSquare piece;
- int rt, ft;
+int
+HasLion (Board board, int flags)
+{
+ int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
+ int r, f;
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
+ if(board[r][f] == lion) return 1;
+ return 0;
+}
+
+ChessMove
+LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
{ // [HGM] put drop legality testing in separate routine for clarity
int n;
if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
n = PieceToNumber(piece);
- if(gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
+ if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
+ && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
return ImpossibleMove; // piece not available
if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
if(piece == WhitePawn || piece == BlackPawn) {
- int r;
+ int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
for(r=1; r<BOARD_HEIGHT-1; r++)
- if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
+ if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
// should still test if we mate with this Pawn
}
} else if(gameInfo.variant == VariantSChess) { // only back-rank drops
int rf, int ff, int rt, int ft,
VOIDSTAR closure));
-void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
- Board board;
- int flags;
- ChessMove kind;
- int rf, ff, rt, ft;
- VOIDSTAR closure;
+void
+LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
{
register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
-// if (appData.debugMode) {
-// fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
-// }
if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
cl->captures++; // [HGM] losers: count legal captures
if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
cl->kind = kind;
}
-ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
- Board board;
- int flags;
- int rf, ff, rt, ft, promoChar;
+ChessMove
+LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
{
- LegalityTestClosure cl; ChessSquare piece, *castlingRights = board[CASTLING];
+ LegalityTestClosure cl; ChessSquare piece, filterPiece;
+ if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
- piece = board[rf][ff];
+ piece = filterPiece = board[rf][ff];
+ if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
- if (appData.debugMode) {
- int i;
- for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
- fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
- }
/* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
/* (perhaps we should disallow moves that obviously leave us in check?) */
- if(piece == WhiteFalcon || piece == BlackFalcon ||
- piece == WhiteCobra || piece == BlackCobra)
+ if((piece == WhiteFalcon || piece == BlackFalcon ||
+ piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu)
return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
cl.rf = rf;
cl.ff = ff;
- cl.rt = rt;
- cl.ft = ft;
+ cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
+ cl.ft = fFilter = ft;
cl.kind = IllegalMove;
cl.captures = 0; // [HGM] losers: prepare to count legal captures.
- GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl);
+ if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
+ GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
&& cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
if(board[rf][ff] < BlackPawn) { // white
if(rf != 0) return IllegalMove; // must be on back rank
+ if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
+ if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
+ if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
} else {
if(rf != BOARD_HEIGHT-1) return IllegalMove;
+ if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
+ if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
+ if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
}
} else
+ if(gameInfo.variant == VariantChu) {
+ if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
+ if(promoChar != '+')
+ return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
+ if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
+ return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
+ } else
if(gameInfo.variant == VariantShogi) {
/* [HGM] Shogi promotions. '=' means defer */
if(rf != DROP_RANK && cl.kind == NormalMove) {
return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
else if(flags & F_WHITE_ON_MOVE) {
if( (int) piece < (int) WhiteWazir &&
- (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
+ (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
}
} else
if (promoChar != NULLCHAR) {
+ if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
+ ChessSquare piece = board[rf][ff];
+ if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
+ // should test if in zone, really
+ if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
+ return IllegalMove;
+ if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
+ } else
if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
if(piece == EmptySquare)
cl.kind = ImpossibleMove; // non-existing piece
- if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
+ if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
+ cl.kind = IllegalMove; // no two Lions
+ } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
if(promoChar != PieceToChar(BlackKing)) {
if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
if(piece == BlackLance) cl.kind = ImpossibleMove;
- } else { // promotion to King allowed only if we do not haave two yet
+ } else { // promotion to King allowed only if we do not have two yet
int r, f, kings = 0;
for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
if(kings == 2) cl.kind = IllegalMove;
}
- } else if(piece == WhitePawn || piece == BlackPawn) cl.kind = ImpossibleMove; // cannot stay Pawn in any variant
+ } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
+ piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
cl.kind = IllegalMove; // promotion to Royal Knight not allowed
else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
int rf, int ff, int rt, int ft,
VOIDSTAR closure));
-void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
- Board board;
- int flags;
- ChessMove kind;
- int rf, ff, rt, ft;
- VOIDSTAR closure;
+void
+MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
{
register MateTestClosure *cl = (MateTestClosure *) closure;
}
/* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
-int MateTest(board, flags)
- Board board;
- int flags;
+int
+MateTest (Board board, int flags)
{
MateTestClosure cl;
int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
else hisPieces++;
}
}
- if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
switch(gameInfo.variant) { // [HGM] losers: extinction wins
case VariantShatranj:
if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
if(myPieces == 1) return MT_BARE;
}
cl.count = 0;
- inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl);
+ inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
// [HGM] 3check: yet to do!
if (cl.count > 0) {
return inCheck ? MT_CHECK : MT_NONE;
} else {
- if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat) { // drop game
+ if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
+ && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
return inCheck ? MT_CHECKMATE
- : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
+ : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
MT_STAINMATE : MT_STALEMATE;
}
}
int rf, int ff, int rt, int ft,
VOIDSTAR closure));
-void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
- Board board;
- int flags;
- ChessMove kind;
- int rf, ff, rt, ft;
- VOIDSTAR closure;
+void
+DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
{
register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
int wildCard = FALSE; ChessSquare piece = board[rf][ff];
}
}
-void Disambiguate(board, flags, closure)
- Board board;
- int flags;
- DisambiguateClosure *closure;
+void
+Disambiguate (Board board, int flags, DisambiguateClosure *closure)
{
int illegal = 0; char c = closure->promoCharIn;
+ if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
closure->count = closure->captures = 0;
closure->rf = closure->ff = closure->rt = closure->ft = 0;
closure->kind = ImpossibleMove;
- if (appData.debugMode) {
- fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
- closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
- closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
- }
- GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure);
+ rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
+ fFilter = closure->ftIn;
+ if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
+ GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
+ if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
+ closure->count = closure->captures = 0;
+ closure->rf = closure->ff = closure->rt = closure->ft = 0;
+ closure->kind = ImpossibleMove;
+ GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
+ }
+ } else
+ GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
if (closure->count == 0) {
/* See if it's an illegal move due to check */
illegal = 1;
- GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure);
+ GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
if (closure->count == 0) {
/* No, it's not even that */
- if (appData.debugMode) { int i, j;
- for(i=BOARD_HEIGHT-1; i>=0; i--) {
- for(j=0; j<BOARD_WIDTH; j++)
- fprintf(debugFP, "%3d", (int) board[i][j]);
- fprintf(debugFP, "\n");
- }
- }
+ if(!appData.testLegality && closure->pieceIn != EmptySquare) {
+ int f, r; // if there is only a single piece of the requested type on the board, use that
+ closure->rt = closure->rtIn, closure->ft = closure->ftIn;
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
+ if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
+ if(closure->count > 1) illegal = 0; // ambiguous
+ }
+ if(closure->count == 0) {
+ if (appData.debugMode) { int i, j;
+ for(i=BOARD_HEIGHT-1; i>=0; i--) {
+ for(j=0; j<BOARD_WIDTH; j++)
+ fprintf(debugFP, "%3d", (int) board[i][j]);
+ fprintf(debugFP, "\n");
+ }
+ }
return;
+ }
}
}
if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
if(closure->piece < BlackPawn) { // white
if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
+ if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
+ if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
+ if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
} else {
if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
+ if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
+ if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
+ if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
}
} else
+ if(gameInfo.variant == VariantChu) {
+ if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
+ } else
if(gameInfo.variant == VariantShogi) {
/* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
closure->kind = c == '=' ? IllegalMove : WhitePromotion;
else /* promotion optional, default is defer */
- closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
+ closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
} else closure->kind = c == '+' ? IllegalMove : NormalMove;
} else {
if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
} else
if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
- if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
+ if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
+ gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
c = PieceToChar(BlackFerz);
else if(gameInfo.variant == VariantGreat)
c = PieceToChar(BlackMan);
+ else if(gameInfo.variant == VariantGrand)
+ closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
else
c = PieceToChar(BlackQueen);
} else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
+ else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
+ } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
+ ChessSquare p = closure->piece;
+ if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
+ closure->kind = ImpossibleMove; // used on non-promotable piece
+ else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
} else if (c != NULLCHAR) closure->kind = IllegalMove;
closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
*/
closure->kind = IllegalMove;
}
- if (appData.debugMode) {
- fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
- closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
- closure->promoChar >= ' ' ? closure->promoChar:'-');
- }
}
ChessMove kind, int rf, int ff,
int rt, int ft, VOIDSTAR closure));
-void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
- Board board;
- int flags;
- ChessMove kind;
- int rf, ff, rt, ft;
- VOIDSTAR closure;
+void
+CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
{
register CoordsToAlgebraicClosure *cl =
(CoordsToAlgebraicClosure *) closure;
/* Convert coordinates to normal algebraic notation.
promoChar must be NULLCHAR or 'x' if not a promotion.
*/
-ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
- Board board;
- int flags;
- int rf, ff, rt, ft;
- int promoChar;
- char out[MOVE_LEN];
+ChessMove
+CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
{
ChessSquare piece;
ChessMove kind;
- char *outp = out, c;
+ char *outp = out, c, capture;
CoordsToAlgebraicClosure cl;
if (rf == DROP_RANK) {
+ if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
/* Bughouse piece drop */
*outp++ = ToUpper(PieceToChar((ChessSquare) ff));
*outp++ = '@';
piece = board[rf][ff];
if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
- if (appData.debugMode)
- fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
switch (piece) {
case WhitePawn:
case BlackPawn:
}
/* Pawn move */
*outp++ = ff + AAA;
- if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
+ capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
+ if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
/* Non-capture; use style "e5" */
if(rt+ONE <= '9')
*outp++ = rt + ONE;
else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
} else {
/* Capture; use style "exd5" */
- if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
+ if(capture)
*outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
*outp++ = ft + AAA;
if(rt+ONE <= '9')
}
/* Use promotion suffix style "=Q" */
*outp = NULLCHAR;
- if (appData.debugMode)
- fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
if (promoChar != NULLCHAR) {
- if(gameInfo.variant == VariantShogi) {
+ if(IS_SHOGI(gameInfo.variant)) {
/* [HGM] ... but not in Shogi! */
*outp++ = promoChar == '=' ? '=' : '+';
} else {
/* Piece move */
cl.rf = rf;
cl.ff = ff;
- cl.rt = rt;
- cl.ft = ft;
+ cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
+ cl.ft = fFilter = ft;
cl.piece = piece;
cl.kind = IllegalMove;
cl.rank = cl.file = cl.either = 0;
- GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
+ c = PieceToChar(piece) ;
+ GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
/* Generate pretty moves for moving into check, but
still return IllegalMove.
*/
- GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
+ GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
if (cl.kind == IllegalMove) break;
cl.kind = IllegalMove;
}
else "N1f3" or "N5xf7",
else "Ng1f3" or "Ng5xf7".
*/
- c = PieceToChar(piece) ;
if( c == '~' || c == '+') {
/* [HGM] print nonexistent piece as its demoted version */
- piece = (ChessSquare) (DEMOTED piece);
+ piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
}
if(c=='+') *outp++ = c;
*outp++ = ToUpper(PieceToChar(piece));
if(rt+ONE <= '9')
*outp++ = rt + ONE;
else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
- if (gameInfo.variant == VariantShogi) {
+ if (IS_SHOGI(gameInfo.variant)) {
/* [HGM] in Shogi non-pawns can promote */
*outp++ = promoChar; // Don't bother to correct move type, return value is never used!
}
- else if (gameInfo.variant != VariantSuper && promoChar &&
+ else if (gameInfo.variant == VariantChuChess && promoChar ||
+ gameInfo.variant != VariantSuper && promoChar &&
(piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
*outp++ = '=';
*outp++ = ToUpper(promoChar);
}
*outp = NULLCHAR;
return cl.kind;
-
+
case EmptySquare:
/* Moving a nonexistent piece */
break;
int preyStackPointer, chaseStackPointer;
struct {
-char rf, ff, rt, ft;
+unsigned char rf, ff, rt, ft;
} chaseStack[100];
struct {
-char rank, file;
+unsigned char rank, file;
} preyStack[100];
int rf, int ff, int rt, int ft,
VOIDSTAR closure));
-void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
- Board board;
- int flags;
- ChessMove kind;
- int rf, ff, rt, ft;
- VOIDSTAR closure;
+void
+AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
{ // For adding captures that can lead to chase indictment to the chaseStack
if(board[rt][ft] == EmptySquare) return; // non-capture
if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
int rf, int ff, int rt, int ft,
VOIDSTAR closure));
-void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
- Board board;
- int flags;
- ChessMove kind;
- int rf, ff, rt, ft;
- VOIDSTAR closure;
+void
+ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
{ // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
int i;
register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
int rf, int ff, int rt, int ft,
VOIDSTAR closure));
-void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
- Board board;
- int flags;
- ChessMove kind;
- int rf, ff, rt, ft;
- VOIDSTAR closure;
+void
+ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
{ // for determining if a piece (given through the closure) is protected
register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
extern char moveList[MAX_MOVES][MOVE_LEN];
-int PerpetualChase(int first, int last)
+int
+PerpetualChase (int first, int last)
{ // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
int i, j, k, tail;
ChaseClosure cl;
if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
chaseStackPointer = 0; // clear stack that is going to hold possible chases
// determine all captures possible after the move, and put them on chaseStack
- GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl);
+ GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
if(appData.debugMode) { int n;
for(n=0; n<chaseStackPointer; n++)
fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
cl.rt = moveList[i][3]-ONE;
cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
- GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl);
+ CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
+ GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
+ xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
if(appData.debugMode) { int n;
for(n=0; n<chaseStackPointer; n++)
fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
}
// the attack is on a lower piece, or on a pinned or blocked equal one
+ CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
+ CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
// test if the victim is protected by a true protector. First make the capture.
captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
if(appData.debugMode) {
fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
}
- GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl); // try all moves
+ xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
+ GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
+ xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
// unmake the capture
boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
}
}
}
- return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased
+ return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
+ : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the
}