/*
* moves.c - Move generation and checking
- * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $
*
- * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
- * Enhancements Copyright 1992-95 Free Software Foundation, Inc.
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,
+ * Massachusetts.
+ *
+ * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
+ * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+ *
+ * Enhancements Copyright 2005 Alessandro Scotti
*
* The following terms apply to Digital Equipment Corporation's copyright
* interest in XBoard:
* SOFTWARE.
* ------------------------------------------------------------------------
*
- * The following terms apply to the enhanced version of XBoard distributed
- * by the Free Software Foundation:
+ * The following terms apply to the enhanced version of XBoard
+ * distributed by the Free Software Foundation:
* ------------------------------------------------------------------------
- * This program is free software; you can redistribute it and/or modify
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * ------------------------------------------------------------------------
- */
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
#include "config.h"
# include <strings.h>
#endif /* not HAVE_STRING_H */
#include "common.h"
-#include "backend.h"
+#include "backend.h"
#include "moves.h"
#include "parser.h"
int WhitePiece P((ChessSquare));
int BlackPiece P((ChessSquare));
int SameColor P((ChessSquare, ChessSquare));
+int PosFlags(int index);
-extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */
+extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
int WhitePiece(piece)
(int) piece2 < (int) EmptySquare);
}
-ChessSquare PromoPiece(moveType)
- ChessMove moveType;
-{
- switch (moveType) {
- default:
- return EmptySquare;
- case WhitePromotionQueen:
- return WhiteQueen;
- case BlackPromotionQueen:
- return BlackQueen;
- case WhitePromotionRook:
- return WhiteRook;
- case BlackPromotionRook:
- return BlackRook;
- case WhitePromotionBishop:
- return WhiteBishop;
- case BlackPromotionBishop:
- return BlackBishop;
- case WhitePromotionKnight:
- return WhiteKnight;
- case BlackPromotionKnight:
- return BlackKnight;
- case WhitePromotionKing:
- return WhiteKing;
- case BlackPromotionKing:
- return BlackKing;
- case WhitePromotionChancellor:
- return WhiteMarshall;
- case BlackPromotionChancellor:
- return BlackMarshall;
- case WhitePromotionArchbishop:
- return WhiteAngel;
- case BlackPromotionArchbishop:
- return BlackAngel;
- case WhitePromotionCentaur:
- return WhiteSilver;
- case BlackPromotionCentaur:
- return BlackSilver;
- }
-}
-
char pieceToChar[] = {
- 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
- 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 's', 'U', 'K',
- 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
- 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
+ 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
+ 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
+ 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
+ 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
'x' };
+char pieceNickName[EmptySquare];
char PieceToChar(p)
ChessSquare p;
{
int i;
for(i=0; i< (int) EmptySquare; i++)
+ if(pieceNickName[i] == c) return (ChessSquare) i;
+ for(i=0; i< (int) EmptySquare; i++)
if(pieceToChar[i] == c) return (ChessSquare) i;
return EmptySquare;
}
-ChessMove PromoCharToMoveType(whiteOnMove, promoChar)
- int whiteOnMove;
- int promoChar;
-{ /* [HGM] made dependent on CharToPiece to alow alternate piece letters */
- ChessSquare piece = CharToPiece(whiteOnMove ? ToUpper(promoChar) : ToLower(promoChar) );
-
-
- if(promoChar == NULLCHAR) return NormalMove;
-
- switch(piece) {
- case WhiteQueen:
- return WhitePromotionQueen;
- case WhiteRook:
- return WhitePromotionRook;
- case WhiteBishop:
- return WhitePromotionBishop;
- case WhiteKnight:
- return WhitePromotionKnight;
- case WhiteKing:
- return WhitePromotionKing;
- case WhiteAngel:
- return WhitePromotionArchbishop;
- case WhiteMarshall:
- return WhitePromotionChancellor;
- case WhiteSilver:
- return WhitePromotionCentaur;
- case BlackQueen:
- return BlackPromotionQueen;
- case BlackRook:
- return BlackPromotionRook;
- case BlackBishop:
- return BlackPromotionBishop;
- case BlackKnight:
- return BlackPromotionKnight;
- case BlackKing:
- return BlackPromotionKing;
- case BlackAngel:
- return BlackPromotionArchbishop;
- case BlackMarshall:
- return BlackPromotionChancellor;
- case BlackSilver:
- return BlackPromotionCentaur;
- default:
- // not all promotion implemented yet! Take Queen for those we don't know.
- return (whiteOnMove ? WhitePromotionQueen : BlackPromotionQueen);
- }
-}
-
void CopyBoard(to, from)
Board to, 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
+ to[CASTLING][j] = from[CASTLING][j];
+ to[HOLDINGS_SET] = 0; // flag used in ICS play
}
int CompareBoards(board1, board2)
Board board1, board2;
{
int i, j;
-
+
for (i = 0; i < BOARD_HEIGHT; i++)
for (j = 0; j < BOARD_WIDTH; j++) {
if (board1[i][j] != board2[i][j])
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, epfile, callback, closure)
+void GenPseudoLegal(board, flags, callback, closure)
Board board;
int flags;
- int epfile;
MoveCallback callback;
VOIDSTAR closure;
{
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;
- for (rf = 0; rf < BOARD_HEIGHT; rf++)
+ for (rf = 0; rf < BOARD_HEIGHT; rf++)
for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
ChessSquare piece;
if (!BlackPiece(board[rf][ff])) continue;
}
m = 0; piece = board[rf][ff];
- if(PieceToChar(piece) == '~')
+ if(PieceToChar(piece) == '~')
piece = (ChessSquare) ( DEMOTED piece );
if(gameInfo.variant == VariantShogi)
piece = (ChessSquare) ( SHOGI piece );
}
if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
callback(board, flags,
- rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
+ 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 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
((flags & F_KRIEGSPIEL_CAPTURE) ||
BlackPiece(board[rf + 1][ff + s]))) {
- callback(board, flags,
- rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
+ callback(board, flags,
+ rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
rf, ff, rf + 1, ff + s, closure);
}
if (rf == BOARD_HEIGHT-4) {
rf, ff, 5, ff + s, closure);
}
}
- }
+ }
break;
case BlackPawn:
break;
}
if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
- callback(board, flags,
- rf == 1 ? BlackPromotionQueen : NormalMove,
+ callback(board, flags,
+ rf <= promoRank ? BlackPromotion : NormalMove,
rf, ff, rf - 1, ff, closure);
}
if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
((flags & F_KRIEGSPIEL_CAPTURE) ||
WhitePiece(board[rf - 1][ff + s]))) {
- callback(board, flags,
- rf == 1 ? BlackPromotionQueen : NormalMove,
+ callback(board, flags,
+ rf <= promoRank ? BlackPromotion : NormalMove,
rf, ff, rf - 1, ff + s, closure);
}
if (rf == 3) {
rf, ff, 2, ff + s, closure);
}
}
- }
+ }
break;
case WhiteUnicorn:
callback(board, flags, NormalMove,
rf, ff, rf - 2, ff + s, closure);
}
- }
+ }
break;
case WhiteCannon:
callback(board, flags, NormalMove,
rf, ff, rf - 1, ff + s, closure);
}
- }
+ }
case WhiteWazir:
case BlackWazir:
case WhiteAlfil:
case BlackAlfil:
/* [HGM] support Shatranj pieces */
- for (rs = -1; rs <= 1; rs += 2)
+ for (rs = -1; rs <= 1; rs += 2)
for (fs = -1; fs <= 1; fs += 2) {
rt = rf + 2 * rs;
ft = ff + 2 * fs;
if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
&& ( gameInfo.variant != VariantXiangqi ||
board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
-
+
&& !SameColor(board[rf][ff], board[rt][ft]))
callback(board, flags, NormalMove,
rf, ff, rt, ft, closure);
case SHOGI BlackBishop:
case WhiteBishop:
case BlackBishop:
- for (rs = -1; rs <= 1; rs += 2)
- for (fs = -1; fs <= 1; fs += 2)
+ 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 (board[rt][ft] != EmptySquare) break;
}
if(m==1) goto mounted;
- if(m==2) goto finishGold;
+ if(m==2) goto finishSilver;
break;
case WhiteQueen:
case BlackQueen:
- for (rs = -1; rs <= 1; rs++)
+ for (rs = -1; rs <= 1; rs++)
for (fs = -1; fs <= 1; fs++) {
if (rs == 0 && fs == 0) continue;
for (i = 1;; i++) {
/* 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:
case SHOGI WhiteFerz:
if (rf < BOARD_HEIGHT-1 &&
- !SameColor(board[rf][ff], board[rf + 1][ff]) )
+ !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;
+ case BlackMan:
+ if(gameInfo.variant != VariantMakruk) goto commoner;
case SHOGI BlackPawn:
case SHOGI BlackFerz:
if (rf > 0 &&
- !SameColor(board[rf][ff], board[rf - 1][ff]) )
+ !SameColor(board[rf][ff], board[rf - 1][ff]) )
callback(board, flags, NormalMove,
rf, ff, rf - 1, ff, closure);
if(piece == SHOGI BlackPawn) break;
case BlackFerz:
finishSilver:
/* [HGM] support Shatranj pieces */
- for (rs = -1; rs <= 1; rs += 2)
+ for (rs = -1; rs <= 1; rs += 2)
for (fs = -1; fs <= 1; fs += 2) {
rt = rf + rs;
ft = ff + fs;
case WhiteSilver:
case BlackSilver:
m++; // [HGM] superchess: use for Centaur
- case WhiteMan:
- case BlackMan:
+ commoner:
case SHOGI WhiteKing:
case SHOGI BlackKing:
case WhiteKing:
case BlackKing:
- walking:
+// walking:
for (i = -1; i <= 1; i++)
for (j = -1; j <= 1; j++) {
if (i == 0 && j == 0) continue;
}
}
break;
+ case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
+ case BlackFalcon:
+ case WhiteCobra:
+ case BlackCobra:
+ case WhiteLance:
+ case BlackLance:
+ callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+ break;
}
}
typedef struct {
int rf, ff, rt, ft;
ChessMove kind;
+ int captures; // [HGM] losers
} LegalityTestClosure;
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, epfile, castlingRights, callback, closure)
+int GenLegal(board, flags, callback, closure)
Board board;
int flags;
- int epfile;
- char castlingRights[];
MoveCallback callback;
VOIDSTAR closure;
{
GenLegalClosure cl;
int ff, ft, k, left, right;
int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
- ChessSquare wKing = WhiteKing, bKing = BlackKing;
+ ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
cl.cb = callback;
cl.cl = closure;
- GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
+ GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl);
if (!ignoreCheck &&
CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
board[0][BOARD_RGHT-3] == EmptySquare &&
board[0][BOARD_RGHT-2] == EmptySquare &&
board[0][BOARD_RGHT-1] == WhiteRook &&
- castlingRights[0] >= 0 && /* [HGM] check rights */
+ castlingRights[0] != NoRights && /* [HGM] check rights */
( castlingRights[2] == ff || castlingRights[6] == ff ) &&
- (ignoreCheck ||
+ (ignoreCheck ||
(!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
!CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
(gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
board[0][BOARD_LEFT+2] == EmptySquare &&
board[0][BOARD_LEFT+1] == EmptySquare &&
board[0][BOARD_LEFT+0] == WhiteRook &&
- castlingRights[1] >= 0 && /* [HGM] check rights */
+ castlingRights[1] != NoRights && /* [HGM] check rights */
( castlingRights[2] == ff || castlingRights[6] == ff ) &&
(ignoreCheck ||
(!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
- castlingRights[3] >= 0 && /* [HGM] check rights */
+ castlingRights[3] != NoRights && /* [HGM] check rights */
( castlingRights[5] == ff || castlingRights[7] == ff ) &&
(ignoreCheck ||
(!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
- castlingRights[4] >= 0 && /* [HGM] check rights */
+ castlingRights[4] != NoRights && /* [HGM] check rights */
( castlingRights[5] == ff || castlingRights[7] == ff ) &&
(ignoreCheck ||
(!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
}
}
- if(gameInfo.variant == VariantFischeRandom) {
+ if(flags & F_FRC_TYPE_CASTLING) {
/* generate all potential FRC castling moves (KxR), ignoring flags */
/* [HGM] test if the Rooks we find have castling rights */
if ((flags & F_WHITE_ON_MOVE) != 0) {
ff = castlingRights[2]; /* King file if we have any rights */
- if(ff > 0 && board[0][ff] == WhiteKing) {
+ if(ff != NoRights && board[0][ff] == WhiteKing) {
if (appData.debugMode) {
fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
left = ff+1;
right = BOARD_RGHT-2;
if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
- for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
- if(k != ft && board[0][k] != EmptySquare) ft = -1;
- for(k=left; k<right && ft >= 0; k++) /* then if not checked */
- if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
- if(ft >= 0 && board[0][ft] == WhiteRook)
+ for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
+ 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, ff, 0, ft, closure);
ft = castlingRights[1]; /* Rook file if we have A-side rights */
left = BOARD_LEFT+2;
right = ff-1;
if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
- for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
- if(k != ft && board[0][k] != EmptySquare) ft = -1;
- if(ff > BOARD_LEFT+2)
- for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
- if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
- if(ft >= 0 && board[0][ft] == WhiteRook)
+ for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
+ if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
+ 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, ff, 0, ft, closure);
}
} else {
ff = castlingRights[5]; /* King file if we have any rights */
- if(ff > 0 && board[BOARD_HEIGHT-1][ff] == BlackKing) {
+ if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
ft = castlingRights[3]; /* Rook file if we have H-side rights */
left = ff+1;
right = BOARD_RGHT-2;
if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
- for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
- if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
- for(k=left; k<right && ft >= 0; k++) /* then if not checked */
- if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
- if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
+ for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
+ 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, ff, BOARD_HEIGHT-1, ft, closure);
ft = castlingRights[4]; /* Rook file if we have A-side rights */
left = BOARD_LEFT+2;
right = ff-1;
if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
- for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
- if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
- if(ff > BOARD_LEFT+2)
- for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
- if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
- if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
+ for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
+ if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
+ 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, ff, BOARD_HEIGHT-1, ft, closure);
}
}
e.p. capture. The possibility of castling out of a check along the
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. */
+ king is in check. */
int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
Board board;
int flags;
}
board[rt][ft] = board[rf][ff];
board[rf][ff] = EmptySquare;
- }
+ } else board[rt][ft] = ff; // [HGM] drop
/* For compatibility with ICS wild 9, we scan the board in the
order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
cl.check++;
}
- GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
- CheckTestCallback, (VOIDSTAR) &cl);
+ GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
goto undo_move; /* 2-level break */
}
}
} else {
board[rt][ft] = captured;
}
- }
+ } else board[rt][ft] = EmptySquare; // [HGM] drop
- return cl.check;
+ 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;
+{ // [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)
+ 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 == BlackPawn || piece == BlackQueen) && rt == 0 ||
+ 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;
+ for(r=1; r<BOARD_HEIGHT-1; r++)
+ 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( (piece == WhitePawn || piece == BlackPawn) &&
+ (rt == 0 || rt == BOARD_HEIGHT -1 ) )
+ return IllegalMove; /* no pawn drops on 1st/8th */
+ }
+if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
+ if (!(flags & F_IGNORE_CHECK) &&
+ CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
+ return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
+}
extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
int rf, int ff, int rt, int ft,
// 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, epfile, castlingRights, rf, ff, rt, ft, promoChar)
+ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
Board board;
- int flags, epfile;
+ int flags;
int rf, ff, rt, ft, promoChar;
- char castlingRights[];
{
- LegalityTestClosure cl; ChessSquare piece = board[rf][ff];
-
+ LegalityTestClosure cl; ChessSquare piece, *castlingRights = board[CASTLING];
+
+ if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
+ piece = board[rf][ff];
+
if (appData.debugMode) {
int i;
for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
if(piece == WhiteFalcon || piece == BlackFalcon ||
piece == WhiteCobra || piece == BlackCobra ||
piece == WhiteLance || piece == BlackLance)
- return NormalMove;
+ return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
cl.rf = rf;
cl.ff = ff;
cl.rt = rt;
cl.ft = ft;
cl.kind = IllegalMove;
- GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
+ cl.captures = 0; // [HGM] losers: prepare to count legal captures.
+ GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl);
+ 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) {
/* [HGM] Shogi promotions. '=' means defer */
if(rf != DROP_RANK && cl.kind == NormalMove) {
ChessSquare piece = board[rf][ff];
if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
- if(promoChar != NULLCHAR && promoChar != 'x' &&
- promoChar != '+' && promoChar != '=' &&
+ if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=' &&
ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
cl.kind = IllegalMove;
else if(flags & F_WHITE_ON_MOVE) {
if( (int) piece < (int) WhiteWazir &&
- (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
+ (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
- cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
+ cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
else /* promotion optional, default is promote */
- cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
-
+ cl.kind = promoChar == '=' ? WhiteNonPromotion : WhitePromotion;
+
} else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
NormalMove : IllegalMove;
} else {
- if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
+ if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
piece == BlackKnight && rt < 2 ) /* promotion obligatory */
- cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
+ cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
else /* promotion optional, default is promote */
- cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
+ cl.kind = promoChar == '=' ? BlackNonPromotion : BlackPromotion;
- } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
- NormalMove : IllegalMove;
+ } else cl.kind = (promoChar == NULLCHAR || promoChar == '=') ? NormalMove : IllegalMove;
}
}
} else
- if (promoChar != NULLCHAR && promoChar != 'x') {
+ if (promoChar != NULLCHAR) {
if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
- if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
- cl.kind =
- PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
+ if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
+ if(CharToPiece(promoChar) == EmptySquare) cl.kind = ImpossibleMove; // non-existing piece
} else {
cl.kind = IllegalMove;
}
}
- /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
return cl.kind;
}
}
/* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
-int MateTest(board, flags, epfile, castlingRights)
+int MateTest(board, flags)
Board board;
- int flags, epfile;
- char castlingRights[];
+ int flags;
{
MateTestClosure cl;
- int inCheck;
+ int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
+ ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+ // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
+ nrKing += (board[r][f] == king); // stm has king
+ if( board[r][f] != EmptySquare ) {
+ if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
+ myPieces++;
+ 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;
+ default:
+ break;
+ case VariantAtomic:
+ if(nrKing == 0) return MT_NOKING;
+ break;
+ case VariantLosers:
+ if(myPieces == 1) return MT_BARE;
+ }
cl.count = 0;
- inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
+ inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl);
+ // [HGM] 3check: yet to do!
if (cl.count > 0) {
return inCheck ? MT_CHECK : MT_NONE;
} else {
- return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ?
- MT_CHECKMATE : MT_STALEMATE;
+ if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper || gameInfo.variant != VariantGreat) { // 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
+ if(board[n][holdings] != EmptySquare) {
+ int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
+ if(moveType == WhiteDrop || moveType == BlackDrop) return MT_CHECK; // we can resolve check by legal drop
+ }
+ }
+ if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
+ return myPieces == hisPieces ? MT_STALEMATE :
+ myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
+ else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
+ else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
+
+ return inCheck ? MT_CHECKMATE
+ : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
+ MT_STAINMATE : MT_STALEMATE;
}
}
-
+
extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
int rf, int ff, int rt, int ft,
VOIDSTAR closure));
VOIDSTAR closure;
{
register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
+ int wildCard = FALSE; ChessSquare piece = board[rf][ff];
+
+ // [HGM] wild: for wild-card pieces rt and rf are dummies
+ if(piece == WhiteFalcon || piece == BlackFalcon ||
+ piece == WhiteCobra || piece == BlackCobra ||
+ piece == WhiteLance || piece == BlackLance)
+ wildCard = TRUE;
if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
|| PieceToChar(board[rf][ff]) == '~'
) &&
(cl->rfIn == -1 || cl->rfIn == rf) &&
(cl->ffIn == -1 || cl->ffIn == ff) &&
- (cl->rtIn == -1 || cl->rtIn == rt) &&
- (cl->ftIn == -1 || cl->ftIn == ft)) {
+ (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
+ (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
cl->count++;
- cl->piece = board[rf][ff];
- cl->rf = rf;
- cl->ff = ff;
- cl->rt = rt;
- cl->ft = ft;
- cl->kind = kind;
+ if(cl->count == 1 || board[rt][ft] != EmptySquare) {
+ // [HGM] oneclick: if multiple moves, be sure we remember capture
+ cl->piece = board[rf][ff];
+ cl->rf = rf;
+ cl->ff = ff;
+ cl->rt = wildCard ? cl->rtIn : rt;
+ cl->ft = wildCard ? cl->ftIn : ft;
+ cl->kind = kind;
+ }
+ cl->captures += (board[cl->rt][cl->ft] != EmptySquare); // [HGM] oneclick: count captures
}
}
-void Disambiguate(board, flags, epfile, closure)
+void Disambiguate(board, flags, closure)
Board board;
- int flags, epfile;
+ int flags;
DisambiguateClosure *closure;
{
int illegal = 0; char c = closure->promoCharIn;
- closure->count = 0;
+
+ closure->count = closure->captures = 0;
closure->rf = closure->ff = closure->rt = closure->ft = 0;
closure->kind = ImpossibleMove;
if (appData.debugMode) {
closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
}
- GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
+ GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure);
if (closure->count == 0) {
/* See if it's an illegal move due to check */
illegal = 1;
- GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
- (VOIDSTAR) closure);
+ GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure);
if (closure->count == 0) {
/* No, it's not even that */
if (appData.debugMode) { int i, j;
}
}
+ if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
if(gameInfo.variant == VariantShogi) {
- /* [HGM] Shogi promotions. '=' means defer */
+ /* [HGM] Shogi promotions. On input, '=' means defer, NULL promote. Afterwards, c is set to '+' for promotions, NULL other */
if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
ChessSquare piece = closure->piece;
-#if 0
- if (appData.debugMode) {
- fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
- closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
- closure->promoCharIn,closure->promoCharIn);
- }
-#endif
- if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
- ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
- closure->kind = IllegalMove;
+ if(c != NULLCHAR && c != '+' && c != '=' &&
+ ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
+ closure->kind = IllegalMove; // the only allowed cases are '+', '=' and the promoted partner.
else if(flags & F_WHITE_ON_MOVE) {
-#if 0
- if (appData.debugMode) {
- fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
- closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
- closure->promoCharIn,closure->promoCharIn);
- }
-#endif
if( (int) piece < (int) WhiteWazir &&
- (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
+ (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
- closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
+ closure->kind = c == '=' ? IllegalMove : WhitePromotion;
else /* promotion optional, default is promote */
- closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
-
- } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
- NormalMove : IllegalMove;
+ closure->kind = c == '=' ? WhiteNonPromotion : WhitePromotion;
+ } else closure->kind = (c == NULLCHAR || c == '=') ? NormalMove : IllegalMove;
} else {
- if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
+ if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
- closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
+ closure->kind = c == '=' ? IllegalMove : BlackPromotion;
else /* promotion optional, default is promote */
- closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
-
- } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
- NormalMove : IllegalMove;
+ closure->kind = c == '=' ? BlackNonPromotion : BlackPromotion;
+ } else closure->kind = (c == NULLCHAR || c == '=') ? NormalMove : IllegalMove;
}
}
+ c = (closure->kind == WhitePromotion || closure->kind == BlackPromotion) ? '+' : NULLCHAR;
} else
- if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
- if (closure->kind == WhitePromotionQueen
- || closure->kind == BlackPromotionQueen) {
- closure->kind =
- PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
- closure->promoCharIn);
- } else {
- closure->kind = IllegalMove;
- }
- }
-#if 0
- if (appData.debugMode) {
- fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
- closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
- closure->promoCharIn,closure->promoCharIn);
- }
-#endif
- /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
- if(closure->promoCharIn != '=')
- closure->promoChar = ToLower(closure->promoCharIn);
- else closure->promoChar = '=';
- if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
+ if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
+ if(c == NULLCHAR) c = 'q'; // even in Shatranj, Courier, Makruk, as Apply move corrects this to Ferz (how messy!)
+ } else if (c != NULLCHAR) closure->kind = IllegalMove;
+
+ closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
+ if(c != '+' && c != NULLCHAR && CharToPiece(c) == EmptySquare) closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
if (closure->count > 1) {
closure->kind = AmbiguousMove;
}
*/
closure->kind = IllegalMove;
}
- if(closure->kind == IllegalMove)
- /* [HGM] might be a variant we don't understand, pass on promotion info */
- closure->promoChar = ToLower(closure->promoCharIn);
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,
} else {
cl->either++; /* rank or file will rule out this move */
}
- }
+ }
}
}
/* Convert coordinates to normal algebraic notation.
promoChar must be NULLCHAR or 'x' if not a promotion.
*/
-ChessMove CoordsToAlgebraic(board, flags, epfile,
- rf, ff, rt, ft, promoChar, out)
+ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
Board board;
- int flags, epfile;
+ int flags;
int rf, ff, rt, ft;
int promoChar;
char out[MOVE_LEN];
ChessMove kind;
char *outp = out, c;
CoordsToAlgebraicClosure cl;
-
+
if (rf == DROP_RANK) {
/* Bughouse piece drop */
*outp++ = ToUpper(PieceToChar((ChessSquare) ff));
switch (piece) {
case WhitePawn:
case BlackPawn:
- kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
+ kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
/* Keep short notation if move is illegal only because it
leaves the player in check, but still return IllegalMove */
- kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
- rf, ff, rt, ft, promoChar);
+ kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
if (kind == IllegalMove) break;
kind = IllegalMove;
}
}
return kind;
-
+
case WhiteKing:
case BlackKing:
/* Fabien moved code: FRC castling first (if KxR), wild castling second */
/* Code added by Tord: FRC castling. */
if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
(piece == BlackKing && board[rt][ft] == BlackRook)) {
- if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
- return LegalityTest(board, flags, epfile, initialRights,
- rf, ff, rt, ft, promoChar);
+ if(ft > ff)
+ safeStrCpy(out, "O-O", MOVE_LEN);
+ else
+ safeStrCpy(out, "O-O-O", MOVE_LEN);
+ return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
}
/* End of code added by Tord */
/* Test for castling or ICS wild castling */
/* Use style "O-O" (oh-oh) for PGN compatibility */
else if (rf == rt &&
rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
+ (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
((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)
- strcpy(out, "O-O");
+ safeStrCpy(out, "O-O", MOVE_LEN);
else
- strcpy(out, "O-O-O");
+ safeStrCpy(out, "O-O-O", MOVE_LEN);
/* This notation is always unambiguous, unless there are
kings on both the d and e files, with "wild castling"
this situation. So I am not going to worry about it;
I'll just generate an ambiguous O-O in this case.
*/
- return LegalityTest(board, flags, epfile, initialRights,
- rf, ff, rt, ft, promoChar);
+ return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
}
/* else fall through */
cl.piece = piece;
cl.kind = IllegalMove;
cl.rank = cl.file = cl.either = 0;
- GenLegal(board, flags, epfile, initialRights,
- CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
+ GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
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, epfile, initialRights,
- CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
+ GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
if (cl.kind == IllegalMove) break;
cl.kind = IllegalMove;
}
/* [HGM] in Shogi non-pawns can promote */
if(flags & F_WHITE_ON_MOVE) {
if( (int) cl.piece < (int) WhiteWazir &&
- (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
+ (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 : WhitePromotionKnight;
- else cl.kind = WhitePromotionQueen; /* promotion optional */
-
+ cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
+ else cl.kind = WhitePromotion; /* promotion optional */
} else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
NormalMove : IllegalMove;
} else {
- if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
+ if( (int) cl.piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
piece == BlackKnight && rt < 2 ) /* promotion obligatory */
- cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
- else cl.kind = BlackPromotionQueen; /* promotion optional */
+ cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
+ else cl.kind = BlackPromotion; /* promotion optional */
} else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
NormalMove : IllegalMove;
}
- if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
+ if(cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
/* for optional promotions append '+' or '=' */
if(promoChar == '=') {
*outp++ = '=';
}
}
return cl.kind;
-
+
/* [HGM] Always long notation for fairies we don't know */
case WhiteFalcon:
case BlackFalcon:
/* Moving a nonexistent piece */
break;
}
-
+
/* Not a legal move, even ignoring check.
- If there was a piece on the from square,
+ If there was a piece on the from square,
use style "Ng1g3" or "Ng1xe8";
if there was a pawn or nothing (!),
use style "g1g3" or "g1xe8". Use "x"
return IllegalMove;
}
+// [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
+
+typedef struct {
+ /* Input */
+ int rf, ff, rt, ft;
+ /* Output */
+ int recaptures;
+} ChaseClosure;
+
+// I guess the following variables logically belong in the closure too, but I was too lazy and used globals
+
+int preyStackPointer, chaseStackPointer;
+
+struct {
+char rf, ff, rt, ft;
+} chaseStack[100];
+
+struct {
+char rank, file;
+} preyStack[100];
+
+
+
+
+// there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
+
+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;
+{ // 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
+ if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
+ if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
+ if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
+ // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
+ chaseStack[chaseStackPointer].rf = rf;
+ chaseStack[chaseStackPointer].ff = ff;
+ chaseStack[chaseStackPointer].rt = rt;
+ chaseStack[chaseStackPointer].ft = ft;
+ chaseStackPointer++;
+}
+
+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;
+{ // 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
+ if(board[rt][ft] == EmptySquare) return; // no capture
+ if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
+ rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
+ }
+ // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
+ for(i=0; i<chaseStackPointer; i++) {
+ if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
+ chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
+ // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
+ chaseStack[i] = chaseStack[--chaseStackPointer];
+ break;
+ }
+ }
+}
+
+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;
+{ // for determining if a piece (given through the closure) is protected
+ register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
+
+ if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
+ if(appData.debugMode && board[rt][ft] != EmptySquare)
+ fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
+}
+
+extern char moveList[MAX_MOVES][MOVE_LEN];
+
+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;
+ ChessSquare captured;
+
+ preyStackPointer = 0; // clear stack of chased pieces
+ for(i=first; i<last; i+=2) { // for all positions with same side to move
+ 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);
+ 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,
+ chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
+ fprintf(debugFP, ": all capts\n");
+ }
+ // determine all captures possible before the move, and delete them from chaseStack
+ cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
+ 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);
+ 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,
+ chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
+ fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
+ }
+ // chaseSack now contains all captures made possible by the move
+ for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
+ int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
+ int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
+
+ if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
+ if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
+
+ if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
+ continue; // C or H attack on R is always chase; leave on chaseStack
+
+ if(attacker == victim) {
+ if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
+ chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
+ // we can capture back with equal piece, so this is no chase but a sacrifice
+ chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
+ j--; /* ! */ continue;
+ }
+
+ }
+
+ // the attack is on a lower piece, or on a pinned or blocked equal one
+ // 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];
+ boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
+ // Then test if the opponent can recapture
+ cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
+ cl.rt = chaseStack[j].rt;
+ cl.ft = chaseStack[j].ft;
+ 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
+ // 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;
+ // if a recapture was found, piece is protected, and we are not chasing it.
+ if(cl.recaptures) { // attacked piece was defended by true protector, no chase
+ chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
+ j--; /* ! */
+ }
+ }
+ // chaseStack now contains all moves that chased
+ 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,
+ chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
+ fprintf(debugFP, ": chases\n");
+ }
+ if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
+ for(j=0; j<chaseStackPointer; j++) {
+ preyStack[j].rank = chaseStack[j].rt;
+ preyStack[j].file = chaseStack[j].ft;
+ }
+ preyStackPointer = chaseStackPointer;
+ }
+ tail = 0;
+ for(j=0; j<chaseStackPointer; j++) {
+ for(k=0; k<preyStackPointer; k++) {
+ // search the victim of each chase move on the preyStack (first occurrence)
+ if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
+ if(k < tail) break; // piece was already identified as still being chased
+ preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
+ preyStack[tail] = preyStack[k]; // by swapping
+ preyStack[k] = preyStack[preyStackPointer];
+ tail++;
+ break;
+ }
+ }
+ }
+ preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
+ if(appData.debugMode) { int n;
+ for(n=0; n<preyStackPointer; n++)
+ fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
+ fprintf(debugFP, "always chased upto ply %d\n", i);
+ }
+ // now adjust the location of the chased pieces according to opponent move
+ for(j=0; j<preyStackPointer; j++) {
+ if(preyStack[j].rank == moveList[i+1][1]-ONE &&
+ preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
+ preyStack[j].rank = moveList[i+1][3]-ONE;
+ preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
+ break;
+ }
+ }
+ }
+ return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased
+}