--- /dev/null
+/*
+ * moves.c - Move generation and checking
+ * $Id$
+ *
+ * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
+ * Enhancements Copyright 1992-95 Free Software Foundation, Inc.
+ *
+ * The following terms apply to Digital Equipment Corporation's copyright
+ * interest in XBoard:
+ * ------------------------------------------------------------------------
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * ------------------------------------------------------------------------
+ *
+ * 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
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ * ------------------------------------------------------------------------
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#if HAVE_STRING_H
+# include <string.h>
+#else /* not HAVE_STRING_H */
+# include <strings.h>
+#endif /* not HAVE_STRING_H */
+#include "common.h"
+#include "backend.h"
+#include "moves.h"
+#include "parser.h"
+
+int WhitePiece P((ChessSquare));
+int BlackPiece P((ChessSquare));
+int SameColor P((ChessSquare, ChessSquare));
+
+
+int WhitePiece(piece)
+ ChessSquare piece;
+{
+ return (int) piece >= (int) WhitePawn && (int) piece <= (int) WhiteKing;
+}
+
+int BlackPiece(piece)
+ ChessSquare piece;
+{
+ return (int) piece >= (int) BlackPawn && (int) piece <= (int) BlackKing;
+}
+
+int SameColor(piece1, piece2)
+ ChessSquare piece1, piece2;
+{
+ return ((int) piece1 >= (int) WhitePawn &&
+ (int) piece1 <= (int) WhiteKing &&
+ (int) piece2 >= (int) WhitePawn &&
+ (int) piece2 <= (int) WhiteKing)
+ || ((int) piece1 >= (int) BlackPawn &&
+ (int) piece1 <= (int) BlackKing &&
+ (int) piece2 >= (int) BlackPawn &&
+ (int) piece2 <= (int) BlackKing);
+}
+
+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;
+ }
+}
+
+ChessMove PromoCharToMoveType(whiteOnMove, promoChar)
+ int whiteOnMove;
+ int promoChar;
+{
+ if (whiteOnMove) {
+ switch (promoChar) {
+ case 'n':
+ case 'N':
+ return WhitePromotionKnight;
+ case 'b':
+ case 'B':
+ return WhitePromotionBishop;
+ case 'r':
+ case 'R':
+ return WhitePromotionRook;
+ case 'q':
+ case 'Q':
+ return WhitePromotionQueen;
+ case 'k':
+ case 'K':
+ return WhitePromotionKing;
+ case NULLCHAR:
+ default:
+ return NormalMove;
+ }
+ } else {
+ switch (promoChar) {
+ case 'n':
+ case 'N':
+ return BlackPromotionKnight;
+ case 'b':
+ case 'B':
+ return BlackPromotionBishop;
+ case 'r':
+ case 'R':
+ return BlackPromotionRook;
+ case 'q':
+ case 'Q':
+ return BlackPromotionQueen;
+ case 'k':
+ case 'K':
+ return BlackPromotionKing;
+ case NULLCHAR:
+ default:
+ return NormalMove;
+ }
+ }
+}
+
+char pieceToChar[] = {
+ 'P', 'N', 'B', 'R', 'Q', 'K',
+ 'p', 'n', 'b', 'r', 'q', 'k', 'x'
+ };
+
+char PieceToChar(p)
+ ChessSquare p;
+{
+ return pieceToChar[(int) p];
+}
+
+ChessSquare CharToPiece(c)
+ int c;
+{
+ switch (c) {
+ default:
+ case 'x': return EmptySquare;
+ case 'P': return WhitePawn;
+ case 'R': return WhiteRook;
+ case 'N': return WhiteKnight;
+ case 'B': return WhiteBishop;
+ case 'Q': return WhiteQueen;
+ case 'K': return WhiteKing;
+ case 'p': return BlackPawn;
+ case 'r': return BlackRook;
+ case 'n': return BlackKnight;
+ case 'b': return BlackBishop;
+ case 'q': return BlackQueen;
+ case 'k': return BlackKing;
+ }
+}
+
+void CopyBoard(to, from)
+ Board to, from;
+{
+ int i, j;
+
+ for (i = 0; i < BOARD_SIZE; i++)
+ for (j = 0; j < BOARD_SIZE; j++)
+ to[i][j] = from[i][j];
+}
+
+int CompareBoards(board1, board2)
+ Board board1, board2;
+{
+ int i, j;
+
+ for (i = 0; i < BOARD_SIZE; i++)
+ for (j = 0; j < BOARD_SIZE; j++) {
+ if (board1[i][j] != board2[i][j])
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/* Call callback once for each pseudo-legal move in the given
+ position, except castling moves. A move is pseudo-legal if it is
+ legal, or if it would be legal except that it leaves the king in
+ check. In the arguments, epfile is EP_NONE if the previous move
+ was not a double pawn push, or the file 0..7 if it was, or
+ 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)
+ Board board;
+ int flags;
+ int epfile;
+ MoveCallback callback;
+ VOIDSTAR closure;
+{
+ int rf, ff;
+ int i, j, d, s, fs, rs, rt, ft;
+
+ for (rf = 0; rf <= 7; rf++)
+ for (ff = 0; ff <= 7; ff++) {
+ if (flags & F_WHITE_ON_MOVE) {
+ if (!WhitePiece(board[rf][ff])) continue;
+ } else {
+ if (!BlackPiece(board[rf][ff])) continue;
+ }
+ switch (board[rf][ff]) {
+ case EmptySquare:
+ default:
+ /* can't happen */
+ break;
+
+ case WhitePawn:
+ if (rf < 7 && board[rf + 1][ff] == EmptySquare) {
+ callback(board, flags,
+ rf == 6 ? WhitePromotionQueen : NormalMove,
+ rf, ff, rf + 1, ff, closure);
+ }
+ if (rf == 1 && board[2][ff] == EmptySquare &&
+ board[3][ff] == EmptySquare) {
+ callback(board, flags, NormalMove,
+ rf, ff, 3, ff, closure);
+ }
+ for (s = -1; s <= 1; s += 2) {
+ if (rf < 7 && ff + s >= 0 && ff + s <= 7 &&
+ ((flags & F_KRIEGSPIEL_CAPTURE) ||
+ BlackPiece(board[rf + 1][ff + s]))) {
+ callback(board, flags,
+ rf == 6 ? WhitePromotionQueen : NormalMove,
+ rf, ff, rf + 1, ff + s, closure);
+ }
+ if (rf == 4) {
+ if (ff + s >= 0 && ff + s <= 7 &&
+ (epfile == ff + s || epfile == EP_UNKNOWN) &&
+ board[4][ff + s] == BlackPawn &&
+ board[5][ff + s] == EmptySquare) {
+ callback(board, flags, WhiteCapturesEnPassant,
+ rf, ff, 5, ff + s, closure);
+ }
+ }
+ }
+ break;
+
+ case BlackPawn:
+ if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
+ callback(board, flags,
+ rf == 1 ? BlackPromotionQueen : NormalMove,
+ rf, ff, rf - 1, ff, closure);
+ }
+ if (rf == 6 && board[5][ff] == EmptySquare &&
+ board[4][ff] == EmptySquare) {
+ callback(board, flags, NormalMove,
+ rf, ff, 4, ff, closure);
+ }
+ for (s = -1; s <= 1; s += 2) {
+ if (rf > 0 && ff + s >= 0 && ff + s <= 7 &&
+ ((flags & F_KRIEGSPIEL_CAPTURE) ||
+ WhitePiece(board[rf - 1][ff + s]))) {
+ callback(board, flags,
+ rf == 1 ? BlackPromotionQueen : NormalMove,
+ rf, ff, rf - 1, ff + s, closure);
+ }
+ if (rf == 3) {
+ if (ff + s >= 0 && ff + s <= 7 &&
+ (epfile == ff + s || epfile == EP_UNKNOWN) &&
+ board[3][ff + s] == WhitePawn &&
+ board[2][ff + s] == EmptySquare) {
+ callback(board, flags, BlackCapturesEnPassant,
+ rf, ff, 2, ff + s, closure);
+ }
+ }
+ }
+ break;
+
+ case WhiteKnight:
+ case BlackKnight:
+ for (i = -1; i <= 1; i += 2)
+ for (j = -1; j <= 1; j += 2)
+ for (s = 1; s <= 2; s++) {
+ rt = rf + i*s;
+ ft = ff + j*(3-s);
+ if (rt < 0 || rt > 7 || ft < 0 || ft > 7) continue;
+ if (SameColor(board[rf][ff], board[rt][ft])) continue;
+ callback(board, flags, NormalMove,
+ rf, ff, rt, ft, closure);
+ }
+ break;
+
+ case WhiteBishop:
+ case BlackBishop:
+ for (rs = -1; rs <= 1; rs += 2)
+ for (fs = -1; fs <= 1; fs += 2)
+ for (i = 1;; i++) {
+ rt = rf + (i * rs);
+ ft = ff + (i * fs);
+ if (rt < 0 || rt > 7 || ft < 0 || ft > 7) break;
+ if (SameColor(board[rf][ff], board[rt][ft])) break;
+ callback(board, flags, NormalMove,
+ rf, ff, rt, ft, closure);
+ if (board[rt][ft] != EmptySquare) break;
+ }
+ break;
+
+ case WhiteRook:
+ case BlackRook:
+ for (d = 0; d <= 1; d++)
+ for (s = -1; s <= 1; s += 2)
+ for (i = 1;; i++) {
+ rt = rf + (i * s) * d;
+ ft = ff + (i * s) * (1 - d);
+ if (rt < 0 || rt > 7 || ft < 0 || ft > 7) break;
+ if (SameColor(board[rf][ff], board[rt][ft])) break;
+ callback(board, flags, NormalMove,
+ rf, ff, rt, ft, closure);
+ if (board[rt][ft] != EmptySquare) break;
+ }
+ break;
+
+ case WhiteQueen:
+ case BlackQueen:
+ for (rs = -1; rs <= 1; rs++)
+ for (fs = -1; fs <= 1; fs++) {
+ if (rs == 0 && fs == 0) continue;
+ for (i = 1;; i++) {
+ rt = rf + (i * rs);
+ ft = ff + (i * fs);
+ if (rt < 0 || rt > 7 || ft < 0 || ft > 7) break;
+ if (SameColor(board[rf][ff], board[rt][ft])) break;
+ callback(board, flags, NormalMove,
+ rf, ff, rt, ft, closure);
+ if (board[rt][ft] != EmptySquare) break;
+ }
+ }
+ break;
+
+ case WhiteKing:
+ case BlackKing:
+ for (i = -1; i <= 1; i++)
+ for (j = -1; j <= 1; j++) {
+ if (i == 0 && j == 0) continue;
+ rt = rf + i;
+ ft = ff + j;
+ if (rt < 0 || rt > 7 || ft < 0 || ft > 7) continue;
+ if (SameColor(board[rf][ff], board[rt][ft])) continue;
+ callback(board, flags, NormalMove,
+ rf, ff, rt, ft, closure);
+ }
+ break;
+ }
+ }
+}
+
+
+typedef struct {
+ MoveCallback cb;
+ VOIDSTAR cl;
+} GenLegalClosure;
+
+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;
+{
+ register GenLegalClosure *cl = (GenLegalClosure *) closure;
+
+ if (!(flags & F_IGNORE_CHECK) &&
+ CheckTest(board, flags, rf, ff, rt, ft,
+ kind == WhiteCapturesEnPassant ||
+ kind == BlackCapturesEnPassant)) return;
+ if (flags & F_ATOMIC_CAPTURE) {
+ if (board[rt][ft] != EmptySquare ||
+ kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
+ int r, f;
+ ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
+ if (board[rf][ff] == king) return;
+ for (r = rt-1; r <= rt+1; r++) {
+ for (f = ft-1; f <= ft+1; f++) {
+ if (r >= 0 && r <= 7 && f >= 0 && f <= 7 &&
+ board[r][f] == king) return;
+ }
+ }
+ }
+ }
+ cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
+}
+
+
+/* Like GenPseudoLegal, but (1) include castling moves, (2) unless
+ F_IGNORE_CHECK is set in the flags, omit moves that would leave the
+ king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
+ moves that would destroy your own king. The CASTLE_OK flags are
+ 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. */
+int GenLegal(board, flags, epfile, callback, closure)
+ Board board;
+ int flags;
+ int epfile;
+ MoveCallback callback;
+ VOIDSTAR closure;
+{
+ GenLegalClosure cl;
+ int ff;
+ int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
+
+ cl.cb = callback;
+ cl.cl = closure;
+ GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
+
+ if (!ignoreCheck &&
+ CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
+
+ /* Generate castling moves */
+ for (ff = 4; ff >= 3; ff-- /*ics wild 1*/) {
+ if ((flags & F_WHITE_ON_MOVE) &&
+ (flags & F_WHITE_KCASTLE_OK) &&
+ board[0][ff] == WhiteKing &&
+ board[0][ff + 1] == EmptySquare &&
+ board[0][ff + 2] == EmptySquare &&
+ board[0][6] == EmptySquare &&
+ board[0][7] == WhiteRook &&
+ (ignoreCheck ||
+ (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
+ !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
+
+ callback(board, flags,
+ ff==4 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
+ 0, ff, 0, ff + 2, closure);
+ }
+ if ((flags & F_WHITE_ON_MOVE) &&
+ (flags & F_WHITE_QCASTLE_OK) &&
+ board[0][ff] == WhiteKing &&
+ board[0][ff - 1] == EmptySquare &&
+ board[0][ff - 2] == EmptySquare &&
+ board[0][1] == EmptySquare &&
+ board[0][0] == WhiteRook &&
+ (ignoreCheck ||
+ (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
+ !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
+
+ callback(board, flags,
+ ff==4 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
+ 0, ff, 0, ff - 2, closure);
+ }
+ if (!(flags & F_WHITE_ON_MOVE) &&
+ (flags & F_BLACK_KCASTLE_OK) &&
+ board[7][ff] == BlackKing &&
+ board[7][ff + 1] == EmptySquare &&
+ board[7][ff + 2] == EmptySquare &&
+ board[7][6] == EmptySquare &&
+ board[7][7] == BlackRook &&
+ (ignoreCheck ||
+ (!CheckTest(board, flags, 7, ff, 7, ff + 1, FALSE) &&
+ !CheckTest(board, flags, 7, ff, 7, ff + 2, FALSE)))) {
+
+ callback(board, flags,
+ ff==4 ? BlackKingSideCastle : BlackKingSideCastleWild,
+ 7, ff, 7, ff + 2, closure);
+ }
+ if (!(flags & F_WHITE_ON_MOVE) &&
+ (flags & F_BLACK_QCASTLE_OK) &&
+ board[7][ff] == BlackKing &&
+ board[7][ff - 1] == EmptySquare &&
+ board[7][ff - 2] == EmptySquare &&
+ board[7][1] == EmptySquare &&
+ board[7][0] == BlackRook &&
+ (ignoreCheck ||
+ (!CheckTest(board, flags, 7, ff, 7, ff - 1, FALSE) &&
+ !CheckTest(board, flags, 7, ff, 7, ff - 1, FALSE)))) {
+
+ callback(board, flags,
+ ff==4 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
+ 7, ff, 7, ff - 2, closure);
+ }
+ }
+
+ return FALSE;
+}
+
+
+typedef struct {
+ int rking, fking;
+ int check;
+} CheckTestClosure;
+
+
+extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
+ int rf, int ff, int rt, int ft,
+ VOIDSTAR closure));
+
+
+void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
+ Board board;
+ int flags;
+ ChessMove kind;
+ int rf, ff, rt, ft;
+ VOIDSTAR closure;
+{
+ register CheckTestClosure *cl = (CheckTestClosure *) closure;
+
+ if (rt == cl->rking && ft == cl->fking) cl->check++;
+}
+
+
+/* If the player on move were to move from (rf, ff) to (rt, ft), would
+ he leave himself in check? Or if rf == -1, is the player on move
+ in check now? enPassant must be TRUE if the indicated move is an
+ 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. */
+int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
+ Board board;
+ int flags;
+ int rf, ff, rt, ft, enPassant;
+{
+ CheckTestClosure cl;
+ ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
+ ChessSquare captured = EmptySquare;
+ /* Suppress warnings on uninitialized variables */
+
+ if (rf >= 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;
+ }
+
+ /* 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 = 0; cl.fking <= 7; cl.fking++)
+ for (cl.rking = 0; cl.rking <= 7; cl.rking++) {
+ if (board[cl.rking][cl.fking] == king) {
+ GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
+ CheckTestCallback, (VOIDSTAR) &cl);
+ goto undo_move; /* 2-level break */
+ }
+ }
+
+ undo_move:
+
+ if (rf >= 0) {
+ board[rf][ff] = board[rt][ft];
+ if (enPassant) {
+ board[rf][ft] = captured;
+ board[rt][ft] = EmptySquare;
+ } else {
+ board[rt][ft] = captured;
+ }
+ }
+
+ return cl.check;
+}
+
+
+typedef struct {
+ int rf, ff, rt, ft;
+ ChessMove kind;
+} LegalityTestClosure;
+
+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;
+{
+ register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
+
+ if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
+ cl->kind = kind;
+}
+
+ChessMove LegalityTest(board, flags, epfile, rf, ff, rt, ft, promoChar)
+ Board board;
+ int flags, epfile;
+ int rf, ff, rt, ft, promoChar;
+{
+ LegalityTestClosure cl;
+
+ cl.rf = rf;
+ cl.ff = ff;
+ cl.rt = rt;
+ cl.ft = ft;
+ cl.kind = IllegalMove;
+ GenLegal(board, flags, epfile, LegalityTestCallback, (VOIDSTAR) &cl);
+ if (promoChar != NULLCHAR && promoChar != 'x') {
+ if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
+ cl.kind =
+ PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
+ } else {
+ cl.kind = IllegalMove;
+ }
+ }
+ return cl.kind;
+}
+
+typedef struct {
+ int count;
+} MateTestClosure;
+
+extern void MateTestCallback P((Board board, int flags, ChessMove kind,
+ int rf, int ff, int rt, int ft,
+ VOIDSTAR closure));
+
+void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
+ Board board;
+ int flags;
+ ChessMove kind;
+ int rf, ff, rt, ft;
+ VOIDSTAR closure;
+{
+ register MateTestClosure *cl = (MateTestClosure *) closure;
+
+ cl->count++;
+}
+
+/* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
+int MateTest(board, flags, epfile)
+ Board board;
+ int flags, epfile;
+{
+ MateTestClosure cl;
+ int inCheck;
+
+ cl.count = 0;
+ inCheck = GenLegal(board, flags, epfile, MateTestCallback, (VOIDSTAR) &cl);
+ if (cl.count > 0) {
+ return inCheck ? MT_CHECK : MT_NONE;
+ } else {
+ return inCheck ? MT_CHECKMATE : MT_STALEMATE;
+ }
+}
+
+
+extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
+ int rf, int ff, int rt, int ft,
+ VOIDSTAR closure));
+
+void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
+ Board board;
+ int flags;
+ ChessMove kind;
+ int rf, ff, rt, ft;
+ VOIDSTAR closure;
+{
+ register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
+
+ if ((cl->pieceIn == EmptySquare || cl->pieceIn == 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->count++;
+ cl->piece = board[rf][ff];
+ cl->rf = rf;
+ cl->ff = ff;
+ cl->rt = rt;
+ cl->ft = ft;
+ cl->kind = kind;
+ }
+}
+
+void Disambiguate(board, flags, epfile, closure)
+ Board board;
+ int flags, epfile;
+ DisambiguateClosure *closure;
+{
+ int illegal = 0;
+ closure->count = 0;
+ closure->rf = closure->ff = closure->rt = closure->ft = 0;
+ closure->kind = ImpossibleMove;
+ GenLegal(board, flags, epfile, 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, DisambiguateCallback,
+ (VOIDSTAR) closure);
+ if (closure->count == 0) {
+ /* No, it's not even that */
+ return;
+ }
+ }
+ 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;
+ }
+ }
+ closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));
+ if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
+ if (closure->count > 1) {
+ closure->kind = AmbiguousMove;
+ }
+ if (illegal) {
+ /* Note: If more than one illegal move matches, but no legal
+ moves, we return IllegalMove, not AmbiguousMove. Caller
+ can look at closure->count to detect this.
+ */
+ closure->kind = IllegalMove;
+ }
+}
+
+
+typedef struct {
+ /* Input */
+ ChessSquare piece;
+ int rf, ff, rt, ft;
+ /* Output */
+ ChessMove kind;
+ int rank;
+ int file;
+ int either;
+} CoordsToAlgebraicClosure;
+
+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;
+{
+ register CoordsToAlgebraicClosure *cl =
+ (CoordsToAlgebraicClosure *) closure;
+
+ if (rt == cl->rt && ft == cl->ft &&
+ board[rf][ff] == cl->piece) {
+ if (rf == cl->rf) {
+ if (ff == cl->ff) {
+ cl->kind = kind; /* this is the move we want */
+ } else {
+ cl->file++; /* need file to rule out this move */
+ }
+ } else {
+ if (ff == cl->ff) {
+ cl->rank++; /* need rank to rule out this move */
+ } 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)
+ Board board;
+ int flags, epfile;
+ int rf, ff, rt, ft;
+ int promoChar;
+ char out[MOVE_LEN];
+{
+ ChessSquare piece;
+ ChessMove kind;
+ char *outp = out;
+ CoordsToAlgebraicClosure cl;
+
+ if (rf == DROP_RANK) {
+ /* Bughouse piece drop */
+ *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
+ *outp++ = '@';
+ *outp++ = ft + 'a';
+ *outp++ = rt + '1';
+ *outp = NULLCHAR;
+ return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
+ }
+
+ if (promoChar == 'x') promoChar = NULLCHAR;
+ piece = board[rf][ff];
+ switch (piece) {
+ case WhitePawn:
+ case BlackPawn:
+ kind = LegalityTest(board, flags, epfile, 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,
+ rf, ff, rt, ft, promoChar);
+ if (kind == IllegalMove) break;
+ kind = IllegalMove;
+ }
+ /* Pawn move */
+ *outp++ = ff + 'a';
+ if (ff == ft) {
+ /* Non-capture; use style "e5" */
+ *outp++ = rt + '1';
+ } else {
+ /* Capture; use style "exd5" */
+ *outp++ = 'x';
+ *outp++ = ft + 'a';
+ *outp++ = rt + '1';
+ }
+ /* Use promotion suffix style "=Q" */
+ if (promoChar != NULLCHAR && promoChar != 'x') {
+ *outp++ = '=';
+ *outp++ = ToUpper(promoChar);
+ }
+ *outp = NULLCHAR;
+ return kind;
+
+
+ case WhiteKing:
+ case BlackKing:
+ /* Test for castling or ICS wild castling */
+ /* Use style "O-O" (oh-oh) for PGN compatibility */
+ if (rf == rt &&
+ rf == ((piece == WhiteKing) ? 0 : 7) &&
+ ((ff == 4 && (ft == 2 || ft == 6)) ||
+ (ff == 3 && (ft == 1 || ft == 5)))) {
+ switch (ft) {
+ case 1:
+ case 6:
+ strcpy(out, "O-O");
+ break;
+ case 2:
+ case 5:
+ strcpy(out, "O-O-O");
+ break;
+ }
+ /* This notation is always unambiguous, unless there are
+ kings on both the d and e files, with "wild castling"
+ possible for the king on the d file and normal castling
+ possible for the other. ICS rules for wild 9
+ effectively make castling illegal for either king in
+ 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,
+ rf, ff, rt, ft, promoChar);
+ }
+ /* else fall through */
+
+ default:
+ /* Piece move */
+ cl.rf = rf;
+ cl.ff = ff;
+ cl.rt = rt;
+ cl.ft = ft;
+ cl.piece = piece;
+ cl.kind = IllegalMove;
+ cl.rank = cl.file = cl.either = 0;
+ GenLegal(board, flags, epfile,
+ 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,
+ CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
+ if (cl.kind == IllegalMove) break;
+ cl.kind = IllegalMove;
+ }
+
+ /* Style is "Nf3" or "Nxf7" if this is unambiguous,
+ else "Ngf3" or "Ngxf7",
+ else "N1f3" or "N5xf7",
+ else "Ng1f3" or "Ng5xf7".
+ */
+ *outp++ = ToUpper(PieceToChar(piece));
+
+ if (cl.file || (cl.either && !cl.rank)) {
+ *outp++ = ff + 'a';
+ }
+ if (cl.rank) {
+ *outp++ = rf + '1';
+ }
+
+ if(board[rt][ft] != EmptySquare)
+ *outp++ = 'x';
+
+ *outp++ = ft + 'a';
+ *outp++ = rt + '1';
+ *outp = NULLCHAR;
+ return cl.kind;
+
+ case EmptySquare:
+ /* Moving a nonexistent piece */
+ break;
+ }
+
+ /* Not a legal move, even ignoring check.
+ 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"
+ if a piece was on the to square, even
+ a piece of the same color.
+ */
+ outp = out;
+ if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
+ *outp++ = ToUpper(PieceToChar(piece));
+ }
+ *outp++ = ff + 'a';
+ *outp++ = rf + '1';
+ if (board[rt][ft] != EmptySquare) *outp++ = 'x';
+ *outp++ = ft + 'a';
+ *outp++ = rt + '1';
+ /* Use promotion suffix style "=Q" */
+ if (promoChar != NULLCHAR && promoChar != 'x') {
+ *outp++ = '=';
+ *outp++ = ToUpper(promoChar);
+ }
+ *outp = NULLCHAR;
+
+ return IllegalMove;
+}