2 * moves.c - Move generation and checking
\r
3 * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $
\r
5 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
\r
6 * Enhancements Copyright 1992-95 Free Software Foundation, Inc.
\r
8 * The following terms apply to Digital Equipment Corporation's copyright
\r
9 * interest in XBoard:
\r
10 * ------------------------------------------------------------------------
\r
11 * All Rights Reserved
\r
13 * Permission to use, copy, modify, and distribute this software and its
\r
14 * documentation for any purpose and without fee is hereby granted,
\r
15 * provided that the above copyright notice appear in all copies and that
\r
16 * both that copyright notice and this permission notice appear in
\r
17 * supporting documentation, and that the name of Digital not be
\r
18 * used in advertising or publicity pertaining to distribution of the
\r
19 * software without specific, written prior permission.
\r
21 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
22 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
23 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
24 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
25 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
26 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
28 * ------------------------------------------------------------------------
\r
30 * The following terms apply to the enhanced version of XBoard distributed
\r
31 * by the Free Software Foundation:
\r
32 * ------------------------------------------------------------------------
\r
33 * This program is free software; you can redistribute it and/or modify
\r
34 * it under the terms of the GNU General Public License as published by
\r
35 * the Free Software Foundation; either version 2 of the License, or
\r
36 * (at your option) any later version.
\r
38 * This program is distributed in the hope that it will be useful,
\r
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
41 * GNU General Public License for more details.
\r
43 * You should have received a copy of the GNU General Public License
\r
44 * along with this program; if not, write to the Free Software
\r
45 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
46 * ------------------------------------------------------------------------
\r
53 # include <string.h>
\r
54 #else /* not HAVE_STRING_H */
\r
55 # include <strings.h>
\r
56 #endif /* not HAVE_STRING_H */
\r
58 #include "backend.h"
\r
62 int WhitePiece P((ChessSquare));
\r
63 int BlackPiece P((ChessSquare));
\r
64 int SameColor P((ChessSquare, ChessSquare));
\r
66 extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */
\r
69 int WhitePiece(piece)
\r
72 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
\r
75 int BlackPiece(piece)
\r
78 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
\r
81 int SameColor(piece1, piece2)
\r
82 ChessSquare piece1, piece2;
\r
84 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
\r
85 (int) piece1 < (int) BlackPawn &&
\r
86 (int) piece2 >= (int) WhitePawn &&
\r
87 (int) piece2 < (int) BlackPawn)
\r
88 || ((int) piece1 >= (int) BlackPawn &&
\r
89 (int) piece1 < (int) EmptySquare &&
\r
90 (int) piece2 >= (int) BlackPawn &&
\r
91 (int) piece2 < (int) EmptySquare);
\r
94 ChessSquare PromoPiece(moveType)
\r
100 case WhitePromotionQueen:
\r
102 case BlackPromotionQueen:
\r
104 case WhitePromotionRook:
\r
106 case BlackPromotionRook:
\r
108 case WhitePromotionBishop:
\r
109 return WhiteBishop;
\r
110 case BlackPromotionBishop:
\r
111 return BlackBishop;
\r
112 case WhitePromotionKnight:
\r
113 return WhiteKnight;
\r
114 case BlackPromotionKnight:
\r
115 return BlackKnight;
\r
116 case WhitePromotionKing:
\r
118 case BlackPromotionKing:
\r
121 case WhitePromotionChancellor:
\r
122 return WhiteFairyRook;
\r
123 case BlackPromotionChancellor:
\r
124 return BlackFairyRook;
\r
125 case WhitePromotionArchbishop:
\r
126 return WhiteFairyBishop;
\r
127 case BlackPromotionArchbishop:
\r
128 return BlackFairyBishop;
\r
133 ChessMove PromoCharToMoveType(whiteOnMove, promoChar)
\r
138 switch (promoChar) {
\r
141 return WhitePromotionKnight;
\r
144 return WhitePromotionBishop;
\r
147 return WhitePromotionRook;
\r
151 return WhitePromotionArchbishop;
\r
154 return WhitePromotionChancellor;
\r
158 return WhitePromotionQueen;
\r
161 return WhitePromotionKing;
\r
167 switch (promoChar) {
\r
170 return BlackPromotionKnight;
\r
173 return BlackPromotionBishop;
\r
176 return BlackPromotionRook;
\r
180 return BlackPromotionArchbishop;
\r
183 return BlackPromotionChancellor;
\r
187 return BlackPromotionQueen;
\r
190 return BlackPromotionKing;
\r
198 char pieceToChar[] = {
\r
199 'P', 'N', 'B', 'R',
\r
201 'A', 'C', 'F', 'H', 'E', 'W', 'D', 'O', 'G', 'M',
\r
203 'Q', 'K', 'p', 'n', 'b', 'r',
\r
205 'a', 'c', 'f', 'h', 'e', 'w', 'd', 'o', 'g', 'm',
\r
210 char PieceToChar(p)
\r
213 return pieceToChar[(int) p];
\r
216 ChessSquare CharToPiece(c)
\r
220 case 'x': return EmptySquare;
\r
221 case 'P': return WhitePawn;
\r
222 case 'R': return WhiteRook;
\r
223 case 'N': return WhiteKnight;
\r
224 case 'B': return WhiteBishop;
\r
225 case 'Q': return WhiteQueen;
\r
226 case 'K': return WhiteKing;
\r
227 case 'p': return BlackPawn;
\r
228 case 'r': return BlackRook;
\r
229 case 'n': return BlackKnight;
\r
230 case 'b': return BlackBishop;
\r
231 case 'q': return BlackQueen;
\r
232 case 'k': return BlackKing;
\r
234 case 'A': return WhiteCardinal;
\r
235 case 'C': return WhiteMarshall;
\r
236 case 'F': return WhiteFairyPawn;
\r
237 case 'H': return WhiteFairyKnight;
\r
238 case 'E': return WhiteFairyBishop;
\r
239 case 'W': return WhiteFairyRook;
\r
240 case 'D': return WhiteFairyCardinal;
\r
241 case 'O': return WhiteFairyMarshall;
\r
242 case 'G': return WhiteFairyQueen;
\r
243 case 'M': return WhiteFairyKing;
\r
245 case 'a': return BlackCardinal;
\r
246 case 'c': return BlackMarshall;
\r
247 case 'f': return BlackFairyPawn;
\r
248 case 'h': return BlackFairyKnight;
\r
249 case 'e': return BlackFairyBishop;
\r
250 case 'w': return BlackFairyRook;
\r
251 case 'd': return BlackFairyCardinal;
\r
252 case 'o': return BlackFairyMarshall;
\r
253 case 'g': return BlackFairyQueen;
\r
254 case 'm': return BlackFairyKing;
\r
260 void CopyBoard(to, from)
\r
265 for (i = 0; i < BOARD_HEIGHT; i++)
\r
266 for (j = 0; j < BOARD_WIDTH; j++)
\r
267 to[i][j] = from[i][j];
\r
270 int CompareBoards(board1, board2)
\r
271 Board board1, board2;
\r
275 for (i = 0; i < BOARD_HEIGHT; i++)
\r
276 for (j = 0; j < BOARD_WIDTH; j++) {
\r
277 if (board1[i][j] != board2[i][j])
\r
284 /* Call callback once for each pseudo-legal move in the given
\r
285 position, except castling moves. A move is pseudo-legal if it is
\r
286 legal, or if it would be legal except that it leaves the king in
\r
287 check. In the arguments, epfile is EP_NONE if the previous move
\r
288 was not a double pawn push, or the file 0..7 if it was, or
\r
289 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
\r
290 Promotion moves generated are to Queen only.
\r
292 void GenPseudoLegal(board, flags, epfile, callback, closure)
\r
296 MoveCallback callback;
\r
300 int i, j, d, s, fs, rs, rt, ft, m;
\r
302 for (rf = 0; rf < BOARD_HEIGHT; rf++)
\r
303 for (ff = 0; ff < BOARD_WIDTH; ff++) {
\r
304 if (flags & F_WHITE_ON_MOVE) {
\r
305 if (!WhitePiece(board[rf][ff])) continue;
\r
307 if (!BlackPiece(board[rf][ff])) continue;
\r
310 switch (board[rf][ff]) {
\r
313 /* can't happen ([HGM] except for faries...) */
\r
318 if(gameInfo.variant == VariantXiangqi) {
\r
319 /* [HGM] capture and move straight ahead in Xiangqi */
\r
320 if (rf < BOARD_HEIGHT-1 &&
\r
321 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
\r
322 callback(board, flags, NormalMove,
\r
323 rf, ff, rf + 1, ff, closure);
\r
325 /* and move sideways when across the river */
\r
326 for (s = -1; s <= 1; s += 2) {
\r
327 if (rf >= BOARD_HEIGHT>>1 &&
\r
328 ff + s >= 0 && ff + s < BOARD_WIDTH &&
\r
329 !WhitePiece(board[rf][ff+s]) ) {
\r
330 callback(board, flags, NormalMove,
\r
331 rf, ff, rf, ff+s, closure);
\r
337 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
\r
338 callback(board, flags,
\r
339 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
340 rf, ff, rf + 1, ff, closure);
\r
342 if (rf == 1 && board[2][ff] == EmptySquare &&
\r
343 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
344 board[3][ff] == EmptySquare ) {
\r
345 callback(board, flags, NormalMove,
\r
346 rf, ff, 3, ff, closure);
\r
348 for (s = -1; s <= 1; s += 2) {
\r
349 if (rf < BOARD_HEIGHT-1 && ff + s >= 0 && ff + s < BOARD_WIDTH &&
\r
350 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
351 BlackPiece(board[rf + 1][ff + s]))) {
\r
352 callback(board, flags,
\r
353 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
354 rf, ff, rf + 1, ff + s, closure);
\r
356 if (rf == BOARD_HEIGHT-4) {
\r
357 if (ff + s >= 0 && ff + s < BOARD_WIDTH &&
\r
358 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
359 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
\r
360 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
\r
361 callback(board, flags, WhiteCapturesEnPassant,
\r
362 rf, ff, 5, ff + s, closure);
\r
370 if(gameInfo.variant == VariantXiangqi) {
\r
371 /* [HGM] capture straight ahead in Xiangqi */
\r
372 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
\r
373 callback(board, flags, NormalMove,
\r
374 rf, ff, rf - 1, ff, closure);
\r
376 /* and move sideways when across the river */
\r
377 for (s = -1; s <= 1; s += 2) {
\r
378 if (rf < BOARD_HEIGHT>>1 &&
\r
379 ff + s >= 0 && ff + s < BOARD_WIDTH &&
\r
380 !BlackPiece(board[rf][ff+s]) ) {
\r
381 callback(board, flags, NormalMove,
\r
382 rf, ff, rf, ff+s, closure);
\r
388 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
\r
389 callback(board, flags,
\r
390 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
391 rf, ff, rf - 1, ff, closure);
\r
393 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
\r
394 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
395 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
\r
396 callback(board, flags, NormalMove,
\r
397 rf, ff, BOARD_HEIGHT-4, ff, closure);
\r
399 for (s = -1; s <= 1; s += 2) {
\r
400 if (rf > 0 && ff + s >= 0 && ff + s < BOARD_WIDTH &&
\r
401 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
402 WhitePiece(board[rf - 1][ff + s]))) {
\r
403 callback(board, flags,
\r
404 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
405 rf, ff, rf - 1, ff + s, closure);
\r
408 if (ff + s >= 0 && ff + s < BOARD_WIDTH &&
\r
409 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
410 board[3][ff + s] == WhitePawn &&
\r
411 board[2][ff + s] == EmptySquare) {
\r
412 callback(board, flags, BlackCapturesEnPassant,
\r
413 rf, ff, 2, ff + s, closure);
\r
422 for (i = -1; i <= 1; i += 2)
\r
423 for (j = -1; j <= 1; j += 2)
\r
424 for (s = 1; s <= 2; s++) {
\r
427 if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) continue;
\r
428 if (SameColor(board[rf][ff], board[rt][ft])) continue;
\r
429 callback(board, flags, NormalMove,
\r
430 rf, ff, rt, ft, closure);
\r
434 case WhiteFairyMarshall:
\r
435 case BlackFairyMarshall:
\r
436 for (d = 0; d <= 1; d++)
\r
437 for (s = -1; s <= 1; s += 2) {
\r
439 for (i = 1;; i++) {
\r
440 rt = rf + (i * s) * d;
\r
441 ft = ff + (i * s) * (1 - d);
\r
442 if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break;
\r
443 if (m == 0 && board[rt][ft] == EmptySquare)
\r
444 callback(board, flags, NormalMove,
\r
445 rf, ff, rt, ft, closure);
\r
446 if (m == 1 && board[rt][ft] != EmptySquare &&
\r
447 !SameColor(board[rf][ff], board[rt][ft]) )
\r
448 callback(board, flags, NormalMove,
\r
449 rf, ff, rt, ft, closure);
\r
450 if (board[rt][ft] != EmptySquare && m++) break;
\r
455 case WhiteFairyRook:
\r
456 case BlackFairyRook:
\r
457 for (d = 0; d <= 1; d++)
\r
458 for (s = -1; s <= 1; s += 2)
\r
460 ft = ff + s * (1 - d);
\r
461 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH)
\r
462 && !SameColor(board[rf][ff], board[rt][ft]))
\r
463 callback(board, flags, NormalMove,
\r
464 rf, ff, rt, ft, closure);
\r
467 case WhiteFairyBishop:
\r
468 case BlackFairyBishop:
\r
469 /* [HGM] support Shatranj pieces */
\r
470 for (rs = -1; rs <= 1; rs += 2)
\r
471 for (fs = -1; fs <= 1; fs += 2) {
\r
474 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH)
\r
475 && !SameColor(board[rf][ff], board[rt][ft]))
\r
476 callback(board, flags, NormalMove,
\r
477 rf, ff, rt, ft, closure);
\r
481 case WhiteCardinal:
\r
482 case BlackCardinal:
\r
487 for (rs = -1; rs <= 1; rs += 2)
\r
488 for (fs = -1; fs <= 1; fs += 2)
\r
489 for (i = 1;; i++) {
\r
490 rt = rf + (i * rs);
\r
491 ft = ff + (i * fs);
\r
492 if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break;
\r
493 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
494 callback(board, flags, NormalMove,
\r
495 rf, ff, rt, ft, closure);
\r
496 if (board[rt][ft] != EmptySquare) break;
\r
498 if(m) goto mounted;
\r
502 case WhiteMarshall:
\r
503 case BlackMarshall:
\r
508 for (d = 0; d <= 1; d++)
\r
509 for (s = -1; s <= 1; s += 2)
\r
510 for (i = 1;; i++) {
\r
511 rt = rf + (i * s) * d;
\r
512 ft = ff + (i * s) * (1 - d);
\r
513 if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break;
\r
514 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
515 callback(board, flags, NormalMove,
\r
516 rf, ff, rt, ft, closure);
\r
517 if (board[rt][ft] != EmptySquare) break;
\r
519 if(m) goto mounted;
\r
524 for (rs = -1; rs <= 1; rs++)
\r
525 for (fs = -1; fs <= 1; fs++) {
\r
526 if (rs == 0 && fs == 0) continue;
\r
527 for (i = 1;; i++) {
\r
528 rt = rf + (i * rs);
\r
529 ft = ff + (i * fs);
\r
530 if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break;
\r
531 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
532 callback(board, flags, NormalMove,
\r
533 rf, ff, rt, ft, closure);
\r
534 if (board[rt][ft] != EmptySquare) break;
\r
540 case WhiteFairyPawn:
\r
541 case BlackFairyPawn:
\r
542 /* [HGM] support Shatranj pieces */
\r
543 for (rs = -1; rs <= 1; rs += 2)
\r
544 for (fs = -1; fs <= 1; fs += 2) {
\r
547 if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break;
\r
548 if (!SameColor(board[rf][ff], board[rt][ft]))
\r
549 callback(board, flags, NormalMove,
\r
550 rf, ff, rt, ft, closure);
\r
554 case WhiteFairyKing:
\r
555 case BlackFairyKing:
\r
559 for (i = -1; i <= 1; i++)
\r
560 for (j = -1; j <= 1; j++) {
\r
561 if (i == 0 && j == 0) continue;
\r
564 if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) continue;
\r
565 if (SameColor(board[rf][ff], board[rt][ft])) continue;
\r
566 callback(board, flags, NormalMove,
\r
567 rf, ff, rt, ft, closure);
\r
580 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
\r
581 int rf, int ff, int rt, int ft,
\r
582 VOIDSTAR closure));
\r
584 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
588 int rf, ff, rt, ft;
\r
591 register GenLegalClosure *cl = (GenLegalClosure *) closure;
\r
593 if (!(flags & F_IGNORE_CHECK) &&
\r
594 CheckTest(board, flags, rf, ff, rt, ft,
\r
595 kind == WhiteCapturesEnPassant ||
\r
596 kind == BlackCapturesEnPassant)) return;
\r
597 if (flags & F_ATOMIC_CAPTURE) {
\r
598 if (board[rt][ft] != EmptySquare ||
\r
599 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
\r
601 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
\r
602 if (board[rf][ff] == king) return;
\r
603 for (r = rt-1; r <= rt+1; r++) {
\r
604 for (f = ft-1; f <= ft+1; f++) {
\r
605 if (r >= 0 && r < BOARD_HEIGHT && f >= 0 && f < BOARD_WIDTH &&
\r
606 board[r][f] == king) return;
\r
611 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
\r
616 int rf, ff, rt, ft;
\r
618 } LegalityTestClosure;
\r
621 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
\r
622 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
\r
623 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
\r
624 moves that would destroy your own king. The CASTLE_OK flags are
\r
625 true if castling is not yet ruled out by a move of the king or
\r
626 rook. Return TRUE if the player on move is currently in check and
\r
627 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
\r
628 int GenLegal(board, flags, epfile, castlingRights, callback, closure)
\r
632 char castlingRights[];
\r
633 MoveCallback callback;
\r
636 GenLegalClosure cl;
\r
638 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
\r
642 GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
\r
644 if (!ignoreCheck &&
\r
645 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
\r
647 /* Generate castling moves */
\r
648 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
\r
649 if ((flags & F_WHITE_ON_MOVE) &&
\r
650 (flags & F_WHITE_KCASTLE_OK) &&
\r
651 board[0][ff] == WhiteKing &&
\r
652 board[0][ff + 1] == EmptySquare &&
\r
653 board[0][ff + 2] == EmptySquare &&
\r
654 board[0][BOARD_WIDTH-3] == EmptySquare &&
\r
655 board[0][BOARD_WIDTH-2] == EmptySquare &&
\r
656 board[0][BOARD_WIDTH-1] == WhiteRook &&
\r
657 castlingRights[0] >= 0 && /* [HGM] check rights */
\r
658 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
660 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
\r
661 !CheckTest(board, flags, 0, ff, 0, BOARD_WIDTH-3, FALSE) &&
\r
662 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
\r
664 callback(board, flags,
\r
665 ff==4 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
\r
666 0, ff, 0, ff + ((BOARD_WIDTH+2)>>2), closure);
\r
668 if ((flags & F_WHITE_ON_MOVE) &&
\r
669 (flags & F_WHITE_QCASTLE_OK) &&
\r
670 board[0][ff] == WhiteKing &&
\r
671 board[0][ff - 1] == EmptySquare &&
\r
672 board[0][ff - 2] == EmptySquare &&
\r
673 board[0][2] == EmptySquare &&
\r
674 board[0][1] == EmptySquare &&
\r
675 board[0][0] == WhiteRook &&
\r
676 castlingRights[1] >= 0 && /* [HGM] check rights */
\r
677 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
679 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
\r
680 !CheckTest(board, flags, 0, ff, 0, 3, FALSE) &&
\r
681 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
\r
683 callback(board, flags,
\r
684 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
\r
685 0, ff, 0, ff - ((BOARD_WIDTH+2)>>2), closure);
\r
687 if (!(flags & F_WHITE_ON_MOVE) &&
\r
688 (flags & F_BLACK_KCASTLE_OK) &&
\r
689 board[BOARD_HEIGHT-1][ff] == BlackKing &&
\r
690 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
\r
691 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
\r
692 board[BOARD_HEIGHT-1][BOARD_WIDTH-3] == EmptySquare &&
\r
693 board[BOARD_HEIGHT-1][BOARD_WIDTH-2] == EmptySquare &&
\r
694 board[BOARD_HEIGHT-1][BOARD_WIDTH-1] == BlackRook &&
\r
695 castlingRights[3] >= 0 && /* [HGM] check rights */
\r
696 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
698 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
\r
699 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_WIDTH-3, FALSE) &&
\r
700 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
\r
702 callback(board, flags,
\r
703 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
\r
704 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((BOARD_WIDTH+2)>>2), closure);
\r
706 if (!(flags & F_WHITE_ON_MOVE) &&
\r
707 (flags & F_BLACK_QCASTLE_OK) &&
\r
708 board[BOARD_HEIGHT-1][ff] == BlackKing &&
\r
709 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
\r
710 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
\r
711 board[BOARD_HEIGHT-1][2] == EmptySquare &&
\r
712 board[BOARD_HEIGHT-1][1] == EmptySquare &&
\r
713 board[BOARD_HEIGHT-1][0] == BlackRook &&
\r
714 castlingRights[4] >= 0 && /* [HGM] check rights */
\r
715 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
717 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
\r
718 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, 3, FALSE) &&
\r
719 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE)))) {
\r
721 callback(board, flags,
\r
722 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
\r
723 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((BOARD_WIDTH+2)>>2), closure);
\r
729 /* generate all potential FRC castling moves (KxR), ignoring flags */
\r
730 /* [HGM] Tord! Help requested! */
\r
732 if ((flags & F_WHITE_ON_MOVE) != 0) {
\r
734 for (ff = 1; ff < BOARD_WIDTH-1; ff++) {
\r
735 if (board[0][ff] == WhiteKing) {
\r
736 for (ft = 0; ft < BOARD_WIDTH; ft++) {
\r
737 if (board[0][ft] == WhiteRook) {
\r
738 callback(board, flags,
\r
739 (ft > ff) ? WhiteHSideCastleFR : WhiteASideCastleFR,
\r
740 0, ff, 0, ft, closure);
\r
748 for (ff = 1; ff < BOARD_WIDTH-1; ff++) {
\r
749 if (board[BOARD_HEIGHT-1][ff] == BlackKing) {
\r
750 for (ft = 0; ft < BOARD_WIDTH; ft++) {
\r
751 if (board[BOARD_HEIGHT-1][ft] == BlackRook) {
\r
752 callback(board, flags,
\r
753 (ft > ff) ? BlackHSideCastleFR : BlackASideCastleFR,
\r
754 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
770 } CheckTestClosure;
\r
773 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
\r
774 int rf, int ff, int rt, int ft,
\r
775 VOIDSTAR closure));
\r
778 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
782 int rf, ff, rt, ft;
\r
785 register CheckTestClosure *cl = (CheckTestClosure *) closure;
\r
787 if (rt == cl->rking && ft == cl->fking) cl->check++;
\r
791 /* If the player on move were to move from (rf, ff) to (rt, ft), would
\r
792 he leave himself in check? Or if rf == -1, is the player on move
\r
793 in check now? enPassant must be TRUE if the indicated move is an
\r
794 e.p. capture. The possibility of castling out of a check along the
\r
795 back rank is not accounted for (i.e., we still return nonzero), as
\r
796 this is illegal anyway. Return value is the number of times the
\r
797 king is in check. */
\r
798 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
\r
801 int rf, ff, rt, ft, enPassant;
\r
803 CheckTestClosure cl;
\r
804 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
\r
805 ChessSquare captured = EmptySquare;
\r
806 /* Suppress warnings on uninitialized variables */
\r
810 captured = board[rf][ft];
\r
811 board[rf][ft] = EmptySquare;
\r
813 captured = board[rt][ft];
\r
815 board[rt][ft] = board[rf][ff];
\r
816 board[rf][ff] = EmptySquare;
\r
819 /* For compatibility with ICS wild 9, we scan the board in the
\r
820 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
\r
821 and we test only whether that one is in check. */
\r
823 for (cl.fking = 0; cl.fking < BOARD_WIDTH; cl.fking++)
\r
824 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
\r
825 if (board[cl.rking][cl.fking] == king) {
\r
826 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
\r
827 CheckTestCallback, (VOIDSTAR) &cl);
\r
828 goto undo_move; /* 2-level break */
\r
835 board[rf][ff] = board[rt][ft];
\r
837 board[rf][ft] = captured;
\r
838 board[rt][ft] = EmptySquare;
\r
840 board[rt][ft] = captured;
\r
848 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
\r
849 int rf, int ff, int rt, int ft,
\r
850 VOIDSTAR closure));
\r
852 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
856 int rf, ff, rt, ft;
\r
859 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
\r
861 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
\r
865 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
\r
868 int rf, ff, rt, ft, promoChar;
\r
869 char castlingRights[];
\r
871 LegalityTestClosure cl;
\r
877 cl.kind = IllegalMove;
\r
878 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
\r
879 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
880 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
882 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
\r
884 cl.kind = IllegalMove;
\r
894 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
\r
895 int rf, int ff, int rt, int ft,
\r
896 VOIDSTAR closure));
\r
898 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
902 int rf, ff, rt, ft;
\r
905 register MateTestClosure *cl = (MateTestClosure *) closure;
\r
910 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
\r
911 int MateTest(board, flags, epfile, castlingRights)
\r
914 char castlingRights[];
\r
916 MateTestClosure cl;
\r
920 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
\r
921 if (cl.count > 0) {
\r
922 return inCheck ? MT_CHECK : MT_NONE;
\r
924 return inCheck ? MT_CHECKMATE : MT_STALEMATE;
\r
929 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
\r
930 int rf, int ff, int rt, int ft,
\r
931 VOIDSTAR closure));
\r
933 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
937 int rf, ff, rt, ft;
\r
940 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
\r
942 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]) &&
\r
943 (cl->rfIn == -1 || cl->rfIn == rf) &&
\r
944 (cl->ffIn == -1 || cl->ffIn == ff) &&
\r
945 (cl->rtIn == -1 || cl->rtIn == rt) &&
\r
946 (cl->ftIn == -1 || cl->ftIn == ft)) {
\r
949 cl->piece = board[rf][ff];
\r
958 void Disambiguate(board, flags, epfile, closure)
\r
961 DisambiguateClosure *closure;
\r
964 closure->count = 0;
\r
965 closure->rf = closure->ff = closure->rt = closure->ft = 0;
\r
966 closure->kind = ImpossibleMove;
\r
967 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
\r
968 if (closure->count == 0) {
\r
969 /* See if it's an illegal move due to check */
\r
971 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
\r
972 (VOIDSTAR) closure);
\r
973 if (closure->count == 0) {
\r
974 /* No, it's not even that */
\r
978 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
\r
979 if (closure->kind == WhitePromotionQueen
\r
980 || closure->kind == BlackPromotionQueen) {
\r
982 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
\r
983 closure->promoCharIn);
\r
985 closure->kind = IllegalMove;
\r
988 closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));
\r
989 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
\r
990 if (closure->count > 1) {
\r
991 closure->kind = AmbiguousMove;
\r
994 /* Note: If more than one illegal move matches, but no legal
\r
995 moves, we return IllegalMove, not AmbiguousMove. Caller
\r
996 can look at closure->count to detect this.
\r
998 closure->kind = IllegalMove;
\r
1005 ChessSquare piece;
\r
1006 int rf, ff, rt, ft;
\r
1012 } CoordsToAlgebraicClosure;
\r
1014 extern void CoordsToAlgebraicCallback P((Board board, int flags,
\r
1015 ChessMove kind, int rf, int ff,
\r
1016 int rt, int ft, VOIDSTAR closure));
\r
1018 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1022 int rf, ff, rt, ft;
\r
1025 register CoordsToAlgebraicClosure *cl =
\r
1026 (CoordsToAlgebraicClosure *) closure;
\r
1028 if (rt == cl->rt && ft == cl->ft &&
\r
1029 board[rf][ff] == cl->piece) {
\r
1030 if (rf == cl->rf) {
\r
1031 if (ff == cl->ff) {
\r
1032 cl->kind = kind; /* this is the move we want */
\r
1034 cl->file++; /* need file to rule out this move */
\r
1037 if (ff == cl->ff) {
\r
1038 cl->rank++; /* need rank to rule out this move */
\r
1040 cl->either++; /* rank or file will rule out this move */
\r
1046 /* Convert coordinates to normal algebraic notation.
\r
1047 promoChar must be NULLCHAR or 'x' if not a promotion.
\r
1049 ChessMove CoordsToAlgebraic(board, flags, epfile,
\r
1050 rf, ff, rt, ft, promoChar, out)
\r
1052 int flags, epfile;
\r
1053 int rf, ff, rt, ft;
\r
1055 char out[MOVE_LEN];
\r
1057 ChessSquare piece;
\r
1060 CoordsToAlgebraicClosure cl;
\r
1062 if (rf == DROP_RANK) {
\r
1063 /* Bughouse piece drop */
\r
1064 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
\r
1066 *outp++ = ft + 'a';
\r
1068 *outp++ = rt + ONE;
\r
1069 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1071 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
\r
1074 if (promoChar == 'x') promoChar = NULLCHAR;
\r
1075 piece = board[rf][ff];
\r
1079 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
\r
1080 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1081 /* Keep short notation if move is illegal only because it
\r
1082 leaves the player in check, but still return IllegalMove */
\r
1083 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1084 rf, ff, rt, ft, promoChar);
\r
1085 if (kind == IllegalMove) break;
\r
1086 kind = IllegalMove;
\r
1089 *outp++ = ff + 'a';
\r
1090 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
\r
1091 /* Non-capture; use style "e5" */
\r
1093 *outp++ = rt + ONE;
\r
1094 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1096 /* Capture; use style "exd5" */
\r
1097 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
\r
1098 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
\r
1099 *outp++ = ft + 'a';
\r
1101 *outp++ = rt + ONE;
\r
1102 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1104 /* Use promotion suffix style "=Q" */
\r
1105 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1107 *outp++ = ToUpper(promoChar);
\r
1115 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
\r
1116 /* Code added by Tord: FRC castling. */
\r
1117 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
\r
1118 (piece == BlackKing && board[rt][ft] == BlackRook)) {
\r
1119 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
\r
1120 return LegalityTest(board, flags, epfile, initialRights,
\r
1121 rf, ff, rt, ft, promoChar);
\r
1123 /* End of code added by Tord */
\r
1124 /* Test for castling or ICS wild castling */
\r
1125 /* Use style "O-O" (oh-oh) for PGN compatibility */
\r
1126 else if (rf == rt &&
\r
1127 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
\r
1128 ((ff == BOARD_WIDTH>>1 && (ft == 2 || ft == BOARD_WIDTH-2)) ||
\r
1129 (ff == (BOARD_WIDTH-1)>>1 && (ft == 1 || ft == BOARD_WIDTH-3)))) {
\r
1130 if(ft==1 || ft==BOARD_WIDTH-2)
\r
1131 strcpy(out, "O-O");
\r
1133 strcpy(out, "O-O-O");
\r
1135 /* This notation is always unambiguous, unless there are
\r
1136 kings on both the d and e files, with "wild castling"
\r
1137 possible for the king on the d file and normal castling
\r
1138 possible for the other. ICS rules for wild 9
\r
1139 effectively make castling illegal for either king in
\r
1140 this situation. So I am not going to worry about it;
\r
1141 I'll just generate an ambiguous O-O in this case.
\r
1143 return LegalityTest(board, flags, epfile, initialRights,
\r
1144 rf, ff, rt, ft, promoChar);
\r
1147 /* else fall through */
\r
1155 cl.kind = IllegalMove;
\r
1156 cl.rank = cl.file = cl.either = 0;
\r
1157 GenLegal(board, flags, epfile, initialRights,
\r
1158 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1160 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1161 /* Generate pretty moves for moving into check, but
\r
1162 still return IllegalMove.
\r
1164 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1165 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1166 if (cl.kind == IllegalMove) break;
\r
1167 cl.kind = IllegalMove;
\r
1170 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
\r
1171 else "Ngf3" or "Ngxf7",
\r
1172 else "N1f3" or "N5xf7",
\r
1173 else "Ng1f3" or "Ng5xf7".
\r
1175 *outp++ = ToUpper(PieceToChar(piece));
\r
1177 if (cl.file || (cl.either && !cl.rank)) {
\r
1178 *outp++ = ff + 'a';
\r
1182 *outp++ = rf + ONE;
\r
1183 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1186 if(board[rt][ft] != EmptySquare)
\r
1189 *outp++ = ft + 'a';
\r
1191 *outp++ = rt + ONE;
\r
1192 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1197 /* [HGM] Always long notation for fairies, don't know how they move */
\r
1198 case WhiteFairyRook:
\r
1199 case BlackFairyRook:
\r
1200 case WhiteFairyKnight:
\r
1201 case BlackFairyKnight:
\r
1202 case WhiteFairyQueen:
\r
1203 case BlackFairyQueen:
\r
1206 /* Moving a nonexistent piece */
\r
1210 /* Not a legal move, even ignoring check.
\r
1211 If there was a piece on the from square,
\r
1212 use style "Ng1g3" or "Ng1xe8";
\r
1213 if there was a pawn or nothing (!),
\r
1214 use style "g1g3" or "g1xe8". Use "x"
\r
1215 if a piece was on the to square, even
\r
1216 a piece of the same color.
\r
1219 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
\r
1220 *outp++ = ToUpper(PieceToChar(piece));
\r
1222 *outp++ = ff + 'a';
\r
1224 *outp++ = rf + ONE;
\r
1225 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1226 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
\r
1227 *outp++ = ft + 'a';
\r
1229 *outp++ = rt + ONE;
\r
1230 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1231 /* Use promotion suffix style "=Q" */
\r
1232 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1234 *outp++ = ToUpper(promoChar);
\r
1238 return IllegalMove;
\r