2 * moves.c - Move generation and checking
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
5 * Massachusetts. Enhancements Copyright
6 * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software
9 * The following terms apply to Digital Equipment Corporation's copyright
11 * ------------------------------------------------------------------------
14 * Permission to use, copy, modify, and distribute this software and its
15 * documentation for any purpose and without fee is hereby granted,
16 * provided that the above copyright notice appear in all copies and that
17 * both that copyright notice and this permission notice appear in
18 * supporting documentation, and that the name of Digital not be
19 * used in advertising or publicity pertaining to distribution of the
20 * software without specific, written prior permission.
22 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
23 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
24 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
25 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
26 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
27 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29 * ------------------------------------------------------------------------
31 * The following terms apply to the enhanced version of XBoard
32 * distributed by the Free Software Foundation:
33 * ------------------------------------------------------------------------
35 * GNU XBoard is free software: you can redistribute it and/or modify
36 * it under the terms of the GNU General Public License as published by
37 * the Free Software Foundation, either version 3 of the License, or (at
38 * your option) any later version.
40 * GNU XBoard is distributed in the hope that it will be useful, but
41 * WITHOUT ANY WARRANTY; without even the implied warranty of
42 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
43 * General Public License for more details.
45 * You should have received a copy of the GNU General Public License
46 * along with this program. If not, see http://www.gnu.org/licenses/. *
48 *------------------------------------------------------------------------
49 ** See the file ChangeLog for a revision history. */
56 #else /* not HAVE_STRING_H */
58 #endif /* not HAVE_STRING_H */
64 int WhitePiece P((ChessSquare));
65 int BlackPiece P((ChessSquare));
66 int SameColor P((ChessSquare, ChessSquare));
67 int PosFlags(int index);
69 extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */
75 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
81 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
84 int SameColor(piece1, piece2)
85 ChessSquare piece1, piece2;
87 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
88 (int) piece1 < (int) BlackPawn &&
89 (int) piece2 >= (int) WhitePawn &&
90 (int) piece2 < (int) BlackPawn)
91 || ((int) piece1 >= (int) BlackPawn &&
92 (int) piece1 < (int) EmptySquare &&
93 (int) piece2 >= (int) BlackPawn &&
94 (int) piece2 < (int) EmptySquare);
97 ChessSquare PromoPiece(moveType)
103 case WhitePromotionQueen:
105 case BlackPromotionQueen:
107 case WhitePromotionRook:
109 case BlackPromotionRook:
111 case WhitePromotionBishop:
113 case BlackPromotionBishop:
115 case WhitePromotionKnight:
117 case BlackPromotionKnight:
119 case WhitePromotionKing:
121 case BlackPromotionKing:
123 case WhitePromotionChancellor:
124 return WhiteMarshall;
125 case BlackPromotionChancellor:
126 return BlackMarshall;
127 case WhitePromotionArchbishop:
129 case BlackPromotionArchbishop:
131 case WhitePromotionCentaur:
133 case BlackPromotionCentaur:
138 char pieceToChar[] = {
139 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
140 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
141 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
142 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
148 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
149 return pieceToChar[(int) p];
152 int PieceToNumber(p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
156 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
158 while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
162 ChessSquare CharToPiece(c)
166 for(i=0; i< (int) EmptySquare; i++)
167 if(pieceToChar[i] == c) return (ChessSquare) i;
171 ChessMove PromoCharToMoveType(whiteOnMove, promoChar)
174 { /* [HGM] made dependent on CharToPiece to alow alternate piece letters */
175 ChessSquare piece = CharToPiece(whiteOnMove ? ToUpper(promoChar) : ToLower(promoChar) );
178 if(promoChar == NULLCHAR) return NormalMove;
182 return WhitePromotionQueen;
184 return WhitePromotionRook;
186 return WhitePromotionBishop;
188 return WhitePromotionKnight;
190 return WhitePromotionKing;
192 return WhitePromotionArchbishop;
194 return WhitePromotionChancellor;
196 return WhitePromotionCentaur;
198 return BlackPromotionQueen;
200 return BlackPromotionRook;
202 return BlackPromotionBishop;
204 return BlackPromotionKnight;
206 return BlackPromotionKing;
208 return BlackPromotionArchbishop;
210 return BlackPromotionChancellor;
212 return BlackPromotionCentaur;
214 // not all promotion implemented yet! Take Queen for those we don't know.
215 return (whiteOnMove ? WhitePromotionQueen : BlackPromotionQueen);
219 void CopyBoard(to, from)
224 for (i = 0; i < BOARD_HEIGHT; i++)
225 for (j = 0; j < BOARD_WIDTH; j++)
226 to[i][j] = from[i][j];
229 int CompareBoards(board1, board2)
230 Board board1, board2;
234 for (i = 0; i < BOARD_HEIGHT; i++)
235 for (j = 0; j < BOARD_WIDTH; j++) {
236 if (board1[i][j] != board2[i][j])
243 /* Call callback once for each pseudo-legal move in the given
244 position, except castling moves. A move is pseudo-legal if it is
245 legal, or if it would be legal except that it leaves the king in
246 check. In the arguments, epfile is EP_NONE if the previous move
247 was not a double pawn push, or the file 0..7 if it was, or
248 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
249 Promotion moves generated are to Queen only.
251 void GenPseudoLegal(board, flags, epfile, callback, closure)
255 MoveCallback callback;
259 int i, j, d, s, fs, rs, rt, ft, m;
261 for (rf = 0; rf < BOARD_HEIGHT; rf++)
262 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
265 if (flags & F_WHITE_ON_MOVE) {
266 if (!WhitePiece(board[rf][ff])) continue;
268 if (!BlackPiece(board[rf][ff])) continue;
270 m = 0; piece = board[rf][ff];
271 if(PieceToChar(piece) == '~')
272 piece = (ChessSquare) ( DEMOTED piece );
273 if(gameInfo.variant == VariantShogi)
274 piece = (ChessSquare) ( SHOGI piece );
277 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
279 /* can't happen ([HGM] except for faries...) */
283 if(gameInfo.variant == VariantXiangqi) {
284 /* [HGM] capture and move straight ahead in Xiangqi */
285 if (rf < BOARD_HEIGHT-1 &&
286 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
287 callback(board, flags, NormalMove,
288 rf, ff, rf + 1, ff, closure);
290 /* and move sideways when across the river */
291 for (s = -1; s <= 1; s += 2) {
292 if (rf >= BOARD_HEIGHT>>1 &&
293 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
294 !WhitePiece(board[rf][ff+s]) ) {
295 callback(board, flags, NormalMove,
296 rf, ff, rf, ff+s, closure);
301 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
302 callback(board, flags,
303 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
304 rf, ff, rf + 1, ff, closure);
306 if (rf == 1 && board[2][ff] == EmptySquare &&
307 gameInfo.variant != VariantShatranj && /* [HGM] */
308 gameInfo.variant != VariantCourier && /* [HGM] */
309 board[3][ff] == EmptySquare ) {
310 callback(board, flags, NormalMove,
311 rf, ff, 3, ff, closure);
313 for (s = -1; s <= 1; s += 2) {
314 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
315 ((flags & F_KRIEGSPIEL_CAPTURE) ||
316 BlackPiece(board[rf + 1][ff + s]))) {
317 callback(board, flags,
318 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
319 rf, ff, rf + 1, ff + s, closure);
321 if (rf == BOARD_HEIGHT-4) {
322 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
323 (epfile == ff + s || epfile == EP_UNKNOWN) &&
324 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
325 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
326 callback(board, flags, WhiteCapturesEnPassant,
327 rf, ff, 5, ff + s, closure);
334 if(gameInfo.variant == VariantXiangqi) {
335 /* [HGM] capture straight ahead in Xiangqi */
336 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
337 callback(board, flags, NormalMove,
338 rf, ff, rf - 1, ff, closure);
340 /* and move sideways when across the river */
341 for (s = -1; s <= 1; s += 2) {
342 if (rf < BOARD_HEIGHT>>1 &&
343 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
344 !BlackPiece(board[rf][ff+s]) ) {
345 callback(board, flags, NormalMove,
346 rf, ff, rf, ff+s, closure);
351 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
352 callback(board, flags,
353 rf == 1 ? BlackPromotionQueen : NormalMove,
354 rf, ff, rf - 1, ff, closure);
356 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
357 gameInfo.variant != VariantShatranj && /* [HGM] */
358 gameInfo.variant != VariantCourier && /* [HGM] */
359 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
360 callback(board, flags, NormalMove,
361 rf, ff, BOARD_HEIGHT-4, ff, closure);
363 for (s = -1; s <= 1; s += 2) {
364 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
365 ((flags & F_KRIEGSPIEL_CAPTURE) ||
366 WhitePiece(board[rf - 1][ff + s]))) {
367 callback(board, flags,
368 rf == 1 ? BlackPromotionQueen : NormalMove,
369 rf, ff, rf - 1, ff + s, closure);
372 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
373 (epfile == ff + s || epfile == EP_UNKNOWN) &&
374 board[3][ff + s] == WhitePawn &&
375 board[2][ff + s] == EmptySquare) {
376 callback(board, flags, BlackCapturesEnPassant,
377 rf, ff, 2, ff + s, closure);
388 for (i = -1; i <= 1; i += 2)
389 for (j = -1; j <= 1; j += 2)
390 for (s = 1; s <= 2; s++) {
393 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
394 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
395 && !SameColor(board[rf][ff], board[rt][ft]))
396 callback(board, flags, NormalMove,
397 rf, ff, rt, ft, closure);
401 case SHOGI WhiteKnight:
402 for (s = -1; s <= 1; s += 2) {
403 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
404 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
405 callback(board, flags, NormalMove,
406 rf, ff, rf + 2, ff + s, closure);
411 case SHOGI BlackKnight:
412 for (s = -1; s <= 1; s += 2) {
413 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
414 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
415 callback(board, flags, NormalMove,
416 rf, ff, rf - 2, ff + s, closure);
423 for (d = 0; d <= 1; d++)
424 for (s = -1; s <= 1; s += 2) {
427 rt = rf + (i * s) * d;
428 ft = ff + (i * s) * (1 - d);
429 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
430 if (m == 0 && board[rt][ft] == EmptySquare)
431 callback(board, flags, NormalMove,
432 rf, ff, rt, ft, closure);
433 if (m == 1 && board[rt][ft] != EmptySquare &&
434 !SameColor(board[rf][ff], board[rt][ft]) )
435 callback(board, flags, NormalMove,
436 rf, ff, rt, ft, closure);
437 if (board[rt][ft] != EmptySquare && m++) break;
442 /* Gold General (and all its promoted versions) . First do the */
443 /* diagonal forward steps, then proceed as normal Wazir */
444 case SHOGI WhiteWazir:
445 case SHOGI (PROMOTED WhitePawn):
446 case SHOGI (PROMOTED WhiteKnight):
447 case SHOGI (PROMOTED WhiteQueen):
448 case SHOGI (PROMOTED WhiteFerz):
449 for (s = -1; s <= 1; s += 2) {
450 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
451 !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
452 callback(board, flags, NormalMove,
453 rf, ff, rf + 1, ff + s, closure);
458 case SHOGI BlackWazir:
459 case SHOGI (PROMOTED BlackPawn):
460 case SHOGI (PROMOTED BlackKnight):
461 case SHOGI (PROMOTED BlackQueen):
462 case SHOGI (PROMOTED BlackFerz):
463 for (s = -1; s <= 1; s += 2) {
464 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
465 !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
466 callback(board, flags, NormalMove,
467 rf, ff, rf - 1, ff + s, closure);
474 for (d = 0; d <= 1; d++)
475 for (s = -1; s <= 1; s += 2) {
477 ft = ff + s * (1 - d);
478 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
479 && !SameColor(board[rf][ff], board[rt][ft]) &&
480 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
481 callback(board, flags, NormalMove,
482 rf, ff, rt, ft, closure);
488 /* [HGM] support Shatranj pieces */
489 for (rs = -1; rs <= 1; rs += 2)
490 for (fs = -1; fs <= 1; fs += 2) {
493 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
494 && ( gameInfo.variant != VariantXiangqi ||
495 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
497 && !SameColor(board[rf][ff], board[rt][ft]))
498 callback(board, flags, NormalMove,
499 rf, ff, rt, ft, closure);
503 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
504 case SHOGI WhiteCardinal:
505 case SHOGI BlackCardinal:
508 /* Capablanca Archbishop continues as Knight */
513 /* Shogi Bishops are ordinary Bishops */
514 case SHOGI WhiteBishop:
515 case SHOGI BlackBishop:
518 for (rs = -1; rs <= 1; rs += 2)
519 for (fs = -1; fs <= 1; fs += 2)
523 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
524 if (SameColor(board[rf][ff], board[rt][ft])) break;
525 callback(board, flags, NormalMove,
526 rf, ff, rt, ft, closure);
527 if (board[rt][ft] != EmptySquare) break;
529 if(m==1) goto mounted;
530 if(m==2) goto finishGold;
531 /* Bishop falls through */
534 /* Shogi Lance is unlike anything, and asymmetric at that */
535 case SHOGI WhiteQueen:
539 if (rt >= BOARD_HEIGHT) break;
540 if (SameColor(board[rf][ff], board[rt][ft])) break;
541 callback(board, flags, NormalMove,
542 rf, ff, rt, ft, closure);
543 if (board[rt][ft] != EmptySquare) break;
547 case SHOGI BlackQueen:
552 if (SameColor(board[rf][ff], board[rt][ft])) break;
553 callback(board, flags, NormalMove,
554 rf, ff, rt, ft, closure);
555 if (board[rt][ft] != EmptySquare) break;
559 /* Shogi Dragon King has to continue as Ferz after Rook moves */
560 case SHOGI WhiteDragon:
561 case SHOGI BlackDragon:
564 /* Capablanca Chancellor sets flag to continue as Knight */
569 /* Shogi Rooks are ordinary Rooks */
570 case SHOGI WhiteRook:
571 case SHOGI BlackRook:
574 for (d = 0; d <= 1; d++)
575 for (s = -1; s <= 1; s += 2)
577 rt = rf + (i * s) * d;
578 ft = ff + (i * s) * (1 - d);
579 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
580 if (SameColor(board[rf][ff], board[rt][ft])) break;
581 callback(board, flags, NormalMove,
582 rf, ff, rt, ft, closure);
583 if (board[rt][ft] != EmptySquare) break;
585 if(m==1) goto mounted;
586 if(m==2) goto finishSilver;
591 for (rs = -1; rs <= 1; rs++)
592 for (fs = -1; fs <= 1; fs++) {
593 if (rs == 0 && fs == 0) continue;
597 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
598 if (SameColor(board[rf][ff], board[rt][ft])) break;
599 callback(board, flags, NormalMove,
600 rf, ff, rt, ft, closure);
601 if (board[rt][ft] != EmptySquare) break;
606 /* Shogi Pawn and Silver General: first the Pawn move, */
607 /* then the General continues like a Ferz */
608 case SHOGI WhitePawn:
609 case SHOGI WhiteFerz:
610 if (rf < BOARD_HEIGHT-1 &&
611 !SameColor(board[rf][ff], board[rf + 1][ff]) )
612 callback(board, flags, NormalMove,
613 rf, ff, rf + 1, ff, closure);
614 if(piece != SHOGI WhitePawn) goto finishSilver;
617 case SHOGI BlackPawn:
618 case SHOGI BlackFerz:
620 !SameColor(board[rf][ff], board[rf - 1][ff]) )
621 callback(board, flags, NormalMove,
622 rf, ff, rf - 1, ff, closure);
623 if(piece == SHOGI BlackPawn) break;
628 /* [HGM] support Shatranj pieces */
629 for (rs = -1; rs <= 1; rs += 2)
630 for (fs = -1; fs <= 1; fs += 2) {
633 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
634 if (!SameColor(board[rf][ff], board[rt][ft]) &&
635 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
636 callback(board, flags, NormalMove,
637 rf, ff, rt, ft, closure);
643 m++; // [HGM] superchess: use for Centaur
646 case SHOGI WhiteKing:
647 case SHOGI BlackKing:
651 for (i = -1; i <= 1; i++)
652 for (j = -1; j <= 1; j++) {
653 if (i == 0 && j == 0) continue;
656 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
657 if (SameColor(board[rf][ff], board[rt][ft])) continue;
658 callback(board, flags, NormalMove,
659 rf, ff, rt, ft, closure);
661 if(m==1) goto mounted;
664 case WhiteNightrider:
665 case BlackNightrider:
666 for (i = -1; i <= 1; i += 2)
667 for (j = -1; j <= 1; j += 2)
668 for (s = 1; s <= 2; s++) { int k;
672 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
673 if (SameColor(board[rf][ff], board[rt][ft])) break;
674 callback(board, flags, NormalMove,
675 rf, ff, rt, ft, closure);
676 if (board[rt][ft] != EmptySquare) break;
691 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
692 int rf, int ff, int rt, int ft,
695 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
702 register GenLegalClosure *cl = (GenLegalClosure *) closure;
704 if (!(flags & F_IGNORE_CHECK) &&
705 CheckTest(board, flags, rf, ff, rt, ft,
706 kind == WhiteCapturesEnPassant ||
707 kind == BlackCapturesEnPassant)) return;
708 if (flags & F_ATOMIC_CAPTURE) {
709 if (board[rt][ft] != EmptySquare ||
710 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
712 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
713 if (board[rf][ff] == king) return;
714 for (r = rt-1; r <= rt+1; r++) {
715 for (f = ft-1; f <= ft+1; f++) {
716 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
717 board[r][f] == king) return;
722 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
729 int captures; // [HGM] losers
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;
1009 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
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(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1030 cl->captures++; // [HGM] losers: count legal captures
1031 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1035 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
1038 int rf, ff, rt, ft, promoChar;
1039 char castlingRights[];
1041 LegalityTestClosure cl; ChessSquare piece = board[rf][ff];
1043 if (appData.debugMode) {
1045 for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
1046 fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1048 /* [HGM] Lance, Cobra and Falcon are wildcard pieces; consider all their moves legal */
1049 /* (perhaps we should disallow moves that obviously leave us in check?) */
1050 if(piece == WhiteFalcon || piece == BlackFalcon ||
1051 piece == WhiteCobra || piece == BlackCobra ||
1052 piece == WhiteLance || piece == BlackLance)
1059 cl.kind = IllegalMove;
1060 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1061 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
1062 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1063 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1064 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1066 if(gameInfo.variant == VariantShogi) {
1067 /* [HGM] Shogi promotions. '=' means defer */
1068 if(rf != DROP_RANK && cl.kind == NormalMove) {
1069 ChessSquare piece = board[rf][ff];
1071 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1072 if(promoChar != NULLCHAR && promoChar != 'x' &&
1073 promoChar != '+' && promoChar != '=' &&
1074 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
1075 cl.kind = IllegalMove;
1076 else if(flags & F_WHITE_ON_MOVE) {
1077 if( (int) piece < (int) WhiteWazir &&
1078 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
1079 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1080 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1081 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
1082 else /* promotion optional, default is promote */
1083 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
1085 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1086 NormalMove : IllegalMove;
1088 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
1089 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1090 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1091 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
1092 else /* promotion optional, default is promote */
1093 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
1095 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1096 NormalMove : IllegalMove;
1100 if (promoChar != NULLCHAR && promoChar != 'x') {
1101 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1102 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
1104 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
1106 cl.kind = IllegalMove;
1109 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
1117 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1118 int rf, int ff, int rt, int ft,
1121 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1128 register MateTestClosure *cl = (MateTestClosure *) closure;
1133 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1134 int MateTest(board, flags, epfile, castlingRights)
1137 char castlingRights[];
1140 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1141 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1143 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1144 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1145 nrKing += (board[r][f] == king); // stm has king
1146 if( board[r][f] != EmptySquare ) {
1147 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1152 if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
1153 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1154 case VariantShatranj:
1155 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1159 if(nrKing == 0) return MT_NOKING;
1162 if(myPieces == 1) return MT_BARE;
1165 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
1166 // [HGM] 3check: yet to do!
1168 return inCheck ? MT_CHECK : MT_NONE;
1170 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1171 return myPieces == hisPieces ? MT_STALEMATE :
1172 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1173 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1174 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1176 return inCheck ? MT_CHECKMATE
1177 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
1178 MT_STAINMATE : MT_STALEMATE;
1183 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1184 int rf, int ff, int rt, int ft,
1187 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
1194 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1196 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1197 || PieceToChar(board[rf][ff]) == '~'
1198 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1200 (cl->rfIn == -1 || cl->rfIn == rf) &&
1201 (cl->ffIn == -1 || cl->ffIn == ff) &&
1202 (cl->rtIn == -1 || cl->rtIn == rt) &&
1203 (cl->ftIn == -1 || cl->ftIn == ft)) {
1206 cl->piece = board[rf][ff];
1215 void Disambiguate(board, flags, epfile, closure)
1218 DisambiguateClosure *closure;
1220 int illegal = 0; char c = closure->promoCharIn;
1222 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1223 closure->kind = ImpossibleMove;
1224 if (appData.debugMode) {
1225 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1226 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1227 closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
1229 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
1230 if (closure->count == 0) {
1231 /* See if it's an illegal move due to check */
1233 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
1234 (VOIDSTAR) closure);
1235 if (closure->count == 0) {
1236 /* No, it's not even that */
1237 if (appData.debugMode) { int i, j;
1238 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1239 for(j=0; j<BOARD_WIDTH; j++)
1240 fprintf(debugFP, "%3d", (int) board[i][j]);
1241 fprintf(debugFP, "\n");
1248 if(gameInfo.variant == VariantShogi) {
1249 /* [HGM] Shogi promotions. '=' means defer */
1250 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1251 ChessSquare piece = closure->piece;
1253 if (appData.debugMode) {
1254 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1255 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1256 closure->promoCharIn,closure->promoCharIn);
1259 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
1260 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
1261 closure->kind = IllegalMove;
1262 else if(flags & F_WHITE_ON_MOVE) {
1264 if (appData.debugMode) {
1265 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1266 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1267 closure->promoCharIn,closure->promoCharIn);
1270 if( (int) piece < (int) WhiteWazir &&
1271 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
1272 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1273 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1274 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
1275 else /* promotion optional, default is promote */
1276 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
1278 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
1279 NormalMove : IllegalMove;
1281 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
1282 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1283 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1284 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
1285 else /* promotion optional, default is promote */
1286 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
1288 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
1289 NormalMove : IllegalMove;
1293 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
1294 if (closure->kind == WhitePromotionQueen
1295 || closure->kind == BlackPromotionQueen) {
1297 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
1298 closure->promoCharIn);
1300 closure->kind = IllegalMove;
1304 if (appData.debugMode) {
1305 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1306 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1307 closure->promoCharIn,closure->promoCharIn);
1310 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
1311 if(closure->promoCharIn != '=')
1312 closure->promoChar = ToLower(closure->promoCharIn);
1313 else closure->promoChar = '=';
1314 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
1315 if (closure->count > 1) {
1316 closure->kind = AmbiguousMove;
1319 /* Note: If more than one illegal move matches, but no legal
1320 moves, we return IllegalMove, not AmbiguousMove. Caller
1321 can look at closure->count to detect this.
1323 closure->kind = IllegalMove;
1325 if(closure->kind == IllegalMove)
1326 /* [HGM] might be a variant we don't understand, pass on promotion info */
1327 closure->promoChar = ToLower(closure->promoCharIn);
1328 if (appData.debugMode) {
1329 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1330 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
1331 closure->promoChar >= ' ' ? closure->promoChar:'-');
1345 } CoordsToAlgebraicClosure;
1347 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1348 ChessMove kind, int rf, int ff,
1349 int rt, int ft, VOIDSTAR closure));
1351 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
1358 register CoordsToAlgebraicClosure *cl =
1359 (CoordsToAlgebraicClosure *) closure;
1361 if (rt == cl->rt && ft == cl->ft &&
1362 (board[rf][ff] == cl->piece
1363 || PieceToChar(board[rf][ff]) == '~' &&
1364 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1368 cl->kind = kind; /* this is the move we want */
1370 cl->file++; /* need file to rule out this move */
1374 cl->rank++; /* need rank to rule out this move */
1376 cl->either++; /* rank or file will rule out this move */
1382 /* Convert coordinates to normal algebraic notation.
1383 promoChar must be NULLCHAR or 'x' if not a promotion.
1385 ChessMove CoordsToAlgebraic(board, flags, epfile,
1386 rf, ff, rt, ft, promoChar, out)
1395 char *outp = out, c;
1396 CoordsToAlgebraicClosure cl;
1398 if (rf == DROP_RANK) {
1399 /* Bughouse piece drop */
1400 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1405 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1407 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1410 if (promoChar == 'x') promoChar = NULLCHAR;
1411 piece = board[rf][ff];
1412 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1414 if (appData.debugMode)
1415 fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
1419 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
1420 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1421 /* Keep short notation if move is illegal only because it
1422 leaves the player in check, but still return IllegalMove */
1423 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
1424 rf, ff, rt, ft, promoChar);
1425 if (kind == IllegalMove) break;
1430 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1431 /* Non-capture; use style "e5" */
1434 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1436 /* Capture; use style "exd5" */
1437 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1438 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
1442 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1444 /* Use promotion suffix style "=Q" */
1446 if (appData.debugMode)
1447 fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
1448 if (promoChar != NULLCHAR) {
1449 if(gameInfo.variant == VariantShogi) {
1450 /* [HGM] ... but not in Shogi! */
1451 *outp++ = promoChar == '=' ? '=' : '+';
1454 *outp++ = ToUpper(promoChar);
1463 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1464 /* Code added by Tord: FRC castling. */
1465 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1466 (piece == BlackKing && board[rt][ft] == BlackRook)) {
1467 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
1468 return LegalityTest(board, flags, epfile, initialRights,
1469 rf, ff, rt, ft, promoChar);
1471 /* End of code added by Tord */
1472 /* Test for castling or ICS wild castling */
1473 /* Use style "O-O" (oh-oh) for PGN compatibility */
1474 else if (rf == rt &&
1475 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1476 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1477 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1478 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1481 strcpy(out, "O-O-O");
1483 /* This notation is always unambiguous, unless there are
1484 kings on both the d and e files, with "wild castling"
1485 possible for the king on the d file and normal castling
1486 possible for the other. ICS rules for wild 9
1487 effectively make castling illegal for either king in
1488 this situation. So I am not going to worry about it;
1489 I'll just generate an ambiguous O-O in this case.
1491 return LegalityTest(board, flags, epfile, initialRights,
1492 rf, ff, rt, ft, promoChar);
1495 /* else fall through */
1503 cl.kind = IllegalMove;
1504 cl.rank = cl.file = cl.either = 0;
1505 GenLegal(board, flags, epfile, initialRights,
1506 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1508 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1509 /* Generate pretty moves for moving into check, but
1510 still return IllegalMove.
1512 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
1513 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1514 if (cl.kind == IllegalMove) break;
1515 cl.kind = IllegalMove;
1518 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1519 else "Ngf3" or "Ngxf7",
1520 else "N1f3" or "N5xf7",
1521 else "Ng1f3" or "Ng5xf7".
1523 c = PieceToChar(piece) ;
1524 if( c == '~' || c == '+') {
1525 /* [HGM] print nonexistent piece as its demoted version */
1526 piece = (ChessSquare) (DEMOTED piece);
1528 if(c=='+') *outp++ = c;
1529 *outp++ = ToUpper(PieceToChar(piece));
1531 if (cl.file || (cl.either && !cl.rank)) {
1537 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1540 if(board[rt][ft] != EmptySquare)
1546 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1548 if (gameInfo.variant == VariantShogi) {
1549 /* [HGM] in Shogi non-pawns can promote */
1550 if(flags & F_WHITE_ON_MOVE) {
1551 if( (int) cl.piece < (int) WhiteWazir &&
1552 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
1553 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1554 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1555 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
1556 else cl.kind = WhitePromotionQueen; /* promotion optional */
1558 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1559 NormalMove : IllegalMove;
1561 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
1562 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1563 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1564 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
1565 else cl.kind = BlackPromotionQueen; /* promotion optional */
1566 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1567 NormalMove : IllegalMove;
1569 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
1570 /* for optional promotions append '+' or '=' */
1571 if(promoChar == '=') {
1573 cl.kind = NormalMove;
1574 } else *outp++ = '+';
1576 } else if(cl.kind == IllegalMove) {
1577 /* Illegal move specifies any given promotion */
1578 if(promoChar != NULLCHAR && promoChar != 'x') {
1580 *outp++ = ToUpper(promoChar);
1587 /* [HGM] Always long notation for fairies we don't know */
1592 case WhiteGrasshopper:
1593 case BlackGrasshopper:
1595 /* Moving a nonexistent piece */
1599 /* Not a legal move, even ignoring check.
1600 If there was a piece on the from square,
1601 use style "Ng1g3" or "Ng1xe8";
1602 if there was a pawn or nothing (!),
1603 use style "g1g3" or "g1xe8". Use "x"
1604 if a piece was on the to square, even
1605 a piece of the same color.
1608 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1609 *outp++ = ToUpper(PieceToChar(piece));
1614 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1615 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1619 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1620 /* Use promotion suffix style "=Q" */
1621 if (promoChar != NULLCHAR && promoChar != 'x') {
1623 *outp++ = ToUpper(promoChar);
1630 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1639 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1641 int preyStackPointer, chaseStackPointer;
1644 char rf, ff, rt, ft;
1654 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1656 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1657 int rf, int ff, int rt, int ft,
1660 void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1666 { // For adding captures that can lead to chase indictment to the chaseStack
1667 if(board[rt][ft] == EmptySquare) return; // non-capture
1668 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
1669 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
1670 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
1671 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1672 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1673 chaseStack[chaseStackPointer].rf = rf;
1674 chaseStack[chaseStackPointer].ff = ff;
1675 chaseStack[chaseStackPointer].rt = rt;
1676 chaseStack[chaseStackPointer].ft = ft;
1677 chaseStackPointer++;
1680 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1681 int rf, int ff, int rt, int ft,
1684 void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1690 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1692 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1694 if(board[rt][ft] == EmptySquare) return; // no capture
1695 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1696 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
1698 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1699 for(i=0; i<chaseStackPointer; i++) {
1700 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1701 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
1702 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1703 chaseStack[i] = chaseStack[--chaseStackPointer];
1709 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1710 int rf, int ff, int rt, int ft,
1713 void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
1719 { // for determining if a piece (given through the closure) is protected
1720 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1722 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
1723 if(appData.debugMode && board[rt][ft] != EmptySquare)
1724 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1727 extern char moveList[MAX_MOVES][MOVE_LEN];
1729 int PerpetualChase(int first, int last)
1730 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1733 ChessSquare captured;
1735 preyStackPointer = 0; // clear stack of chased pieces
1736 for(i=first; i<last; i+=2) { // for all positions with same side to move
1737 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1738 chaseStackPointer = 0; // clear stack that is going to hold possible chases
1739 // determine all captures possible after the move, and put them on chaseStack
1740 GenLegal(boards[i+1], PosFlags(i), EP_NONE, initialRights, AttacksCallback, &cl);
1741 if(appData.debugMode) { int n;
1742 for(n=0; n<chaseStackPointer; n++)
1743 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1744 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1745 fprintf(debugFP, ": all capts\n");
1747 // determine all captures possible before the move, and delete them from chaseStack
1748 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1749 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1750 cl.rt = moveList[i][3]-ONE;
1751 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1752 GenLegal(boards[i], PosFlags(i), EP_NONE, initialRights, ExistingAttacksCallback, &cl);
1753 if(appData.debugMode) { int n;
1754 for(n=0; n<chaseStackPointer; n++)
1755 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1756 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1757 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1759 // chaseSack now contains all captures made possible by the move
1760 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1761 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1762 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1764 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1765 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
1767 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1768 continue; // C or H attack on R is always chase; leave on chaseStack
1770 if(attacker == victim) {
1771 if(LegalityTest(boards[i+1], PosFlags(i+1), EP_NONE, initialRights, chaseStack[j].rt,
1772 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1773 // we can capture back with equal piece, so this is no chase but a sacrifice
1774 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1775 j--; /* ! */ continue;
1780 // the attack is on a lower piece, or on a pinned or blocked equal one
1781 // test if the victim is protected by a true protector. First make the capture.
1782 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1783 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1784 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1785 // Then test if the opponent can recapture
1786 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
1787 cl.rt = chaseStack[j].rt;
1788 cl.ft = chaseStack[j].ft;
1789 if(appData.debugMode) {
1790 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1792 GenLegal(boards[i+1], PosFlags(i+1), EP_NONE, initialRights, ProtectedCallback, &cl); // try all moves
1793 // unmake the capture
1794 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1795 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1796 // if a recapture was found, piece is protected, and we are not chasing it.
1797 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1798 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1802 // chaseStack now contains all moves that chased
1803 if(appData.debugMode) { int n;
1804 for(n=0; n<chaseStackPointer; n++)
1805 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1806 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1807 fprintf(debugFP, ": chases\n");
1809 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1810 for(j=0; j<chaseStackPointer; j++) {
1811 preyStack[j].rank = chaseStack[j].rt;
1812 preyStack[j].file = chaseStack[j].ft;
1814 preyStackPointer = chaseStackPointer;
1817 for(j=0; j<chaseStackPointer; j++) {
1818 for(k=0; k<preyStackPointer; k++) {
1819 // search the victim of each chase move on the preyStack (first occurrence)
1820 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1821 if(k < tail) break; // piece was already identified as still being chased
1822 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1823 preyStack[tail] = preyStack[k]; // by swapping
1824 preyStack[k] = preyStack[preyStackPointer];
1830 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1831 if(appData.debugMode) { int n;
1832 for(n=0; n<preyStackPointer; n++)
1833 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1834 fprintf(debugFP, "always chased upto ply %d\n", i);
1836 // now adjust the location of the chased pieces according to opponent move
1837 for(j=0; j<preyStackPointer; j++) {
1838 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1839 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1840 preyStack[j].rank = moveList[i+1][3]-ONE;
1841 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1846 return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased