2 * moves.c - Move generation and checking
3 * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $
5 * Copyright 1991 by Digital Equipment Corporation, Maynard,
6 * Massachusetts. Enhancements Copyright
7 * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
57 #else /* not HAVE_STRING_H */
59 #endif /* not HAVE_STRING_H */
65 int WhitePiece P((ChessSquare));
66 int BlackPiece P((ChessSquare));
67 int SameColor P((ChessSquare, ChessSquare));
68 int PosFlags(int index);
70 extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */
76 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
82 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
85 int SameColor(piece1, piece2)
86 ChessSquare piece1, piece2;
88 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
89 (int) piece1 < (int) BlackPawn &&
90 (int) piece2 >= (int) WhitePawn &&
91 (int) piece2 < (int) BlackPawn)
92 || ((int) piece1 >= (int) BlackPawn &&
93 (int) piece1 < (int) EmptySquare &&
94 (int) piece2 >= (int) BlackPawn &&
95 (int) piece2 < (int) EmptySquare);
98 ChessSquare PromoPiece(moveType)
104 case WhitePromotionQueen:
106 case BlackPromotionQueen:
108 case WhitePromotionRook:
110 case BlackPromotionRook:
112 case WhitePromotionBishop:
114 case BlackPromotionBishop:
116 case WhitePromotionKnight:
118 case BlackPromotionKnight:
120 case WhitePromotionKing:
122 case BlackPromotionKing:
124 case WhitePromotionChancellor:
125 return WhiteMarshall;
126 case BlackPromotionChancellor:
127 return BlackMarshall;
128 case WhitePromotionArchbishop:
130 case BlackPromotionArchbishop:
132 case WhitePromotionCentaur:
134 case BlackPromotionCentaur:
139 char pieceToChar[] = {
140 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
141 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 's', 'U', 'K',
142 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
143 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
149 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
150 return pieceToChar[(int) p];
153 int PieceToNumber(p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
157 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
159 while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
163 ChessSquare CharToPiece(c)
167 for(i=0; i< (int) EmptySquare; i++)
168 if(pieceToChar[i] == c) return (ChessSquare) i;
172 ChessMove PromoCharToMoveType(whiteOnMove, promoChar)
175 { /* [HGM] made dependent on CharToPiece to alow alternate piece letters */
176 ChessSquare piece = CharToPiece(whiteOnMove ? ToUpper(promoChar) : ToLower(promoChar) );
179 if(promoChar == NULLCHAR) return NormalMove;
183 return WhitePromotionQueen;
185 return WhitePromotionRook;
187 return WhitePromotionBishop;
189 return WhitePromotionKnight;
191 return WhitePromotionKing;
193 return WhitePromotionArchbishop;
195 return WhitePromotionChancellor;
197 return WhitePromotionCentaur;
199 return BlackPromotionQueen;
201 return BlackPromotionRook;
203 return BlackPromotionBishop;
205 return BlackPromotionKnight;
207 return BlackPromotionKing;
209 return BlackPromotionArchbishop;
211 return BlackPromotionChancellor;
213 return BlackPromotionCentaur;
215 // not all promotion implemented yet! Take Queen for those we don't know.
216 return (whiteOnMove ? WhitePromotionQueen : BlackPromotionQueen);
220 void CopyBoard(to, from)
225 for (i = 0; i < BOARD_HEIGHT; i++)
226 for (j = 0; j < BOARD_WIDTH; j++)
227 to[i][j] = from[i][j];
230 int CompareBoards(board1, board2)
231 Board board1, board2;
235 for (i = 0; i < BOARD_HEIGHT; i++)
236 for (j = 0; j < BOARD_WIDTH; j++) {
237 if (board1[i][j] != board2[i][j])
244 /* Call callback once for each pseudo-legal move in the given
245 position, except castling moves. A move is pseudo-legal if it is
246 legal, or if it would be legal except that it leaves the king in
247 check. In the arguments, epfile is EP_NONE if the previous move
248 was not a double pawn push, or the file 0..7 if it was, or
249 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
250 Promotion moves generated are to Queen only.
252 void GenPseudoLegal(board, flags, epfile, callback, closure)
256 MoveCallback callback;
260 int i, j, d, s, fs, rs, rt, ft, m;
262 for (rf = 0; rf < BOARD_HEIGHT; rf++)
263 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
266 if (flags & F_WHITE_ON_MOVE) {
267 if (!WhitePiece(board[rf][ff])) continue;
269 if (!BlackPiece(board[rf][ff])) continue;
271 m = 0; piece = board[rf][ff];
272 if(PieceToChar(piece) == '~')
273 piece = (ChessSquare) ( DEMOTED piece );
274 if(gameInfo.variant == VariantShogi)
275 piece = (ChessSquare) ( SHOGI piece );
278 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
280 /* can't happen ([HGM] except for faries...) */
284 if(gameInfo.variant == VariantXiangqi) {
285 /* [HGM] capture and move straight ahead in Xiangqi */
286 if (rf < BOARD_HEIGHT-1 &&
287 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
288 callback(board, flags, NormalMove,
289 rf, ff, rf + 1, ff, closure);
291 /* and move sideways when across the river */
292 for (s = -1; s <= 1; s += 2) {
293 if (rf >= BOARD_HEIGHT>>1 &&
294 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
295 !WhitePiece(board[rf][ff+s]) ) {
296 callback(board, flags, NormalMove,
297 rf, ff, rf, ff+s, closure);
302 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
303 callback(board, flags,
304 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
305 rf, ff, rf + 1, ff, closure);
307 if (rf == 1 && board[2][ff] == EmptySquare &&
308 gameInfo.variant != VariantShatranj && /* [HGM] */
309 gameInfo.variant != VariantCourier && /* [HGM] */
310 board[3][ff] == EmptySquare ) {
311 callback(board, flags, NormalMove,
312 rf, ff, 3, ff, closure);
314 for (s = -1; s <= 1; s += 2) {
315 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
316 ((flags & F_KRIEGSPIEL_CAPTURE) ||
317 BlackPiece(board[rf + 1][ff + s]))) {
318 callback(board, flags,
319 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
320 rf, ff, rf + 1, ff + s, closure);
322 if (rf == BOARD_HEIGHT-4) {
323 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
324 (epfile == ff + s || epfile == EP_UNKNOWN) &&
325 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
326 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
327 callback(board, flags, WhiteCapturesEnPassant,
328 rf, ff, 5, ff + s, closure);
335 if(gameInfo.variant == VariantXiangqi) {
336 /* [HGM] capture straight ahead in Xiangqi */
337 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
338 callback(board, flags, NormalMove,
339 rf, ff, rf - 1, ff, closure);
341 /* and move sideways when across the river */
342 for (s = -1; s <= 1; s += 2) {
343 if (rf < BOARD_HEIGHT>>1 &&
344 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
345 !BlackPiece(board[rf][ff+s]) ) {
346 callback(board, flags, NormalMove,
347 rf, ff, rf, ff+s, closure);
352 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
353 callback(board, flags,
354 rf == 1 ? BlackPromotionQueen : NormalMove,
355 rf, ff, rf - 1, ff, closure);
357 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
358 gameInfo.variant != VariantShatranj && /* [HGM] */
359 gameInfo.variant != VariantCourier && /* [HGM] */
360 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
361 callback(board, flags, NormalMove,
362 rf, ff, BOARD_HEIGHT-4, ff, closure);
364 for (s = -1; s <= 1; s += 2) {
365 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
366 ((flags & F_KRIEGSPIEL_CAPTURE) ||
367 WhitePiece(board[rf - 1][ff + s]))) {
368 callback(board, flags,
369 rf == 1 ? BlackPromotionQueen : NormalMove,
370 rf, ff, rf - 1, ff + s, closure);
373 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
374 (epfile == ff + s || epfile == EP_UNKNOWN) &&
375 board[3][ff + s] == WhitePawn &&
376 board[2][ff + s] == EmptySquare) {
377 callback(board, flags, BlackCapturesEnPassant,
378 rf, ff, 2, ff + s, closure);
389 for (i = -1; i <= 1; i += 2)
390 for (j = -1; j <= 1; j += 2)
391 for (s = 1; s <= 2; s++) {
394 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
395 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
396 && !SameColor(board[rf][ff], board[rt][ft]))
397 callback(board, flags, NormalMove,
398 rf, ff, rt, ft, closure);
402 case SHOGI WhiteKnight:
403 for (s = -1; s <= 1; s += 2) {
404 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
405 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
406 callback(board, flags, NormalMove,
407 rf, ff, rf + 2, ff + s, closure);
412 case SHOGI BlackKnight:
413 for (s = -1; s <= 1; s += 2) {
414 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
415 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
416 callback(board, flags, NormalMove,
417 rf, ff, rf - 2, ff + s, closure);
424 for (d = 0; d <= 1; d++)
425 for (s = -1; s <= 1; s += 2) {
428 rt = rf + (i * s) * d;
429 ft = ff + (i * s) * (1 - d);
430 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
431 if (m == 0 && board[rt][ft] == EmptySquare)
432 callback(board, flags, NormalMove,
433 rf, ff, rt, ft, closure);
434 if (m == 1 && board[rt][ft] != EmptySquare &&
435 !SameColor(board[rf][ff], board[rt][ft]) )
436 callback(board, flags, NormalMove,
437 rf, ff, rt, ft, closure);
438 if (board[rt][ft] != EmptySquare && m++) break;
443 /* Gold General (and all its promoted versions) . First do the */
444 /* diagonal forward steps, then proceed as normal Wazir */
445 case SHOGI WhiteWazir:
446 case SHOGI (PROMOTED WhitePawn):
447 case SHOGI (PROMOTED WhiteKnight):
448 case SHOGI (PROMOTED WhiteQueen):
449 case SHOGI (PROMOTED WhiteFerz):
450 for (s = -1; s <= 1; s += 2) {
451 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
452 !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
453 callback(board, flags, NormalMove,
454 rf, ff, rf + 1, ff + s, closure);
459 case SHOGI BlackWazir:
460 case SHOGI (PROMOTED BlackPawn):
461 case SHOGI (PROMOTED BlackKnight):
462 case SHOGI (PROMOTED BlackQueen):
463 case SHOGI (PROMOTED BlackFerz):
464 for (s = -1; s <= 1; s += 2) {
465 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
466 !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
467 callback(board, flags, NormalMove,
468 rf, ff, rf - 1, ff + s, closure);
475 for (d = 0; d <= 1; d++)
476 for (s = -1; s <= 1; s += 2) {
478 ft = ff + s * (1 - d);
479 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
480 && !SameColor(board[rf][ff], board[rt][ft]) &&
481 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
482 callback(board, flags, NormalMove,
483 rf, ff, rt, ft, closure);
489 /* [HGM] support Shatranj pieces */
490 for (rs = -1; rs <= 1; rs += 2)
491 for (fs = -1; fs <= 1; fs += 2) {
494 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
495 && ( gameInfo.variant != VariantXiangqi ||
496 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
498 && !SameColor(board[rf][ff], board[rt][ft]))
499 callback(board, flags, NormalMove,
500 rf, ff, rt, ft, closure);
504 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
505 case SHOGI WhiteCardinal:
506 case SHOGI BlackCardinal:
509 /* Capablanca Archbishop continues as Knight */
514 /* Shogi Bishops are ordinary Bishops */
515 case SHOGI WhiteBishop:
516 case SHOGI BlackBishop:
519 for (rs = -1; rs <= 1; rs += 2)
520 for (fs = -1; fs <= 1; fs += 2)
524 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
525 if (SameColor(board[rf][ff], board[rt][ft])) break;
526 callback(board, flags, NormalMove,
527 rf, ff, rt, ft, closure);
528 if (board[rt][ft] != EmptySquare) break;
530 if(m==1) goto mounted;
531 if(m==2) goto finishGold;
532 /* Bishop falls through */
535 /* Shogi Lance is unlike anything, and asymmetric at that */
536 case SHOGI WhiteQueen:
540 if (rt >= BOARD_HEIGHT) break;
541 if (SameColor(board[rf][ff], board[rt][ft])) break;
542 callback(board, flags, NormalMove,
543 rf, ff, rt, ft, closure);
544 if (board[rt][ft] != EmptySquare) break;
548 case SHOGI BlackQueen:
553 if (SameColor(board[rf][ff], board[rt][ft])) break;
554 callback(board, flags, NormalMove,
555 rf, ff, rt, ft, closure);
556 if (board[rt][ft] != EmptySquare) break;
560 /* Shogi Dragon King has to continue as Ferz after Rook moves */
561 case SHOGI WhiteDragon:
562 case SHOGI BlackDragon:
565 /* Capablanca Chancellor sets flag to continue as Knight */
570 /* Shogi Rooks are ordinary Rooks */
571 case SHOGI WhiteRook:
572 case SHOGI BlackRook:
575 for (d = 0; d <= 1; d++)
576 for (s = -1; s <= 1; s += 2)
578 rt = rf + (i * s) * d;
579 ft = ff + (i * s) * (1 - d);
580 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
581 if (SameColor(board[rf][ff], board[rt][ft])) break;
582 callback(board, flags, NormalMove,
583 rf, ff, rt, ft, closure);
584 if (board[rt][ft] != EmptySquare) break;
586 if(m==1) goto mounted;
587 if(m==2) goto finishGold;
592 for (rs = -1; rs <= 1; rs++)
593 for (fs = -1; fs <= 1; fs++) {
594 if (rs == 0 && fs == 0) continue;
598 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
599 if (SameColor(board[rf][ff], board[rt][ft])) break;
600 callback(board, flags, NormalMove,
601 rf, ff, rt, ft, closure);
602 if (board[rt][ft] != EmptySquare) break;
607 /* Shogi Pawn and Silver General: first the Pawn move, */
608 /* then the General continues like a Ferz */
609 case SHOGI WhitePawn:
610 case SHOGI WhiteFerz:
611 if (rf < BOARD_HEIGHT-1 &&
612 !SameColor(board[rf][ff], board[rf + 1][ff]) )
613 callback(board, flags, NormalMove,
614 rf, ff, rf + 1, ff, closure);
615 if(piece != SHOGI WhitePawn) goto finishSilver;
618 case SHOGI BlackPawn:
619 case SHOGI BlackFerz:
621 !SameColor(board[rf][ff], board[rf - 1][ff]) )
622 callback(board, flags, NormalMove,
623 rf, ff, rf - 1, ff, closure);
624 if(piece == SHOGI BlackPawn) break;
629 /* [HGM] support Shatranj pieces */
630 for (rs = -1; rs <= 1; rs += 2)
631 for (fs = -1; fs <= 1; fs += 2) {
634 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
635 if (!SameColor(board[rf][ff], board[rt][ft]) &&
636 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
637 callback(board, flags, NormalMove,
638 rf, ff, rt, ft, closure);
644 m++; // [HGM] superchess: use for Centaur
647 case SHOGI WhiteKing:
648 case SHOGI BlackKing:
652 for (i = -1; i <= 1; i++)
653 for (j = -1; j <= 1; j++) {
654 if (i == 0 && j == 0) continue;
657 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
658 if (SameColor(board[rf][ff], board[rt][ft])) continue;
659 callback(board, flags, NormalMove,
660 rf, ff, rt, ft, closure);
662 if(m==1) goto mounted;
665 case WhiteNightrider:
666 case BlackNightrider:
667 for (i = -1; i <= 1; i += 2)
668 for (j = -1; j <= 1; j += 2)
669 for (s = 1; s <= 2; s++) { int k;
673 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
674 if (SameColor(board[rf][ff], board[rt][ft])) break;
675 callback(board, flags, NormalMove,
676 rf, ff, rt, ft, closure);
677 if (board[rt][ft] != EmptySquare) break;
692 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
693 int rf, int ff, int rt, int ft,
696 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
703 register GenLegalClosure *cl = (GenLegalClosure *) closure;
705 if (!(flags & F_IGNORE_CHECK) &&
706 CheckTest(board, flags, rf, ff, rt, ft,
707 kind == WhiteCapturesEnPassant ||
708 kind == BlackCapturesEnPassant)) return;
709 if (flags & F_ATOMIC_CAPTURE) {
710 if (board[rt][ft] != EmptySquare ||
711 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
713 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
714 if (board[rf][ff] == king) return;
715 for (r = rt-1; r <= rt+1; r++) {
716 for (f = ft-1; f <= ft+1; f++) {
717 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
718 board[r][f] == king) return;
723 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
730 } LegalityTestClosure;
733 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
734 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
735 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
736 moves that would destroy your own king. The CASTLE_OK flags are
737 true if castling is not yet ruled out by a move of the king or
738 rook. Return TRUE if the player on move is currently in check and
739 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
740 int GenLegal(board, flags, epfile, castlingRights, callback, closure)
744 char castlingRights[];
745 MoveCallback callback;
749 int ff, ft, k, left, right;
750 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
751 ChessSquare wKing = WhiteKing, bKing = BlackKing;
755 GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
758 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
760 /* Generate castling moves */
761 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
762 wKing = WhiteUnicorn; bKing = BlackUnicorn;
765 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
766 if ((flags & F_WHITE_ON_MOVE) &&
767 (flags & F_WHITE_KCASTLE_OK) &&
768 board[0][ff] == wKing &&
769 board[0][ff + 1] == EmptySquare &&
770 board[0][ff + 2] == EmptySquare &&
771 board[0][BOARD_RGHT-3] == EmptySquare &&
772 board[0][BOARD_RGHT-2] == EmptySquare &&
773 board[0][BOARD_RGHT-1] == WhiteRook &&
774 castlingRights[0] >= 0 && /* [HGM] check rights */
775 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
777 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
778 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
779 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
780 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
782 callback(board, flags,
783 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
784 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
786 if ((flags & F_WHITE_ON_MOVE) &&
787 (flags & F_WHITE_QCASTLE_OK) &&
788 board[0][ff] == wKing &&
789 board[0][ff - 1] == EmptySquare &&
790 board[0][ff - 2] == EmptySquare &&
791 board[0][BOARD_LEFT+2] == EmptySquare &&
792 board[0][BOARD_LEFT+1] == EmptySquare &&
793 board[0][BOARD_LEFT+0] == WhiteRook &&
794 castlingRights[1] >= 0 && /* [HGM] check rights */
795 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
797 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
798 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
799 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
801 callback(board, flags,
802 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
803 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
805 if (!(flags & F_WHITE_ON_MOVE) &&
806 (flags & F_BLACK_KCASTLE_OK) &&
807 board[BOARD_HEIGHT-1][ff] == bKing &&
808 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
809 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
810 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
811 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
812 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
813 castlingRights[3] >= 0 && /* [HGM] check rights */
814 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
816 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
817 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
818 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
819 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
821 callback(board, flags,
822 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
823 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
825 if (!(flags & F_WHITE_ON_MOVE) &&
826 (flags & F_BLACK_QCASTLE_OK) &&
827 board[BOARD_HEIGHT-1][ff] == bKing &&
828 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
829 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
830 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
831 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
832 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
833 castlingRights[4] >= 0 && /* [HGM] check rights */
834 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
836 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
837 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
838 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
840 callback(board, flags,
841 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
842 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
846 if(gameInfo.variant == VariantFischeRandom) {
848 /* generate all potential FRC castling moves (KxR), ignoring flags */
849 /* [HGM] test if the Rooks we find have castling rights */
852 if ((flags & F_WHITE_ON_MOVE) != 0) {
853 ff = castlingRights[2]; /* King file if we have any rights */
854 if(ff > 0 && board[0][ff] == WhiteKing) {
855 if (appData.debugMode) {
856 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
857 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
859 ft = castlingRights[0]; /* Rook file if we have H-side rights */
861 right = BOARD_RGHT-2;
862 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
863 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
864 if(k != ft && board[0][k] != EmptySquare) ft = -1;
865 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
866 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
867 if(ft >= 0 && board[0][ft] == WhiteRook)
868 callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
870 ft = castlingRights[1]; /* Rook file if we have A-side rights */
873 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
874 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
875 if(k != ft && board[0][k] != EmptySquare) ft = -1;
876 if(ff > BOARD_LEFT+2)
877 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
878 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
879 if(ft >= 0 && board[0][ft] == WhiteRook)
880 callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
883 ff = castlingRights[5]; /* King file if we have any rights */
884 if(ff > 0 && board[BOARD_HEIGHT-1][ff] == BlackKing) {
885 ft = castlingRights[3]; /* Rook file if we have H-side rights */
887 right = BOARD_RGHT-2;
888 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
889 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
890 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
891 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
892 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
893 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
894 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
896 ft = castlingRights[4]; /* Rook file if we have A-side rights */
899 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
900 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
901 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
902 if(ff > BOARD_LEFT+2)
903 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
904 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
905 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
906 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
922 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
923 int rf, int ff, int rt, int ft,
927 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
934 register CheckTestClosure *cl = (CheckTestClosure *) closure;
936 if (rt == cl->rking && ft == cl->fking) cl->check++;
940 /* If the player on move were to move from (rf, ff) to (rt, ft), would
941 he leave himself in check? Or if rf == -1, is the player on move
942 in check now? enPassant must be TRUE if the indicated move is an
943 e.p. capture. The possibility of castling out of a check along the
944 back rank is not accounted for (i.e., we still return nonzero), as
945 this is illegal anyway. Return value is the number of times the
947 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
950 int rf, ff, rt, ft, enPassant;
953 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
954 ChessSquare captured = EmptySquare;
955 /* Suppress warnings on uninitialized variables */
957 if(gameInfo.variant == VariantXiangqi)
958 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
959 if(gameInfo.variant == VariantKnightmate)
960 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
964 captured = board[rf][ft];
965 board[rf][ft] = EmptySquare;
967 captured = board[rt][ft];
969 board[rt][ft] = board[rf][ff];
970 board[rf][ff] = EmptySquare;
973 /* For compatibility with ICS wild 9, we scan the board in the
974 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
975 and we test only whether that one is in check. */
977 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
978 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
979 if (board[cl.rking][cl.fking] == king) {
980 if(gameInfo.variant == VariantXiangqi) {
981 /* [HGM] In Xiangqi opposing Kings means check as well */
983 dir = (king >= BlackPawn) ? -1 : 1;
984 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
985 board[i][cl.fking] == EmptySquare; i+=dir );
986 if(i>=0 && i<BOARD_HEIGHT &&
987 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
991 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
992 CheckTestCallback, (VOIDSTAR) &cl);
993 goto undo_move; /* 2-level break */
1000 board[rf][ff] = board[rt][ft];
1002 board[rf][ft] = captured;
1003 board[rt][ft] = EmptySquare;
1005 board[rt][ft] = captured;
1013 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1014 int rf, int ff, int rt, int ft,
1017 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1024 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1026 // if (appData.debugMode) {
1027 // fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1029 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1033 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
1036 int rf, ff, rt, ft, promoChar;
1037 char castlingRights[];
1039 LegalityTestClosure cl; ChessSquare piece = board[rf][ff];
1041 if (appData.debugMode) {
1043 for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
1044 fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1046 /* [HGM] Lance, Cobra and Falcon are wildcard pieces; consider all their moves legal */
1047 /* (perhaps we should disallow moves that obviously leave us in check?) */
1048 if(piece == WhiteFalcon || piece == BlackFalcon ||
1049 piece == WhiteCobra || piece == BlackCobra ||
1050 piece == WhiteLance || piece == BlackLance)
1057 cl.kind = IllegalMove;
1058 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
1060 if(gameInfo.variant == VariantShogi) {
1061 /* [HGM] Shogi promotions. '=' means defer */
1062 if(rf != DROP_RANK && cl.kind == NormalMove) {
1063 ChessSquare piece = board[rf][ff];
1065 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1066 if(promoChar != NULLCHAR && promoChar != 'x' &&
1067 promoChar != '+' && promoChar != '=' &&
1068 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
1069 cl.kind = IllegalMove;
1070 else if(flags & F_WHITE_ON_MOVE) {
1071 if( (int) piece < (int) WhiteWazir &&
1072 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
1073 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1074 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1075 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
1076 else /* promotion optional, default is promote */
1077 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
1079 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1080 NormalMove : IllegalMove;
1082 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
1083 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1084 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1085 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
1086 else /* promotion optional, default is promote */
1087 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
1089 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1090 NormalMove : IllegalMove;
1094 if (promoChar != NULLCHAR && promoChar != 'x') {
1095 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1096 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
1098 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
1100 cl.kind = IllegalMove;
1103 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
1111 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1112 int rf, int ff, int rt, int ft,
1115 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1122 register MateTestClosure *cl = (MateTestClosure *) closure;
1127 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1128 int MateTest(board, flags, epfile, castlingRights)
1131 char castlingRights[];
1137 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
1139 return inCheck ? MT_CHECK : MT_NONE;
1141 return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ?
1142 MT_CHECKMATE : MT_STALEMATE;
1147 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1148 int rf, int ff, int rt, int ft,
1151 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
1158 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1160 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1161 || PieceToChar(board[rf][ff]) == '~'
1162 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1164 (cl->rfIn == -1 || cl->rfIn == rf) &&
1165 (cl->ffIn == -1 || cl->ffIn == ff) &&
1166 (cl->rtIn == -1 || cl->rtIn == rt) &&
1167 (cl->ftIn == -1 || cl->ftIn == ft)) {
1170 cl->piece = board[rf][ff];
1179 void Disambiguate(board, flags, epfile, closure)
1182 DisambiguateClosure *closure;
1184 int illegal = 0; char c = closure->promoCharIn;
1186 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1187 closure->kind = ImpossibleMove;
1188 if (appData.debugMode) {
1189 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1190 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1191 closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
1193 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
1194 if (closure->count == 0) {
1195 /* See if it's an illegal move due to check */
1197 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
1198 (VOIDSTAR) closure);
1199 if (closure->count == 0) {
1200 /* No, it's not even that */
1201 if (appData.debugMode) { int i, j;
1202 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1203 for(j=0; j<BOARD_WIDTH; j++)
1204 fprintf(debugFP, "%3d", (int) board[i][j]);
1205 fprintf(debugFP, "\n");
1212 if(gameInfo.variant == VariantShogi) {
1213 /* [HGM] Shogi promotions. '=' means defer */
1214 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1215 ChessSquare piece = closure->piece;
1217 if (appData.debugMode) {
1218 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1219 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1220 closure->promoCharIn,closure->promoCharIn);
1223 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
1224 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
1225 closure->kind = IllegalMove;
1226 else if(flags & F_WHITE_ON_MOVE) {
1228 if (appData.debugMode) {
1229 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1230 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1231 closure->promoCharIn,closure->promoCharIn);
1234 if( (int) piece < (int) WhiteWazir &&
1235 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
1236 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1237 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1238 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
1239 else /* promotion optional, default is promote */
1240 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
1242 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
1243 NormalMove : IllegalMove;
1245 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
1246 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1247 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1248 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
1249 else /* promotion optional, default is promote */
1250 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
1252 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
1253 NormalMove : IllegalMove;
1257 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
1258 if (closure->kind == WhitePromotionQueen
1259 || closure->kind == BlackPromotionQueen) {
1261 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
1262 closure->promoCharIn);
1264 closure->kind = IllegalMove;
1268 if (appData.debugMode) {
1269 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1270 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1271 closure->promoCharIn,closure->promoCharIn);
1274 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
1275 if(closure->promoCharIn != '=')
1276 closure->promoChar = ToLower(closure->promoCharIn);
1277 else closure->promoChar = '=';
1278 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
1279 if (closure->count > 1) {
1280 closure->kind = AmbiguousMove;
1283 /* Note: If more than one illegal move matches, but no legal
1284 moves, we return IllegalMove, not AmbiguousMove. Caller
1285 can look at closure->count to detect this.
1287 closure->kind = IllegalMove;
1289 if(closure->kind == IllegalMove)
1290 /* [HGM] might be a variant we don't understand, pass on promotion info */
1291 closure->promoChar = ToLower(closure->promoCharIn);
1292 if (appData.debugMode) {
1293 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1294 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
1295 closure->promoChar >= ' ' ? closure->promoChar:'-');
1309 } CoordsToAlgebraicClosure;
1311 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1312 ChessMove kind, int rf, int ff,
1313 int rt, int ft, VOIDSTAR closure));
1315 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
1322 register CoordsToAlgebraicClosure *cl =
1323 (CoordsToAlgebraicClosure *) closure;
1325 if (rt == cl->rt && ft == cl->ft &&
1326 (board[rf][ff] == cl->piece
1327 || PieceToChar(board[rf][ff]) == '~' &&
1328 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1332 cl->kind = kind; /* this is the move we want */
1334 cl->file++; /* need file to rule out this move */
1338 cl->rank++; /* need rank to rule out this move */
1340 cl->either++; /* rank or file will rule out this move */
1346 /* Convert coordinates to normal algebraic notation.
1347 promoChar must be NULLCHAR or 'x' if not a promotion.
1349 ChessMove CoordsToAlgebraic(board, flags, epfile,
1350 rf, ff, rt, ft, promoChar, out)
1359 char *outp = out, c;
1360 CoordsToAlgebraicClosure cl;
1362 if (rf == DROP_RANK) {
1363 /* Bughouse piece drop */
1364 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1369 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1371 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1374 if (promoChar == 'x') promoChar = NULLCHAR;
1375 piece = board[rf][ff];
1376 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1378 if (appData.debugMode)
1379 fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
1383 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
1384 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1385 /* Keep short notation if move is illegal only because it
1386 leaves the player in check, but still return IllegalMove */
1387 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
1388 rf, ff, rt, ft, promoChar);
1389 if (kind == IllegalMove) break;
1394 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1395 /* Non-capture; use style "e5" */
1398 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1400 /* Capture; use style "exd5" */
1401 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1402 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
1406 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1408 /* Use promotion suffix style "=Q" */
1410 if (appData.debugMode)
1411 fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
1412 if (promoChar != NULLCHAR) {
1413 if(gameInfo.variant == VariantShogi) {
1414 /* [HGM] ... but not in Shogi! */
1415 *outp++ = promoChar == '=' ? '=' : '+';
1418 *outp++ = ToUpper(promoChar);
1427 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1428 /* Code added by Tord: FRC castling. */
1429 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1430 (piece == BlackKing && board[rt][ft] == BlackRook)) {
1431 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
1432 return LegalityTest(board, flags, epfile, initialRights,
1433 rf, ff, rt, ft, promoChar);
1435 /* End of code added by Tord */
1436 /* Test for castling or ICS wild castling */
1437 /* Use style "O-O" (oh-oh) for PGN compatibility */
1438 else if (rf == rt &&
1439 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1440 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1441 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1442 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1445 strcpy(out, "O-O-O");
1447 /* This notation is always unambiguous, unless there are
1448 kings on both the d and e files, with "wild castling"
1449 possible for the king on the d file and normal castling
1450 possible for the other. ICS rules for wild 9
1451 effectively make castling illegal for either king in
1452 this situation. So I am not going to worry about it;
1453 I'll just generate an ambiguous O-O in this case.
1455 return LegalityTest(board, flags, epfile, initialRights,
1456 rf, ff, rt, ft, promoChar);
1459 /* else fall through */
1467 cl.kind = IllegalMove;
1468 cl.rank = cl.file = cl.either = 0;
1469 GenLegal(board, flags, epfile, initialRights,
1470 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1472 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1473 /* Generate pretty moves for moving into check, but
1474 still return IllegalMove.
1476 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
1477 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1478 if (cl.kind == IllegalMove) break;
1479 cl.kind = IllegalMove;
1482 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1483 else "Ngf3" or "Ngxf7",
1484 else "N1f3" or "N5xf7",
1485 else "Ng1f3" or "Ng5xf7".
1487 c = PieceToChar(piece) ;
1488 if( c == '~' || c == '+') {
1489 /* [HGM] print nonexistent piece as its demoted version */
1490 piece = (ChessSquare) (DEMOTED piece);
1492 if(c=='+') *outp++ = c;
1493 *outp++ = ToUpper(PieceToChar(piece));
1495 if (cl.file || (cl.either && !cl.rank)) {
1501 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1504 if(board[rt][ft] != EmptySquare)
1510 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1512 if (gameInfo.variant == VariantShogi) {
1513 /* [HGM] in Shogi non-pawns can promote */
1514 if(flags & F_WHITE_ON_MOVE) {
1515 if( (int) cl.piece < (int) WhiteWazir &&
1516 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
1517 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1518 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1519 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
1520 else cl.kind = WhitePromotionQueen; /* promotion optional */
1522 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1523 NormalMove : IllegalMove;
1525 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
1526 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1527 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1528 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
1529 else cl.kind = BlackPromotionQueen; /* promotion optional */
1530 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1531 NormalMove : IllegalMove;
1533 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
1534 /* for optional promotions append '+' or '=' */
1535 if(promoChar == '=') {
1537 cl.kind = NormalMove;
1538 } else *outp++ = '+';
1540 } else if(cl.kind == IllegalMove) {
1541 /* Illegal move specifies any given promotion */
1542 if(promoChar != NULLCHAR && promoChar != 'x') {
1544 *outp++ = ToUpper(promoChar);
1551 /* [HGM] Always long notation for fairies we don't know */
1556 case WhiteGrasshopper:
1557 case BlackGrasshopper:
1559 /* Moving a nonexistent piece */
1563 /* Not a legal move, even ignoring check.
1564 If there was a piece on the from square,
1565 use style "Ng1g3" or "Ng1xe8";
1566 if there was a pawn or nothing (!),
1567 use style "g1g3" or "g1xe8". Use "x"
1568 if a piece was on the to square, even
1569 a piece of the same color.
1572 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1573 *outp++ = ToUpper(PieceToChar(piece));
1578 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1579 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1583 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1584 /* Use promotion suffix style "=Q" */
1585 if (promoChar != NULLCHAR && promoChar != 'x') {
1587 *outp++ = ToUpper(promoChar);
1594 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1603 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1605 int preyStackPointer, chaseStackPointer;
1608 char rf, ff, rt, ft;
1618 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1620 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1621 int rf, int ff, int rt, int ft,
1624 void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1630 { // For adding captures that can lead to chase indictment to the chaseStack
1631 if(board[rt][ft] == EmptySquare) return; // non-capture
1632 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
1633 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
1634 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
1635 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1636 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1637 chaseStack[chaseStackPointer].rf = rf;
1638 chaseStack[chaseStackPointer].ff = ff;
1639 chaseStack[chaseStackPointer].rt = rt;
1640 chaseStack[chaseStackPointer].ft = ft;
1641 chaseStackPointer++;
1644 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1645 int rf, int ff, int rt, int ft,
1648 void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1654 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1656 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1658 if(board[rt][ft] == EmptySquare) return; // no capture
1659 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1660 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
1662 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1663 for(i=0; i<chaseStackPointer; i++) {
1664 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1665 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
1666 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1667 chaseStack[i] = chaseStack[--chaseStackPointer];
1673 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1674 int rf, int ff, int rt, int ft,
1677 void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
1683 { // for determining if a piece (given through the closure) is protected
1684 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1686 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
1687 if(appData.debugMode && board[rt][ft] != EmptySquare)
1688 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1691 extern char moveList[MAX_MOVES][MOVE_LEN];
1693 int PerpetualChase(int first, int last)
1694 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1697 ChessSquare captured;
1699 preyStackPointer = 0; // clear stack of chased pieces
1700 for(i=first; i<last; i+=2) { // for all positions with same side to move
1701 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1702 chaseStackPointer = 0; // clear stack that is going to hold possible chases
1703 // determine all captures possible after the move, and put them on chaseStack
1704 GenLegal(boards[i+1], PosFlags(i), EP_NONE, initialRights, AttacksCallback, &cl);
1705 if(appData.debugMode) { int n;
1706 for(n=0; n<chaseStackPointer; n++)
1707 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1708 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1709 fprintf(debugFP, ": all capts\n");
1711 // determine all captures possible before the move, and delete them from chaseStack
1712 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1713 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1714 cl.rt = moveList[i][3]-ONE;
1715 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1716 GenLegal(boards[i], PosFlags(i), EP_NONE, initialRights, ExistingAttacksCallback, &cl);
1717 if(appData.debugMode) { int n;
1718 for(n=0; n<chaseStackPointer; n++)
1719 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1720 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1721 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1723 // chaseSack now contains all captures made possible by the move
1724 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1725 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1726 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1728 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1729 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
1731 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1732 continue; // C or H attack on R is always chase; leave on chaseStack
1734 if(attacker == victim) {
1735 if(LegalityTest(boards[i+1], PosFlags(i+1), EP_NONE, initialRights, chaseStack[j].rt,
1736 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1737 // we can capture back with equal piece, so this is no chase but a sacrifice
1738 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1739 j--; /* ! */ continue;
1744 // the attack is on a lower piece, or on a pinned or blocked equal one
1745 // test if the victim is protected by a true protector. First make the capture.
1746 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1747 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1748 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1749 // Then test if the opponent can recapture
1750 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
1751 cl.rt = chaseStack[j].rt;
1752 cl.ft = chaseStack[j].ft;
1753 if(appData.debugMode) {
1754 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1756 GenLegal(boards[i+1], PosFlags(i+1), EP_NONE, initialRights, ProtectedCallback, &cl); // try all moves
1757 // unmake the capture
1758 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1759 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1760 // if a recapture was found, piece is protected, and we are not chasing it.
1761 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1762 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1766 // chaseStack now contains all moves that chased
1767 if(appData.debugMode) { int n;
1768 for(n=0; n<chaseStackPointer; n++)
1769 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1770 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1771 fprintf(debugFP, ": chases\n");
1773 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1774 for(j=0; j<chaseStackPointer; j++) {
1775 preyStack[j].rank = chaseStack[j].rt;
1776 preyStack[j].file = chaseStack[j].ft;
1778 preyStackPointer = chaseStackPointer;
1781 for(j=0; j<chaseStackPointer; j++) {
1782 for(k=0; k<preyStackPointer; k++) {
1783 // search the victim of each chase move on the preyStack (first occurrence)
1784 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1785 if(k < tail) break; // piece was already identified as still being chased
1786 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1787 preyStack[tail] = preyStack[k]; // by swapping
1788 preyStack[k] = preyStack[preyStackPointer];
1794 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1795 if(appData.debugMode) { int n;
1796 for(n=0; n<preyStackPointer; n++)
1797 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1798 fprintf(debugFP, "always chased upto ply %d\n", i);
1800 // now adjust the location of the chased pieces according to opponent move
1801 for(j=0; j<preyStackPointer; j++) {
1802 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1803 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1804 preyStack[j].rank = moveList[i+1][3]-ONE;
1805 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1810 return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased