* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
(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',
'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 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 (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
}
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 &&
((flags & F_KRIEGSPIEL_CAPTURE) ||
BlackPiece(board[rf + 1][ff + s]))) {
callback(board, flags,
- rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
+ rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
rf, ff, rf + 1, ff + s, closure);
}
if (rf == BOARD_HEIGHT-4) {
}
if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
callback(board, flags,
- rf == 1 ? BlackPromotionQueen : NormalMove,
+ rf <= promoRank ? BlackPromotion : NormalMove,
rf, ff, rf - 1, ff, closure);
}
if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
((flags & F_KRIEGSPIEL_CAPTURE) ||
WhitePiece(board[rf - 1][ff + s]))) {
callback(board, flags,
- rf == 1 ? BlackPromotionQueen : NormalMove,
+ rf <= promoRank ? BlackPromotion : NormalMove,
rf, ff, rf - 1, ff + s, closure);
}
if (rf == 3) {
/* 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 &&
if(piece != SHOGI WhitePawn) goto finishSilver;
break;
+ case BlackMan:
+ if(gameInfo.variant != VariantMakruk) goto commoner;
case SHOGI BlackPawn:
case SHOGI BlackFerz:
if (rf > 0 &&
case WhiteSilver:
case BlackSilver:
m++; // [HGM] superchess: use for Centaur
- case WhiteMan:
- case BlackMan:
+ commoner:
case SHOGI WhiteKing:
case SHOGI BlackKing:
case WhiteKing:
}
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,
} 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;
+{ // [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,
int flags;
int rf, ff, rt, ft, promoChar;
{
- LegalityTestClosure cl; ChessSquare piece = board[rf][ff], *castlingRights = board[CASTLING];
+ 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;
&& 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;
}
if (cl.count > 0) {
return inCheck ? MT_CHECK : MT_NONE;
} else {
+ 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;
(cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
cl->count++;
- 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;
+ 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
}
}
{
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) {
}
}
+ 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(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
+ if(c != NULLCHAR && c != '+' && c != '=' &&
ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
- closure->kind = IllegalMove;
+ closure->kind = IllegalMove; // the only allowed cases are '+', '=' and the promoted partner.
else if(flags & F_WHITE_ON_MOVE) {
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;
- }
- }
- /* [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,
/* 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)
/* [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++ = '=';