X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=moves.c;h=381b188eadb37c2671cca0c655b4ab309316d6d9;hb=deb3473452a674b82d27b03fcf7e570c1cb03841;hp=50d7501ecf70134a259266c83bcfedd2fe192073;hpb=5521c2e29fe766689f495e85c062c491f99f0405;p=xboard.git diff --git a/moves.c b/moves.c index 50d7501..381b188 100644 --- a/moves.c +++ b/moves.c @@ -5,7 +5,7 @@ * Massachusetts. * * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. * * Enhancements Copyright 2005 Alessandro Scotti * @@ -70,22 +70,23 @@ int SameColor P((ChessSquare, ChessSquare)); int PosFlags(int index); extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */ +int quickFlag; - -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 && @@ -96,6 +97,9 @@ int SameColor(piece1, piece2) (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', @@ -105,15 +109,15 @@ char pieceToChar[] = { '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; @@ -122,10 +126,11 @@ int PieceToNumber(p) /* [HGM] holdings: count piece type, ignoring non-particip return i; } -ChessSquare CharToPiece(c) - int c; +ChessSquare +CharToPiece (int c) { int i; + if(c == '.') return EmptySquare; for(i=0; i< (int) EmptySquare; i++) if(pieceNickName[i] == c) return (ChessSquare) i; for(i=0; i< (int) EmptySquare; i++) @@ -133,21 +138,22 @@ ChessSquare CharToPiece(c) 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; @@ -168,33 +174,31 @@ int CompareBoards(board1, board2) 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 ? 3 : 1; for (rf = 0; rf < BOARD_HEIGHT; rf++) for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) { ChessSquare piece; + int rookRange; - 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 + rookRange = 1000; m = 0; piece = board[rf][ff]; if(PieceToChar(piece) == '~') piece = (ChessSquare) ( DEMOTED piece ); + if(filter != EmptySquare && piece != filter) continue; if(gameInfo.variant == VariantShogi) 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...) */ @@ -224,12 +228,12 @@ void GenPseudoLegal(board, flags, callback, closure) 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 && @@ -239,13 +243,13 @@ void GenPseudoLegal(board, flags, callback, closure) 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); } } } @@ -274,12 +278,12 @@ void GenPseudoLegal(board, flags, callback, 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 && @@ -289,13 +293,13 @@ void GenPseudoLegal(board, flags, callback, closure) 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); } } } @@ -418,7 +422,8 @@ void GenPseudoLegal(board, flags, callback, closure) && !SameColor(board[rf][ff], board[rt][ft])) callback(board, flags, NormalMove, rf, ff, rt, ft, closure); - if(gameInfo.variant != VariantFairy && gameInfo.variant != VariantGreat) continue; + if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier + || 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) @@ -426,8 +431,26 @@ void GenPseudoLegal(board, flags, callback, closure) callback(board, flags, NormalMove, rf, ff, rt, ft, closure); } + if(gameInfo.variant == VariantSpartan) + for(fs = -1; fs <= 1; fs += 2) { + ft = ff + fs; + if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare) + callback(board, flags, NormalMove, rf, ff, rf, ft, closure); + } break; + /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */ + case WhiteCardinal: + case BlackCardinal: + 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) continue; + if (SameColor(board[rf][ff], board[rt][ft])) continue; + callback(board, flags, NormalMove, rf, ff, rt, ft, closure); + } + /* Shogi Dragon Horse has to continue with Wazir after Bishop */ case SHOGI WhiteCardinal: case SHOGI BlackCardinal: @@ -495,8 +518,9 @@ void GenPseudoLegal(board, flags, callback, closure) 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; - + /* Shogi Dragon King has to continue as Ferz after Rook moves */ case SHOGI WhiteDragon: case SHOGI BlackDragon: @@ -506,6 +530,7 @@ void GenPseudoLegal(board, flags, callback, closure) case WhiteMarshall: case BlackMarshall: m++; + m += (gameInfo.variant == VariantSpartan); // in Spartan Chess Chancellor is used for Dragon King. /* Shogi Rooks are ordinary Rooks */ case SHOGI WhiteRook: @@ -522,7 +547,7 @@ void GenPseudoLegal(board, flags, callback, closure) 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 (board[rt][ft] != EmptySquare || i == rookRange) break; } if(m==1) goto mounted; if(m==2) goto finishSilver; @@ -548,7 +573,7 @@ void GenPseudoLegal(board, flags, callback, closure) /* Shogi Pawn and Silver General: first the Pawn move, */ /* then the General continues like a Ferz */ case WhiteMan: - if(gameInfo.variant != VariantMakruk) goto commoner; + if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner; case SHOGI WhitePawn: case SHOGI WhiteFerz: if (rf < BOARD_HEIGHT-1 && @@ -559,7 +584,7 @@ void GenPseudoLegal(board, flags, callback, closure) break; case BlackMan: - if(gameInfo.variant != VariantMakruk) goto commoner; + if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner; case SHOGI BlackPawn: case SHOGI BlackFerz: if (rf > 0 && @@ -623,30 +648,48 @@ void GenPseudoLegal(board, flags, callback, closure) } 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; + // Use Lance as Berolina / Spartan Pawn. case WhiteLance: + if(gameInfo.variant == VariantSuper) goto Amazon; if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff])) callback(board, flags, rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove, 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])) callback(board, flags, rf <= promoRank ? BlackPromotion : NormalMove, 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 ) @@ -671,23 +714,37 @@ typedef struct { 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 (!(flags & F_IGNORE_CHECK) && - CheckTest(board, flags, rf, ff, rt, ft, + if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square + + if (!(flags & F_IGNORE_CHECK) ) { + int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion); + if(promo) { + int r, f, kings=0; + for(r=0; r= 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)) return; + kind == BlackCapturesEnPassant); + if(promo) board[rf][ff] = BlackLance; + if(check) return; + } if (flags & F_ATOMIC_CAPTURE) { if (board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) { @@ -720,23 +777,22 @@ typedef struct { 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; + 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 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 */ @@ -824,10 +880,11 @@ int GenLegal(board, flags, callback, closure) } } - if(flags & F_FRC_TYPE_CASTLING) { + if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) { /* generate all potential FRC castling moves (KxR), ignoring flags */ /* [HGM] test if the Rooks we find have castling rights */ + /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */ if ((flags & F_WHITE_ON_MOVE) != 0) { @@ -846,7 +903,7 @@ int GenLegal(board, flags, callback, closure) for(k=left; krking && 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) + } } @@ -925,10 +982,8 @@ void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure) 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; @@ -940,24 +995,26 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant) if(gameInfo.variant == VariantKnightmate) king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn; - if (rf >= 0) { + if (rt >= 0) { if (enPassant) { captured = board[rf][ft]; board[rf][ft] = EmptySquare; } else { captured = board[rt][ft]; } - board[rt][ft] = board[rf][ff]; - board[rf][ff] = EmptySquare; - } else board[rt][ft] = ff; // [HGM] drop + if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop + board[rt][ft] = board[rf][ff]; + board[rf][ff] = EmptySquare; + } + } /* For compatibility with ICS wild 9, we scan the board in the order a1, a2, a3, ... b1, b2, ..., h8 to find the first king, and we test only whether that one is in check. */ - cl.check = 0; for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++) for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) { if (board[cl.rking][cl.fking] == king) { + cl.check = 0; if(gameInfo.variant == VariantXiangqi) { /* [HGM] In Xiangqi opposing Kings means check as well */ int i, dir; @@ -968,38 +1025,37 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant) board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) ) cl.check++; } - - GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl); - goto undo_move; /* 2-level break */ + 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 { board[rt][ft] = captured; } - } else board[rt][ft] = EmptySquare; // [HGM] drop + } 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; +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 || @@ -1012,6 +1068,8 @@ if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt if(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 + if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove; } else { if( (piece == WhitePawn || piece == BlackPawn) && (rt == 0 || rt == BOARD_HEIGHT -1 ) ) @@ -1027,39 +1085,27 @@ extern void LegalityTestCallback P((Board board, int flags, ChessMove kind, 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 || @@ -1068,17 +1114,29 @@ ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar) 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(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case? - if(gameInfo.variant == VariantShogi) { + 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 + } 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; + } + } else + if(IS_SHOGI(gameInfo.variant)) { /* [HGM] Shogi promotions. '=' means defer */ if(rf != DROP_RANK && cl.kind == NormalMove) { ChessSquare piece = board[rf][ff]; @@ -1093,7 +1151,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo 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; @@ -1114,8 +1172,24 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo if (promoChar != NULLCHAR) { if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) { - if(CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar)) == EmptySquare) + 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(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 + int r, f, kings = 0; + for(r=0; r 1 ? MT_BARE : MT_DRAW; @@ -1174,18 +1242,19 @@ int MateTest(board, flags) 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; rft = wildCard ? cl->ftIn : ft; cl->kind = kind; } - cl->captures += (board[cl->rt][cl->ft] != EmptySquare); // [HGM] oneclick: count captures + cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures } } -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; jpieceIn != 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; rpieceIn) 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; jpiece != 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 + } 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; + } + } 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) { @@ -1293,7 +1386,7 @@ void Disambiguate(board, flags, closure) 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) ) { @@ -1310,10 +1403,13 @@ void Disambiguate(board, flags, closure) } 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 @@ -1332,11 +1428,6 @@ void Disambiguate(board, flags, closure) */ 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:'-'); - } } @@ -1355,12 +1446,8 @@ extern void CoordsToAlgebraicCallback P((Board board, int flags, 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; @@ -1389,12 +1476,8 @@ void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, 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; @@ -1402,6 +1485,7 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out) 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++ = '@'; @@ -1417,8 +1501,6 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out) 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: @@ -1448,10 +1530,8 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out) } /* 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 { @@ -1484,9 +1564,9 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out) ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) || (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) { if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2) - safeStrCpy(out, "O-O", MOVE_LEN); + snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar)); else - safeStrCpy(out, "O-O-O", MOVE_LEN); + snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar)); /* This notation is always unambiguous, unless there are kings on both the d and e files, with "wild castling" @@ -1504,18 +1584,19 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out) /* 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; } @@ -1525,7 +1606,6 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out) 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); @@ -1549,13 +1629,22 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out) 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 && + (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn + *outp++ = '='; + *outp++ = ToUpper(promoChar); + } + else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating + *outp++ = '/'; + *outp++ = ToUpper(promoChar); + } *outp = NULLCHAR; return cl.kind; - + case EmptySquare: /* Moving a nonexistent piece */ break; @@ -1612,11 +1701,11 @@ typedef struct { 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]; @@ -1628,12 +1717,8 @@ extern void AtacksCallback P((Board board, int flags, ChessMove kind, 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 @@ -1652,12 +1737,8 @@ extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind, 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 @@ -1681,12 +1762,8 @@ extern void ProtectedCallback P((Board board, int flags, ChessMove kind, 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 @@ -1697,7 +1774,8 @@ void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure) 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; @@ -1708,7 +1786,7 @@ int PerpetualChase(int first, int last) 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