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 int captures; // [HGM] losers
731 } LegalityTestClosure;
734 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
735 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
736 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
737 moves that would destroy your own king. The CASTLE_OK flags are
738 true if castling is not yet ruled out by a move of the king or
739 rook. Return TRUE if the player on move is currently in check and
740 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
741 int GenLegal(board, flags, epfile, castlingRights, callback, closure)
745 char castlingRights[];
746 MoveCallback callback;
750 int ff, ft, k, left, right;
751 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
752 ChessSquare wKing = WhiteKing, bKing = BlackKing;
756 GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
759 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
761 /* Generate castling moves */
762 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
763 wKing = WhiteUnicorn; bKing = BlackUnicorn;
766 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
767 if ((flags & F_WHITE_ON_MOVE) &&
768 (flags & F_WHITE_KCASTLE_OK) &&
769 board[0][ff] == wKing &&
770 board[0][ff + 1] == EmptySquare &&
771 board[0][ff + 2] == EmptySquare &&
772 board[0][BOARD_RGHT-3] == EmptySquare &&
773 board[0][BOARD_RGHT-2] == EmptySquare &&
774 board[0][BOARD_RGHT-1] == WhiteRook &&
775 castlingRights[0] >= 0 && /* [HGM] check rights */
776 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
778 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
779 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
780 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
781 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
783 callback(board, flags,
784 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
785 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
787 if ((flags & F_WHITE_ON_MOVE) &&
788 (flags & F_WHITE_QCASTLE_OK) &&
789 board[0][ff] == wKing &&
790 board[0][ff - 1] == EmptySquare &&
791 board[0][ff - 2] == EmptySquare &&
792 board[0][BOARD_LEFT+2] == EmptySquare &&
793 board[0][BOARD_LEFT+1] == EmptySquare &&
794 board[0][BOARD_LEFT+0] == WhiteRook &&
795 castlingRights[1] >= 0 && /* [HGM] check rights */
796 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
798 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
799 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
800 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
802 callback(board, flags,
803 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
804 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
806 if (!(flags & F_WHITE_ON_MOVE) &&
807 (flags & F_BLACK_KCASTLE_OK) &&
808 board[BOARD_HEIGHT-1][ff] == bKing &&
809 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
810 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
811 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
812 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
813 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
814 castlingRights[3] >= 0 && /* [HGM] check rights */
815 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
817 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
818 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
819 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
820 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
822 callback(board, flags,
823 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
824 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
826 if (!(flags & F_WHITE_ON_MOVE) &&
827 (flags & F_BLACK_QCASTLE_OK) &&
828 board[BOARD_HEIGHT-1][ff] == bKing &&
829 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
830 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
831 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
832 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
833 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
834 castlingRights[4] >= 0 && /* [HGM] check rights */
835 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
837 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
838 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
839 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
841 callback(board, flags,
842 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
843 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
847 if(gameInfo.variant == VariantFischeRandom) {
849 /* generate all potential FRC castling moves (KxR), ignoring flags */
850 /* [HGM] test if the Rooks we find have castling rights */
853 if ((flags & F_WHITE_ON_MOVE) != 0) {
854 ff = castlingRights[2]; /* King file if we have any rights */
855 if(ff > 0 && board[0][ff] == WhiteKing) {
856 if (appData.debugMode) {
857 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
858 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
860 ft = castlingRights[0]; /* Rook file if we have H-side rights */
862 right = BOARD_RGHT-2;
863 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
864 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
865 if(k != ft && board[0][k] != EmptySquare) ft = -1;
866 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
867 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
868 if(ft >= 0 && board[0][ft] == WhiteRook)
869 callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
871 ft = castlingRights[1]; /* Rook file if we have A-side rights */
874 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
875 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
876 if(k != ft && board[0][k] != EmptySquare) ft = -1;
877 if(ff > BOARD_LEFT+2)
878 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
879 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
880 if(ft >= 0 && board[0][ft] == WhiteRook)
881 callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
884 ff = castlingRights[5]; /* King file if we have any rights */
885 if(ff > 0 && board[BOARD_HEIGHT-1][ff] == BlackKing) {
886 ft = castlingRights[3]; /* Rook file if we have H-side rights */
888 right = BOARD_RGHT-2;
889 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
890 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
891 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
892 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
893 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
894 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
895 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
897 ft = castlingRights[4]; /* Rook file if we have A-side rights */
900 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
901 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
902 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
903 if(ff > BOARD_LEFT+2)
904 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
905 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
906 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
907 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
923 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
924 int rf, int ff, int rt, int ft,
928 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
935 register CheckTestClosure *cl = (CheckTestClosure *) closure;
937 if (rt == cl->rking && ft == cl->fking) cl->check++;
941 /* If the player on move were to move from (rf, ff) to (rt, ft), would
942 he leave himself in check? Or if rf == -1, is the player on move
943 in check now? enPassant must be TRUE if the indicated move is an
944 e.p. capture. The possibility of castling out of a check along the
945 back rank is not accounted for (i.e., we still return nonzero), as
946 this is illegal anyway. Return value is the number of times the
948 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
951 int rf, ff, rt, ft, enPassant;
954 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
955 ChessSquare captured = EmptySquare;
956 /* Suppress warnings on uninitialized variables */
958 if(gameInfo.variant == VariantXiangqi)
959 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
960 if(gameInfo.variant == VariantKnightmate)
961 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
965 captured = board[rf][ft];
966 board[rf][ft] = EmptySquare;
968 captured = board[rt][ft];
970 board[rt][ft] = board[rf][ff];
971 board[rf][ff] = EmptySquare;
974 /* For compatibility with ICS wild 9, we scan the board in the
975 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
976 and we test only whether that one is in check. */
978 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
979 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
980 if (board[cl.rking][cl.fking] == king) {
981 if(gameInfo.variant == VariantXiangqi) {
982 /* [HGM] In Xiangqi opposing Kings means check as well */
984 dir = (king >= BlackPawn) ? -1 : 1;
985 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
986 board[i][cl.fking] == EmptySquare; i+=dir );
987 if(i>=0 && i<BOARD_HEIGHT &&
988 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
992 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
993 CheckTestCallback, (VOIDSTAR) &cl);
994 goto undo_move; /* 2-level break */
1001 board[rf][ff] = board[rt][ft];
1003 board[rf][ft] = captured;
1004 board[rt][ft] = EmptySquare;
1006 board[rt][ft] = captured;
1014 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1015 int rf, int ff, int rt, int ft,
1018 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1025 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1027 // if (appData.debugMode) {
1028 // fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1030 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1031 cl->captures++; // [HGM] losers: count legal captures
1032 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1036 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
1039 int rf, ff, rt, ft, promoChar;
1040 char castlingRights[];
1042 LegalityTestClosure cl; ChessSquare piece = board[rf][ff];
1044 if (appData.debugMode) {
1046 for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
1047 fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1049 /* [HGM] Lance, Cobra and Falcon are wildcard pieces; consider all their moves legal */
1050 /* (perhaps we should disallow moves that obviously leave us in check?) */
1051 if(piece == WhiteFalcon || piece == BlackFalcon ||
1052 piece == WhiteCobra || piece == BlackCobra ||
1053 piece == WhiteLance || piece == BlackLance)
1060 cl.kind = IllegalMove;
1061 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1062 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
1063 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1064 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1065 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1067 if(gameInfo.variant == VariantShogi) {
1068 /* [HGM] Shogi promotions. '=' means defer */
1069 if(rf != DROP_RANK && cl.kind == NormalMove) {
1070 ChessSquare piece = board[rf][ff];
1072 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1073 if(promoChar != NULLCHAR && promoChar != 'x' &&
1074 promoChar != '+' && promoChar != '=' &&
1075 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
1076 cl.kind = IllegalMove;
1077 else if(flags & F_WHITE_ON_MOVE) {
1078 if( (int) piece < (int) WhiteWazir &&
1079 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
1080 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1081 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1082 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
1083 else /* promotion optional, default is promote */
1084 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
1086 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1087 NormalMove : IllegalMove;
1089 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
1090 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1091 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1092 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
1093 else /* promotion optional, default is promote */
1094 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
1096 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1097 NormalMove : IllegalMove;
1101 if (promoChar != NULLCHAR && promoChar != 'x') {
1102 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1103 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
1105 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
1107 cl.kind = IllegalMove;
1110 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
1118 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1119 int rf, int ff, int rt, int ft,
1122 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1129 register MateTestClosure *cl = (MateTestClosure *) closure;
1134 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1135 int MateTest(board, flags, epfile, castlingRights)
1138 char castlingRights[];
1144 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
1146 return inCheck ? MT_CHECK : MT_NONE;
1148 return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ?
1149 MT_CHECKMATE : MT_STALEMATE;
1154 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1155 int rf, int ff, int rt, int ft,
1158 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
1165 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1167 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1168 || PieceToChar(board[rf][ff]) == '~'
1169 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1171 (cl->rfIn == -1 || cl->rfIn == rf) &&
1172 (cl->ffIn == -1 || cl->ffIn == ff) &&
1173 (cl->rtIn == -1 || cl->rtIn == rt) &&
1174 (cl->ftIn == -1 || cl->ftIn == ft)) {
1177 cl->piece = board[rf][ff];
1186 void Disambiguate(board, flags, epfile, closure)
1189 DisambiguateClosure *closure;
1191 int illegal = 0; char c = closure->promoCharIn;
1193 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1194 closure->kind = ImpossibleMove;
1195 if (appData.debugMode) {
1196 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1197 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1198 closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
1200 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
1201 if (closure->count == 0) {
1202 /* See if it's an illegal move due to check */
1204 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
1205 (VOIDSTAR) closure);
1206 if (closure->count == 0) {
1207 /* No, it's not even that */
1208 if (appData.debugMode) { int i, j;
1209 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1210 for(j=0; j<BOARD_WIDTH; j++)
1211 fprintf(debugFP, "%3d", (int) board[i][j]);
1212 fprintf(debugFP, "\n");
1219 if(gameInfo.variant == VariantShogi) {
1220 /* [HGM] Shogi promotions. '=' means defer */
1221 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1222 ChessSquare piece = closure->piece;
1224 if (appData.debugMode) {
1225 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1226 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1227 closure->promoCharIn,closure->promoCharIn);
1230 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
1231 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
1232 closure->kind = IllegalMove;
1233 else if(flags & F_WHITE_ON_MOVE) {
1235 if (appData.debugMode) {
1236 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1237 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1238 closure->promoCharIn,closure->promoCharIn);
1241 if( (int) piece < (int) WhiteWazir &&
1242 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
1243 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1244 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1245 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
1246 else /* promotion optional, default is promote */
1247 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
1249 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
1250 NormalMove : IllegalMove;
1252 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
1253 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1254 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1255 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
1256 else /* promotion optional, default is promote */
1257 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
1259 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
1260 NormalMove : IllegalMove;
1264 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
1265 if (closure->kind == WhitePromotionQueen
1266 || closure->kind == BlackPromotionQueen) {
1268 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
1269 closure->promoCharIn);
1271 closure->kind = IllegalMove;
1275 if (appData.debugMode) {
1276 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1277 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1278 closure->promoCharIn,closure->promoCharIn);
1281 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
1282 if(closure->promoCharIn != '=')
1283 closure->promoChar = ToLower(closure->promoCharIn);
1284 else closure->promoChar = '=';
1285 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
1286 if (closure->count > 1) {
1287 closure->kind = AmbiguousMove;
1290 /* Note: If more than one illegal move matches, but no legal
1291 moves, we return IllegalMove, not AmbiguousMove. Caller
1292 can look at closure->count to detect this.
1294 closure->kind = IllegalMove;
1296 if(closure->kind == IllegalMove)
1297 /* [HGM] might be a variant we don't understand, pass on promotion info */
1298 closure->promoChar = ToLower(closure->promoCharIn);
1299 if (appData.debugMode) {
1300 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1301 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
1302 closure->promoChar >= ' ' ? closure->promoChar:'-');
1316 } CoordsToAlgebraicClosure;
1318 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1319 ChessMove kind, int rf, int ff,
1320 int rt, int ft, VOIDSTAR closure));
1322 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
1329 register CoordsToAlgebraicClosure *cl =
1330 (CoordsToAlgebraicClosure *) closure;
1332 if (rt == cl->rt && ft == cl->ft &&
1333 (board[rf][ff] == cl->piece
1334 || PieceToChar(board[rf][ff]) == '~' &&
1335 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1339 cl->kind = kind; /* this is the move we want */
1341 cl->file++; /* need file to rule out this move */
1345 cl->rank++; /* need rank to rule out this move */
1347 cl->either++; /* rank or file will rule out this move */
1353 /* Convert coordinates to normal algebraic notation.
1354 promoChar must be NULLCHAR or 'x' if not a promotion.
1356 ChessMove CoordsToAlgebraic(board, flags, epfile,
1357 rf, ff, rt, ft, promoChar, out)
1366 char *outp = out, c;
1367 CoordsToAlgebraicClosure cl;
1369 if (rf == DROP_RANK) {
1370 /* Bughouse piece drop */
1371 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1376 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1378 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1381 if (promoChar == 'x') promoChar = NULLCHAR;
1382 piece = board[rf][ff];
1383 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1385 if (appData.debugMode)
1386 fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
1390 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
1391 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1392 /* Keep short notation if move is illegal only because it
1393 leaves the player in check, but still return IllegalMove */
1394 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
1395 rf, ff, rt, ft, promoChar);
1396 if (kind == IllegalMove) break;
1401 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1402 /* Non-capture; use style "e5" */
1405 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1407 /* Capture; use style "exd5" */
1408 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1409 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
1413 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1415 /* Use promotion suffix style "=Q" */
1417 if (appData.debugMode)
1418 fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
1419 if (promoChar != NULLCHAR) {
1420 if(gameInfo.variant == VariantShogi) {
1421 /* [HGM] ... but not in Shogi! */
1422 *outp++ = promoChar == '=' ? '=' : '+';
1425 *outp++ = ToUpper(promoChar);
1434 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1435 /* Code added by Tord: FRC castling. */
1436 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1437 (piece == BlackKing && board[rt][ft] == BlackRook)) {
1438 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
1439 return LegalityTest(board, flags, epfile, initialRights,
1440 rf, ff, rt, ft, promoChar);
1442 /* End of code added by Tord */
1443 /* Test for castling or ICS wild castling */
1444 /* Use style "O-O" (oh-oh) for PGN compatibility */
1445 else if (rf == rt &&
1446 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1447 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1448 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1449 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1452 strcpy(out, "O-O-O");
1454 /* This notation is always unambiguous, unless there are
1455 kings on both the d and e files, with "wild castling"
1456 possible for the king on the d file and normal castling
1457 possible for the other. ICS rules for wild 9
1458 effectively make castling illegal for either king in
1459 this situation. So I am not going to worry about it;
1460 I'll just generate an ambiguous O-O in this case.
1462 return LegalityTest(board, flags, epfile, initialRights,
1463 rf, ff, rt, ft, promoChar);
1466 /* else fall through */
1474 cl.kind = IllegalMove;
1475 cl.rank = cl.file = cl.either = 0;
1476 GenLegal(board, flags, epfile, initialRights,
1477 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1479 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1480 /* Generate pretty moves for moving into check, but
1481 still return IllegalMove.
1483 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
1484 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1485 if (cl.kind == IllegalMove) break;
1486 cl.kind = IllegalMove;
1489 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1490 else "Ngf3" or "Ngxf7",
1491 else "N1f3" or "N5xf7",
1492 else "Ng1f3" or "Ng5xf7".
1494 c = PieceToChar(piece) ;
1495 if( c == '~' || c == '+') {
1496 /* [HGM] print nonexistent piece as its demoted version */
1497 piece = (ChessSquare) (DEMOTED piece);
1499 if(c=='+') *outp++ = c;
1500 *outp++ = ToUpper(PieceToChar(piece));
1502 if (cl.file || (cl.either && !cl.rank)) {
1508 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1511 if(board[rt][ft] != EmptySquare)
1517 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1519 if (gameInfo.variant == VariantShogi) {
1520 /* [HGM] in Shogi non-pawns can promote */
1521 if(flags & F_WHITE_ON_MOVE) {
1522 if( (int) cl.piece < (int) WhiteWazir &&
1523 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
1524 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1525 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1526 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
1527 else cl.kind = WhitePromotionQueen; /* promotion optional */
1529 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1530 NormalMove : IllegalMove;
1532 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
1533 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1534 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1535 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
1536 else cl.kind = BlackPromotionQueen; /* promotion optional */
1537 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1538 NormalMove : IllegalMove;
1540 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
1541 /* for optional promotions append '+' or '=' */
1542 if(promoChar == '=') {
1544 cl.kind = NormalMove;
1545 } else *outp++ = '+';
1547 } else if(cl.kind == IllegalMove) {
1548 /* Illegal move specifies any given promotion */
1549 if(promoChar != NULLCHAR && promoChar != 'x') {
1551 *outp++ = ToUpper(promoChar);
1558 /* [HGM] Always long notation for fairies we don't know */
1563 case WhiteGrasshopper:
1564 case BlackGrasshopper:
1566 /* Moving a nonexistent piece */
1570 /* Not a legal move, even ignoring check.
1571 If there was a piece on the from square,
1572 use style "Ng1g3" or "Ng1xe8";
1573 if there was a pawn or nothing (!),
1574 use style "g1g3" or "g1xe8". Use "x"
1575 if a piece was on the to square, even
1576 a piece of the same color.
1579 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1580 *outp++ = ToUpper(PieceToChar(piece));
1585 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1586 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1590 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1591 /* Use promotion suffix style "=Q" */
1592 if (promoChar != NULLCHAR && promoChar != 'x') {
1594 *outp++ = ToUpper(promoChar);
1601 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1610 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1612 int preyStackPointer, chaseStackPointer;
1615 char rf, ff, rt, ft;
1625 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1627 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1628 int rf, int ff, int rt, int ft,
1631 void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1637 { // For adding captures that can lead to chase indictment to the chaseStack
1638 if(board[rt][ft] == EmptySquare) return; // non-capture
1639 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
1640 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
1641 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
1642 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1643 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1644 chaseStack[chaseStackPointer].rf = rf;
1645 chaseStack[chaseStackPointer].ff = ff;
1646 chaseStack[chaseStackPointer].rt = rt;
1647 chaseStack[chaseStackPointer].ft = ft;
1648 chaseStackPointer++;
1651 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1652 int rf, int ff, int rt, int ft,
1655 void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1661 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1663 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1665 if(board[rt][ft] == EmptySquare) return; // no capture
1666 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1667 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
1669 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1670 for(i=0; i<chaseStackPointer; i++) {
1671 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1672 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
1673 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1674 chaseStack[i] = chaseStack[--chaseStackPointer];
1680 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1681 int rf, int ff, int rt, int ft,
1684 void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
1690 { // for determining if a piece (given through the closure) is protected
1691 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1693 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
1694 if(appData.debugMode && board[rt][ft] != EmptySquare)
1695 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1698 extern char moveList[MAX_MOVES][MOVE_LEN];
1700 int PerpetualChase(int first, int last)
1701 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1704 ChessSquare captured;
1706 preyStackPointer = 0; // clear stack of chased pieces
1707 for(i=first; i<last; i+=2) { // for all positions with same side to move
1708 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1709 chaseStackPointer = 0; // clear stack that is going to hold possible chases
1710 // determine all captures possible after the move, and put them on chaseStack
1711 GenLegal(boards[i+1], PosFlags(i), EP_NONE, initialRights, AttacksCallback, &cl);
1712 if(appData.debugMode) { int n;
1713 for(n=0; n<chaseStackPointer; n++)
1714 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1715 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1716 fprintf(debugFP, ": all capts\n");
1718 // determine all captures possible before the move, and delete them from chaseStack
1719 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1720 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1721 cl.rt = moveList[i][3]-ONE;
1722 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1723 GenLegal(boards[i], PosFlags(i), EP_NONE, initialRights, ExistingAttacksCallback, &cl);
1724 if(appData.debugMode) { int n;
1725 for(n=0; n<chaseStackPointer; n++)
1726 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1727 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1728 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1730 // chaseSack now contains all captures made possible by the move
1731 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1732 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1733 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1735 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1736 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
1738 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1739 continue; // C or H attack on R is always chase; leave on chaseStack
1741 if(attacker == victim) {
1742 if(LegalityTest(boards[i+1], PosFlags(i+1), EP_NONE, initialRights, chaseStack[j].rt,
1743 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1744 // we can capture back with equal piece, so this is no chase but a sacrifice
1745 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1746 j--; /* ! */ continue;
1751 // the attack is on a lower piece, or on a pinned or blocked equal one
1752 // test if the victim is protected by a true protector. First make the capture.
1753 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1754 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1755 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1756 // Then test if the opponent can recapture
1757 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
1758 cl.rt = chaseStack[j].rt;
1759 cl.ft = chaseStack[j].ft;
1760 if(appData.debugMode) {
1761 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1763 GenLegal(boards[i+1], PosFlags(i+1), EP_NONE, initialRights, ProtectedCallback, &cl); // try all moves
1764 // unmake the capture
1765 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1766 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1767 // if a recapture was found, piece is protected, and we are not chasing it.
1768 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1769 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1773 // chaseStack now contains all moves that chased
1774 if(appData.debugMode) { int n;
1775 for(n=0; n<chaseStackPointer; n++)
1776 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1777 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1778 fprintf(debugFP, ": chases\n");
1780 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1781 for(j=0; j<chaseStackPointer; j++) {
1782 preyStack[j].rank = chaseStack[j].rt;
1783 preyStack[j].file = chaseStack[j].ft;
1785 preyStackPointer = chaseStackPointer;
1788 for(j=0; j<chaseStackPointer; j++) {
1789 for(k=0; k<preyStackPointer; k++) {
1790 // search the victim of each chase move on the preyStack (first occurrence)
1791 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1792 if(k < tail) break; // piece was already identified as still being chased
1793 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1794 preyStack[tail] = preyStack[k]; // by swapping
1795 preyStack[k] = preyStack[preyStackPointer];
1801 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1802 if(appData.debugMode) { int n;
1803 for(n=0; n<preyStackPointer; n++)
1804 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1805 fprintf(debugFP, "always chased upto ply %d\n", i);
1807 // now adjust the location of the chased pieces according to opponent move
1808 for(j=0; j<preyStackPointer; j++) {
1809 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1810 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1811 preyStack[j].rank = moveList[i+1][3]-ONE;
1812 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1817 return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased