2 * moves.c - Move generation and checking
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * Enhancements Copyright 2005 Alessandro Scotti
12 * The following terms apply to Digital Equipment Corporation's copyright
14 * ------------------------------------------------------------------------
17 * Permission to use, copy, modify, and distribute this software and its
18 * documentation for any purpose and without fee is hereby granted,
19 * provided that the above copyright notice appear in all copies and that
20 * both that copyright notice and this permission notice appear in
21 * supporting documentation, and that the name of Digital not be
22 * used in advertising or publicity pertaining to distribution of the
23 * software without specific, written prior permission.
25 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
26 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
27 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
28 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
29 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
30 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
32 * ------------------------------------------------------------------------
34 * The following terms apply to the enhanced version of XBoard
35 * distributed by the Free Software Foundation:
36 * ------------------------------------------------------------------------
38 * GNU XBoard is free software: you can redistribute it and/or modify
39 * it under the terms of the GNU General Public License as published by
40 * the Free Software Foundation, either version 3 of the License, or (at
41 * your option) any later version.
43 * GNU XBoard is distributed in the hope that it will be useful, but
44 * WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
46 * General Public License for more details.
48 * You should have received a copy of the GNU General Public License
49 * along with this program. If not, see http://www.gnu.org/licenses/. *
51 *------------------------------------------------------------------------
52 ** See the file ChangeLog for a revision history. */
59 #else /* not HAVE_STRING_H */
61 #endif /* not HAVE_STRING_H */
67 int WhitePiece P((ChessSquare));
68 int BlackPiece P((ChessSquare));
69 int SameColor P((ChessSquare, ChessSquare));
70 int PosFlags(int index);
72 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
78 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
84 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
87 int SameColor(piece1, piece2)
88 ChessSquare piece1, piece2;
90 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
91 (int) piece1 < (int) BlackPawn &&
92 (int) piece2 >= (int) WhitePawn &&
93 (int) piece2 < (int) BlackPawn)
94 || ((int) piece1 >= (int) BlackPawn &&
95 (int) piece1 < (int) EmptySquare &&
96 (int) piece2 >= (int) BlackPawn &&
97 (int) piece2 < (int) EmptySquare);
100 char pieceToChar[] = {
101 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
102 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
103 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
104 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
106 char pieceNickName[EmptySquare];
111 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
112 return pieceToChar[(int) p];
115 int PieceToNumber(p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
119 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
121 while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
125 ChessSquare CharToPiece(c)
129 if(c == '.') return EmptySquare;
130 for(i=0; i< (int) EmptySquare; i++)
131 if(pieceNickName[i] == c) return (ChessSquare) i;
132 for(i=0; i< (int) EmptySquare; i++)
133 if(pieceToChar[i] == c) return (ChessSquare) i;
137 void CopyBoard(to, from)
142 for (i = 0; i < BOARD_HEIGHT; i++)
143 for (j = 0; j < BOARD_WIDTH; j++)
144 to[i][j] = from[i][j];
145 for (j = 0; j < BOARD_FILES-1; j++) // [HGM] gamestate: copy castling rights and ep status
146 to[CASTLING][j] = from[CASTLING][j];
147 to[HOLDINGS_SET] = 0; // flag used in ICS play
150 int CompareBoards(board1, board2)
151 Board board1, board2;
155 for (i = 0; i < BOARD_HEIGHT; i++)
156 for (j = 0; j < BOARD_WIDTH; j++) {
157 if (board1[i][j] != board2[i][j])
164 /* Call callback once for each pseudo-legal move in the given
165 position, except castling moves. A move is pseudo-legal if it is
166 legal, or if it would be legal except that it leaves the king in
167 check. In the arguments, epfile is EP_NONE if the previous move
168 was not a double pawn push, or the file 0..7 if it was, or
169 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
170 Promotion moves generated are to Queen only.
172 void GenPseudoLegal(board, flags, callback, closure)
175 MoveCallback callback;
179 int i, j, d, s, fs, rs, rt, ft, m;
180 int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
181 int promoRank = gameInfo.variant == VariantMakruk ? 3 : 1;
183 for (rf = 0; rf < BOARD_HEIGHT; rf++)
184 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
186 int rookRange = 1000;
188 if (flags & F_WHITE_ON_MOVE) {
189 if (!WhitePiece(board[rf][ff])) continue;
191 if (!BlackPiece(board[rf][ff])) continue;
193 m = 0; piece = board[rf][ff];
194 if(PieceToChar(piece) == '~')
195 piece = (ChessSquare) ( DEMOTED piece );
196 if(gameInfo.variant == VariantShogi)
197 piece = (ChessSquare) ( SHOGI piece );
200 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
202 /* can't happen ([HGM] except for faries...) */
206 if(gameInfo.variant == VariantXiangqi) {
207 /* [HGM] capture and move straight ahead in Xiangqi */
208 if (rf < BOARD_HEIGHT-1 &&
209 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
210 callback(board, flags, NormalMove,
211 rf, ff, rf + 1, ff, closure);
213 /* and move sideways when across the river */
214 for (s = -1; s <= 1; s += 2) {
215 if (rf >= BOARD_HEIGHT>>1 &&
216 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
217 !WhitePiece(board[rf][ff+s]) ) {
218 callback(board, flags, NormalMove,
219 rf, ff, rf, ff+s, closure);
224 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
225 callback(board, flags,
226 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
227 rf, ff, rf + 1, ff, closure);
229 if (rf == 1 && board[2][ff] == EmptySquare &&
230 gameInfo.variant != VariantShatranj && /* [HGM] */
231 gameInfo.variant != VariantCourier && /* [HGM] */
232 board[3][ff] == EmptySquare ) {
233 callback(board, flags, NormalMove,
234 rf, ff, 3, ff, closure);
236 for (s = -1; s <= 1; s += 2) {
237 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
238 ((flags & F_KRIEGSPIEL_CAPTURE) ||
239 BlackPiece(board[rf + 1][ff + s]))) {
240 callback(board, flags,
241 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
242 rf, ff, rf + 1, ff + s, closure);
244 if (rf == BOARD_HEIGHT-4) {
245 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
246 (epfile == ff + s || epfile == EP_UNKNOWN) &&
247 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
248 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
249 callback(board, flags, WhiteCapturesEnPassant,
250 rf, ff, 5, ff + s, closure);
257 if(gameInfo.variant == VariantXiangqi) {
258 /* [HGM] capture straight ahead in Xiangqi */
259 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
260 callback(board, flags, NormalMove,
261 rf, ff, rf - 1, ff, closure);
263 /* and move sideways when across the river */
264 for (s = -1; s <= 1; s += 2) {
265 if (rf < BOARD_HEIGHT>>1 &&
266 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
267 !BlackPiece(board[rf][ff+s]) ) {
268 callback(board, flags, NormalMove,
269 rf, ff, rf, ff+s, closure);
274 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
275 callback(board, flags,
276 rf <= promoRank ? BlackPromotion : NormalMove,
277 rf, ff, rf - 1, ff, closure);
279 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
280 gameInfo.variant != VariantShatranj && /* [HGM] */
281 gameInfo.variant != VariantCourier && /* [HGM] */
282 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
283 callback(board, flags, NormalMove,
284 rf, ff, BOARD_HEIGHT-4, ff, closure);
286 for (s = -1; s <= 1; s += 2) {
287 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
288 ((flags & F_KRIEGSPIEL_CAPTURE) ||
289 WhitePiece(board[rf - 1][ff + s]))) {
290 callback(board, flags,
291 rf <= promoRank ? BlackPromotion : NormalMove,
292 rf, ff, rf - 1, ff + s, closure);
295 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
296 (epfile == ff + s || epfile == EP_UNKNOWN) &&
297 board[3][ff + s] == WhitePawn &&
298 board[2][ff + s] == EmptySquare) {
299 callback(board, flags, BlackCapturesEnPassant,
300 rf, ff, 2, ff + s, closure);
311 for (i = -1; i <= 1; i += 2)
312 for (j = -1; j <= 1; j += 2)
313 for (s = 1; s <= 2; s++) {
316 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
317 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
318 && !SameColor(board[rf][ff], board[rt][ft]))
319 callback(board, flags, NormalMove,
320 rf, ff, rt, ft, closure);
324 case SHOGI WhiteKnight:
325 for (s = -1; s <= 1; s += 2) {
326 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
327 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
328 callback(board, flags, NormalMove,
329 rf, ff, rf + 2, ff + s, closure);
334 case SHOGI BlackKnight:
335 for (s = -1; s <= 1; s += 2) {
336 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
337 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
338 callback(board, flags, NormalMove,
339 rf, ff, rf - 2, ff + s, closure);
346 for (d = 0; d <= 1; d++)
347 for (s = -1; s <= 1; s += 2) {
350 rt = rf + (i * s) * d;
351 ft = ff + (i * s) * (1 - d);
352 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
353 if (m == 0 && board[rt][ft] == EmptySquare)
354 callback(board, flags, NormalMove,
355 rf, ff, rt, ft, closure);
356 if (m == 1 && board[rt][ft] != EmptySquare &&
357 !SameColor(board[rf][ff], board[rt][ft]) )
358 callback(board, flags, NormalMove,
359 rf, ff, rt, ft, closure);
360 if (board[rt][ft] != EmptySquare && m++) break;
365 /* Gold General (and all its promoted versions) . First do the */
366 /* diagonal forward steps, then proceed as normal Wazir */
367 case SHOGI WhiteWazir:
368 case SHOGI (PROMOTED WhitePawn):
369 case SHOGI (PROMOTED WhiteKnight):
370 case SHOGI (PROMOTED WhiteQueen):
371 case SHOGI (PROMOTED WhiteFerz):
372 for (s = -1; s <= 1; s += 2) {
373 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
374 !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
375 callback(board, flags, NormalMove,
376 rf, ff, rf + 1, ff + s, closure);
381 case SHOGI BlackWazir:
382 case SHOGI (PROMOTED BlackPawn):
383 case SHOGI (PROMOTED BlackKnight):
384 case SHOGI (PROMOTED BlackQueen):
385 case SHOGI (PROMOTED BlackFerz):
386 for (s = -1; s <= 1; s += 2) {
387 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
388 !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
389 callback(board, flags, NormalMove,
390 rf, ff, rf - 1, ff + s, closure);
397 for (d = 0; d <= 1; d++)
398 for (s = -1; s <= 1; s += 2) {
400 ft = ff + s * (1 - d);
401 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
402 && !SameColor(board[rf][ff], board[rt][ft]) &&
403 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
404 callback(board, flags, NormalMove,
405 rf, ff, rt, ft, closure);
411 /* [HGM] support Shatranj pieces */
412 for (rs = -1; rs <= 1; rs += 2)
413 for (fs = -1; fs <= 1; fs += 2) {
416 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
417 && ( gameInfo.variant != VariantXiangqi ||
418 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
420 && !SameColor(board[rf][ff], board[rt][ft]))
421 callback(board, flags, NormalMove,
422 rf, ff, rt, ft, closure);
423 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier
424 || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
425 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
427 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
428 && !SameColor(board[rf][ff], board[rt][ft]))
429 callback(board, flags, NormalMove,
430 rf, ff, rt, ft, closure);
432 if(gameInfo.variant == VariantSpartan)
433 for(fs = -1; fs <= 1; fs += 2) {
435 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
436 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
440 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
443 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
444 for (s = -2; s <= 2; s += 4) {
446 ft = ff + s * (1 - d);
447 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
448 if (SameColor(board[rf][ff], board[rt][ft])) continue;
449 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
452 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
453 case SHOGI WhiteCardinal:
454 case SHOGI BlackCardinal:
457 /* Capablanca Archbishop continues as Knight */
462 /* Shogi Bishops are ordinary Bishops */
463 case SHOGI WhiteBishop:
464 case SHOGI BlackBishop:
467 for (rs = -1; rs <= 1; rs += 2)
468 for (fs = -1; fs <= 1; fs += 2)
472 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
473 if (SameColor(board[rf][ff], board[rt][ft])) break;
474 callback(board, flags, NormalMove,
475 rf, ff, rt, ft, closure);
476 if (board[rt][ft] != EmptySquare) break;
478 if(m==1) goto mounted;
479 if(m==2) goto finishGold;
480 /* Bishop falls through */
483 /* Shogi Lance is unlike anything, and asymmetric at that */
484 case SHOGI WhiteQueen:
488 if (rt >= BOARD_HEIGHT) break;
489 if (SameColor(board[rf][ff], board[rt][ft])) break;
490 callback(board, flags, NormalMove,
491 rf, ff, rt, ft, closure);
492 if (board[rt][ft] != EmptySquare) break;
496 case SHOGI BlackQueen:
501 if (SameColor(board[rf][ff], board[rt][ft])) break;
502 callback(board, flags, NormalMove,
503 rf, ff, rt, ft, closure);
504 if (board[rt][ft] != EmptySquare) break;
508 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
511 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
512 for (s = -2; s <= 2; s += 4) {
514 ft = ff + s * (1 - d);
515 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT || board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
516 if (SameColor(board[rf][ff], board[rt][ft])) continue;
517 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
519 if(gameInfo.variant == VariantSpartan) rookRange = 2; // in Spartan Chess restrict range to modern Dababba
522 /* Shogi Dragon King has to continue as Ferz after Rook moves */
523 case SHOGI WhiteDragon:
524 case SHOGI BlackDragon:
527 /* Capablanca Chancellor sets flag to continue as Knight */
531 m += (gameInfo.variant == VariantSpartan); // in Spartan Chess Chancellor is used for Dragon King.
533 /* Shogi Rooks are ordinary Rooks */
534 case SHOGI WhiteRook:
535 case SHOGI BlackRook:
539 for (d = 0; d <= 1; d++)
540 for (s = -1; s <= 1; s += 2)
542 rt = rf + (i * s) * d;
543 ft = ff + (i * s) * (1 - d);
544 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
545 if (SameColor(board[rf][ff], board[rt][ft])) break;
546 callback(board, flags, NormalMove,
547 rf, ff, rt, ft, closure);
548 if (board[rt][ft] != EmptySquare || i == rookRange) break;
550 if(m==1) goto mounted;
551 if(m==2) goto finishSilver;
556 for (rs = -1; rs <= 1; rs++)
557 for (fs = -1; fs <= 1; fs++) {
558 if (rs == 0 && fs == 0) continue;
562 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
563 if (SameColor(board[rf][ff], board[rt][ft])) break;
564 callback(board, flags, NormalMove,
565 rf, ff, rt, ft, closure);
566 if (board[rt][ft] != EmptySquare) break;
571 /* Shogi Pawn and Silver General: first the Pawn move, */
572 /* then the General continues like a Ferz */
574 if(gameInfo.variant != VariantMakruk) goto commoner;
575 case SHOGI WhitePawn:
576 case SHOGI WhiteFerz:
577 if (rf < BOARD_HEIGHT-1 &&
578 !SameColor(board[rf][ff], board[rf + 1][ff]) )
579 callback(board, flags, NormalMove,
580 rf, ff, rf + 1, ff, closure);
581 if(piece != SHOGI WhitePawn) goto finishSilver;
585 if(gameInfo.variant != VariantMakruk) goto commoner;
586 case SHOGI BlackPawn:
587 case SHOGI BlackFerz:
589 !SameColor(board[rf][ff], board[rf - 1][ff]) )
590 callback(board, flags, NormalMove,
591 rf, ff, rf - 1, ff, closure);
592 if(piece == SHOGI BlackPawn) break;
597 /* [HGM] support Shatranj pieces */
598 for (rs = -1; rs <= 1; rs += 2)
599 for (fs = -1; fs <= 1; fs += 2) {
602 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
603 if (!SameColor(board[rf][ff], board[rt][ft]) &&
604 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
605 callback(board, flags, NormalMove,
606 rf, ff, rt, ft, closure);
612 m++; // [HGM] superchess: use for Centaur
614 case SHOGI WhiteKing:
615 case SHOGI BlackKing:
619 for (i = -1; i <= 1; i++)
620 for (j = -1; j <= 1; j++) {
621 if (i == 0 && j == 0) continue;
624 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
625 if (SameColor(board[rf][ff], board[rt][ft])) continue;
626 callback(board, flags, NormalMove,
627 rf, ff, rt, ft, closure);
629 if(m==1) goto mounted;
632 case WhiteNightrider:
633 case BlackNightrider:
634 for (i = -1; i <= 1; i += 2)
635 for (j = -1; j <= 1; j += 2)
636 for (s = 1; s <= 2; s++) { int k;
640 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
641 if (SameColor(board[rf][ff], board[rt][ft])) break;
642 callback(board, flags, NormalMove,
643 rf, ff, rt, ft, closure);
644 if (board[rt][ft] != EmptySquare) break;
650 /* First do Bishop,then continue like Chancellor */
651 for (rs = -1; rs <= 1; rs += 2)
652 for (fs = -1; fs <= 1; fs += 2)
656 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
657 if (SameColor(board[rf][ff], board[rt][ft])) break;
658 callback(board, flags, NormalMove,
659 rf, ff, rt, ft, closure);
660 if (board[rt][ft] != EmptySquare) break;
665 // Use Lance as Berolina / Spartan Pawn.
667 if(gameInfo.variant == VariantSuper) goto Amazon;
668 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
669 callback(board, flags,
670 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
671 rf, ff, rf + 1, ff, closure);
672 for (s = -1; s <= 1; s += 2) {
673 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
674 callback(board, flags,
675 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
676 rf, ff, rf + 1, ff + s, closure);
677 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
678 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
683 if(gameInfo.variant == VariantSuper) goto Amazon;
684 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
685 callback(board, flags,
686 rf <= promoRank ? BlackPromotion : NormalMove,
687 rf, ff, rf - 1, ff, closure);
688 for (s = -1; s <= 1; s += 2) {
689 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
690 callback(board, flags,
691 rf <= promoRank ? BlackPromotion : NormalMove,
692 rf, ff, rf - 1, ff + s, closure);
693 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
694 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
698 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
702 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
715 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
716 int rf, int ff, int rt, int ft,
719 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
726 register GenLegalClosure *cl = (GenLegalClosure *) closure;
728 if (!(flags & F_IGNORE_CHECK) ) {
729 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
730 if(promo) board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
731 check = CheckTest(board, flags, rf, ff, rt, ft,
732 kind == WhiteCapturesEnPassant ||
733 kind == BlackCapturesEnPassant);
734 if(promo) board[rf][ff] = BlackLance;
737 if (flags & F_ATOMIC_CAPTURE) {
738 if (board[rt][ft] != EmptySquare ||
739 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
741 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
742 if (board[rf][ff] == king) return;
743 for (r = rt-1; r <= rt+1; r++) {
744 for (f = ft-1; f <= ft+1; f++) {
745 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
746 board[r][f] == king) return;
751 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
758 int captures; // [HGM] losers
759 } LegalityTestClosure;
762 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
763 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
764 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
765 moves that would destroy your own king. The CASTLE_OK flags are
766 true if castling is not yet ruled out by a move of the king or
767 rook. Return TRUE if the player on move is currently in check and
768 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
769 int GenLegal(board, flags, callback, closure)
772 MoveCallback callback;
776 int ff, ft, k, left, right, swap;
777 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
778 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
782 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl);
785 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
787 /* Generate castling moves */
788 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
789 wKing = WhiteUnicorn; bKing = BlackUnicorn;
792 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
793 if ((flags & F_WHITE_ON_MOVE) &&
794 (flags & F_WHITE_KCASTLE_OK) &&
795 board[0][ff] == wKing &&
796 board[0][ff + 1] == EmptySquare &&
797 board[0][ff + 2] == EmptySquare &&
798 board[0][BOARD_RGHT-3] == EmptySquare &&
799 board[0][BOARD_RGHT-2] == EmptySquare &&
800 board[0][BOARD_RGHT-1] == WhiteRook &&
801 castlingRights[0] != NoRights && /* [HGM] check rights */
802 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
804 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
805 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
806 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
807 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
809 callback(board, flags,
810 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
811 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
813 if ((flags & F_WHITE_ON_MOVE) &&
814 (flags & F_WHITE_QCASTLE_OK) &&
815 board[0][ff] == wKing &&
816 board[0][ff - 1] == EmptySquare &&
817 board[0][ff - 2] == EmptySquare &&
818 board[0][BOARD_LEFT+2] == EmptySquare &&
819 board[0][BOARD_LEFT+1] == EmptySquare &&
820 board[0][BOARD_LEFT+0] == WhiteRook &&
821 castlingRights[1] != NoRights && /* [HGM] check rights */
822 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
824 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
825 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
826 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
828 callback(board, flags,
829 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
830 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
832 if (!(flags & F_WHITE_ON_MOVE) &&
833 (flags & F_BLACK_KCASTLE_OK) &&
834 board[BOARD_HEIGHT-1][ff] == bKing &&
835 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
836 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
837 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
838 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
839 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
840 castlingRights[3] != NoRights && /* [HGM] check rights */
841 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
843 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
844 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
845 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
846 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
848 callback(board, flags,
849 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
850 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
852 if (!(flags & F_WHITE_ON_MOVE) &&
853 (flags & F_BLACK_QCASTLE_OK) &&
854 board[BOARD_HEIGHT-1][ff] == bKing &&
855 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
856 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
857 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
858 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
859 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
860 castlingRights[4] != NoRights && /* [HGM] check rights */
861 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
863 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
864 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
865 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
867 callback(board, flags,
868 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
869 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
873 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
875 /* generate all potential FRC castling moves (KxR), ignoring flags */
876 /* [HGM] test if the Rooks we find have castling rights */
877 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
880 if ((flags & F_WHITE_ON_MOVE) != 0) {
881 ff = castlingRights[2]; /* King file if we have any rights */
882 if(ff != NoRights && board[0][ff] == WhiteKing) {
883 if (appData.debugMode) {
884 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
885 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
887 ft = castlingRights[0]; /* Rook file if we have H-side rights */
889 right = BOARD_RGHT-2;
890 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
891 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
892 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
893 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
894 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
895 if(ft != NoRights && board[0][ft] == WhiteRook)
896 callback(board, flags, WhiteHSideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
898 ft = castlingRights[1]; /* Rook file if we have A-side rights */
901 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
902 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
903 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
904 if(ff > BOARD_LEFT+2)
905 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
906 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
907 if(ft != NoRights && board[0][ft] == WhiteRook)
908 callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
911 ff = castlingRights[5]; /* King file if we have any rights */
912 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
913 ft = castlingRights[3]; /* Rook file if we have H-side rights */
915 right = BOARD_RGHT-2;
916 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
917 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
918 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
919 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
920 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
921 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
922 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
924 ft = castlingRights[4]; /* Rook file if we have A-side rights */
927 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
928 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
929 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
930 if(ff > BOARD_LEFT+2)
931 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
932 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
933 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
934 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
950 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
951 int rf, int ff, int rt, int ft,
955 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
962 register CheckTestClosure *cl = (CheckTestClosure *) closure;
964 if (rt == cl->rking && ft == cl->fking) cl->check++;
968 /* If the player on move were to move from (rf, ff) to (rt, ft), would
969 he leave himself in check? Or if rf == -1, is the player on move
970 in check now? enPassant must be TRUE if the indicated move is an
971 e.p. capture. The possibility of castling out of a check along the
972 back rank is not accounted for (i.e., we still return nonzero), as
973 this is illegal anyway. Return value is the number of times the
975 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
978 int rf, ff, rt, ft, enPassant;
981 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
982 ChessSquare captured = EmptySquare;
983 /* Suppress warnings on uninitialized variables */
985 if(gameInfo.variant == VariantXiangqi)
986 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
987 if(gameInfo.variant == VariantKnightmate)
988 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
992 captured = board[rf][ft];
993 board[rf][ft] = EmptySquare;
995 captured = board[rt][ft];
997 board[rt][ft] = board[rf][ff];
998 board[rf][ff] = EmptySquare;
999 } else board[rt][ft] = ff; // [HGM] drop
1001 /* For compatibility with ICS wild 9, we scan the board in the
1002 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1003 and we test only whether that one is in check. */
1004 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1005 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1006 if (board[cl.rking][cl.fking] == king) {
1008 if(gameInfo.variant == VariantXiangqi) {
1009 /* [HGM] In Xiangqi opposing Kings means check as well */
1011 dir = (king >= BlackPawn) ? -1 : 1;
1012 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1013 board[i][cl.fking] == EmptySquare; i+=dir );
1014 if(i>=0 && i<BOARD_HEIGHT &&
1015 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1018 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
1019 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1020 goto undo_move; /* 2-level break */
1027 board[rf][ff] = board[rt][ft];
1029 board[rf][ft] = captured;
1030 board[rt][ft] = EmptySquare;
1032 board[rt][ft] = captured;
1034 } else board[rt][ft] = EmptySquare; // [HGM] drop
1036 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1039 ChessMove LegalDrop(board, flags, piece, rt, ft)
1044 { // [HGM] put drop legality testing in separate routine for clarity
1046 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1047 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1048 n = PieceToNumber(piece);
1049 if(gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1050 return ImpossibleMove; // piece not available
1051 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1052 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1053 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1054 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1055 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1056 if(piece == WhitePawn || piece == BlackPawn) {
1058 for(r=1; r<BOARD_HEIGHT-1; r++)
1059 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
1060 // should still test if we mate with this Pawn
1062 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1063 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1065 if( (piece == WhitePawn || piece == BlackPawn) &&
1066 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1067 return IllegalMove; /* no pawn drops on 1st/8th */
1069 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1070 if (!(flags & F_IGNORE_CHECK) &&
1071 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1072 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1075 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1076 int rf, int ff, int rt, int ft,
1079 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1086 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1088 // if (appData.debugMode) {
1089 // fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1091 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1092 cl->captures++; // [HGM] losers: count legal captures
1093 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1097 ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
1100 int rf, ff, rt, ft, promoChar;
1102 LegalityTestClosure cl; ChessSquare piece, *castlingRights = board[CASTLING];
1104 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1105 piece = board[rf][ff];
1107 if (appData.debugMode) {
1109 for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
1110 fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1112 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1113 /* (perhaps we should disallow moves that obviously leave us in check?) */
1114 if(piece == WhiteFalcon || piece == BlackFalcon ||
1115 piece == WhiteCobra || piece == BlackCobra)
1116 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1122 cl.kind = IllegalMove;
1123 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1124 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl);
1125 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1126 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1127 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1129 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1130 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1131 if(board[rf][ff] < BlackPawn) { // white
1132 if(rf != 0) return IllegalMove; // must be on back rank
1133 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1135 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1136 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1139 if(gameInfo.variant == VariantShogi) {
1140 /* [HGM] Shogi promotions. '=' means defer */
1141 if(rf != DROP_RANK && cl.kind == NormalMove) {
1142 ChessSquare piece = board[rf][ff];
1144 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1145 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1146 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1147 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1148 promoChar = '+'; // allowed ICS notations
1149 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1150 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1151 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1152 else if(flags & F_WHITE_ON_MOVE) {
1153 if( (int) piece < (int) WhiteWazir &&
1154 (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
1155 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1156 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1157 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1158 else /* promotion optional, default is defer */
1159 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1160 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1162 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1163 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1164 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1165 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1166 else /* promotion optional, default is defer */
1167 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1168 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1172 if (promoChar != NULLCHAR) {
1173 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1174 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1175 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1176 if(piece == EmptySquare)
1177 cl.kind = ImpossibleMove; // non-existing piece
1178 if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1179 if(promoChar != PieceToChar(BlackKing)) {
1180 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1181 if(piece == BlackLance) cl.kind = ImpossibleMove;
1182 } else { // promotion to King allowed only if we do not haave two yet
1183 int r, f, kings = 0;
1184 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1185 if(kings == 2) cl.kind = IllegalMove;
1187 } else if(piece == WhitePawn || piece == BlackPawn) cl.kind = ImpossibleMove; // cannot stay Pawn in any variant
1188 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1189 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1190 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1191 cl.kind = IllegalMove; // promotion to King usually not allowed
1193 cl.kind = IllegalMove;
1203 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1204 int rf, int ff, int rt, int ft,
1207 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1214 register MateTestClosure *cl = (MateTestClosure *) closure;
1219 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1220 int MateTest(board, flags)
1225 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1226 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1228 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1229 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1230 nrKing += (board[r][f] == king); // stm has king
1231 if( board[r][f] != EmptySquare ) {
1232 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1237 if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
1238 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1239 case VariantShatranj:
1240 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1244 if(nrKing == 0) return MT_NOKING;
1247 if(myPieces == 1) return MT_BARE;
1250 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl);
1251 // [HGM] 3check: yet to do!
1253 return inCheck ? MT_CHECK : MT_NONE;
1255 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat) { // drop game
1256 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1257 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1258 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1259 if(board[n][holdings] != EmptySquare) {
1260 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1261 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1264 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1265 return myPieces == hisPieces ? MT_STALEMATE :
1266 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1267 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1268 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1270 return inCheck ? MT_CHECKMATE
1271 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
1272 MT_STAINMATE : MT_STALEMATE;
1277 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1278 int rf, int ff, int rt, int ft,
1281 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
1288 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1289 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1291 // [HGM] wild: for wild-card pieces rt and rf are dummies
1292 if(piece == WhiteFalcon || piece == BlackFalcon ||
1293 piece == WhiteCobra || piece == BlackCobra)
1296 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1297 || PieceToChar(board[rf][ff]) == '~'
1298 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1300 (cl->rfIn == -1 || cl->rfIn == rf) &&
1301 (cl->ffIn == -1 || cl->ffIn == ff) &&
1302 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1303 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1306 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1307 // [HGM] oneclick: if multiple moves, be sure we remember capture
1308 cl->piece = board[rf][ff];
1311 cl->rt = wildCard ? cl->rtIn : rt;
1312 cl->ft = wildCard ? cl->ftIn : ft;
1315 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1319 void Disambiguate(board, flags, closure)
1322 DisambiguateClosure *closure;
1324 int illegal = 0; char c = closure->promoCharIn;
1326 closure->count = closure->captures = 0;
1327 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1328 closure->kind = ImpossibleMove;
1329 if (appData.debugMode) {
1330 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1331 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1332 closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
1334 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure);
1335 if (closure->count == 0) {
1336 /* See if it's an illegal move due to check */
1338 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure);
1339 if (closure->count == 0) {
1340 /* No, it's not even that */
1341 if (appData.debugMode) { int i, j;
1342 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1343 for(j=0; j<BOARD_WIDTH; j++)
1344 fprintf(debugFP, "%3d", (int) board[i][j]);
1345 fprintf(debugFP, "\n");
1352 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1353 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1354 if(closure->piece < BlackPawn) { // white
1355 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1356 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1358 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1359 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1362 if(gameInfo.variant == VariantShogi) {
1363 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1364 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1365 ChessSquare piece = closure->piece;
1366 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1367 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1368 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1369 c = '+'; // allowed ICS notations
1370 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1371 else if(flags & F_WHITE_ON_MOVE) {
1372 if( (int) piece < (int) WhiteWazir &&
1373 (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1374 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1375 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1376 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1377 else /* promotion optional, default is defer */
1378 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1379 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1381 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1382 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1383 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1384 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1385 else /* promotion optional, default is defer */
1386 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1387 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1390 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1391 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1393 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1394 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1395 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
1396 c = PieceToChar(BlackFerz);
1397 else if(gameInfo.variant == VariantGreat)
1398 c = PieceToChar(BlackMan);
1400 c = PieceToChar(BlackQueen);
1401 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1402 } else if (c != NULLCHAR) closure->kind = IllegalMove;
1404 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1405 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1406 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1407 if (closure->count > 1) {
1408 closure->kind = AmbiguousMove;
1411 /* Note: If more than one illegal move matches, but no legal
1412 moves, we return IllegalMove, not AmbiguousMove. Caller
1413 can look at closure->count to detect this.
1415 closure->kind = IllegalMove;
1417 if (appData.debugMode) {
1418 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1419 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
1420 closure->promoChar >= ' ' ? closure->promoChar:'-');
1434 } CoordsToAlgebraicClosure;
1436 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1437 ChessMove kind, int rf, int ff,
1438 int rt, int ft, VOIDSTAR closure));
1440 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
1447 register CoordsToAlgebraicClosure *cl =
1448 (CoordsToAlgebraicClosure *) closure;
1450 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1451 (board[rf][ff] == cl->piece
1452 || PieceToChar(board[rf][ff]) == '~' &&
1453 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1457 cl->kind = kind; /* this is the move we want */
1459 cl->file++; /* need file to rule out this move */
1463 cl->rank++; /* need rank to rule out this move */
1465 cl->either++; /* rank or file will rule out this move */
1471 /* Convert coordinates to normal algebraic notation.
1472 promoChar must be NULLCHAR or 'x' if not a promotion.
1474 ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
1483 char *outp = out, c;
1484 CoordsToAlgebraicClosure cl;
1486 if (rf == DROP_RANK) {
1487 /* Bughouse piece drop */
1488 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1493 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1495 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1498 if (promoChar == 'x') promoChar = NULLCHAR;
1499 piece = board[rf][ff];
1500 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1502 if (appData.debugMode)
1503 fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
1507 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1508 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1509 /* Keep short notation if move is illegal only because it
1510 leaves the player in check, but still return IllegalMove */
1511 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1512 if (kind == IllegalMove) break;
1517 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1518 /* Non-capture; use style "e5" */
1521 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1523 /* Capture; use style "exd5" */
1524 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1525 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
1529 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1531 /* Use promotion suffix style "=Q" */
1533 if (appData.debugMode)
1534 fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
1535 if (promoChar != NULLCHAR) {
1536 if(gameInfo.variant == VariantShogi) {
1537 /* [HGM] ... but not in Shogi! */
1538 *outp++ = promoChar == '=' ? '=' : '+';
1541 *outp++ = ToUpper(promoChar);
1550 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1551 /* Code added by Tord: FRC castling. */
1552 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1553 (piece == BlackKing && board[rt][ft] == BlackRook)) {
1555 safeStrCpy(out, "O-O", MOVE_LEN);
1557 safeStrCpy(out, "O-O-O", MOVE_LEN);
1558 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1560 /* End of code added by Tord */
1561 /* Test for castling or ICS wild castling */
1562 /* Use style "O-O" (oh-oh) for PGN compatibility */
1563 else if (rf == rt &&
1564 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1565 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
1566 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1567 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1568 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1569 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1571 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1573 /* This notation is always unambiguous, unless there are
1574 kings on both the d and e files, with "wild castling"
1575 possible for the king on the d file and normal castling
1576 possible for the other. ICS rules for wild 9
1577 effectively make castling illegal for either king in
1578 this situation. So I am not going to worry about it;
1579 I'll just generate an ambiguous O-O in this case.
1581 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1584 /* else fall through */
1592 cl.kind = IllegalMove;
1593 cl.rank = cl.file = cl.either = 0;
1594 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1596 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1597 /* Generate pretty moves for moving into check, but
1598 still return IllegalMove.
1600 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1601 if (cl.kind == IllegalMove) break;
1602 cl.kind = IllegalMove;
1605 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1606 else "Ngf3" or "Ngxf7",
1607 else "N1f3" or "N5xf7",
1608 else "Ng1f3" or "Ng5xf7".
1610 c = PieceToChar(piece) ;
1611 if( c == '~' || c == '+') {
1612 /* [HGM] print nonexistent piece as its demoted version */
1613 piece = (ChessSquare) (DEMOTED piece);
1615 if(c=='+') *outp++ = c;
1616 *outp++ = ToUpper(PieceToChar(piece));
1618 if (cl.file || (cl.either && !cl.rank)) {
1624 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1627 if(board[rt][ft] != EmptySquare)
1633 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1634 if (gameInfo.variant == VariantShogi) {
1635 /* [HGM] in Shogi non-pawns can promote */
1636 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
1638 else if (gameInfo.variant != VariantSuper && promoChar &&
1639 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
1641 *outp++ = ToUpper(promoChar);
1643 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
1645 *outp++ = ToUpper(promoChar);
1651 /* Moving a nonexistent piece */
1655 /* Not a legal move, even ignoring check.
1656 If there was a piece on the from square,
1657 use style "Ng1g3" or "Ng1xe8";
1658 if there was a pawn or nothing (!),
1659 use style "g1g3" or "g1xe8". Use "x"
1660 if a piece was on the to square, even
1661 a piece of the same color.
1665 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1667 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
1668 c += (board[r][f] == piece); // count on-board pieces of given type
1669 *outp++ = ToUpper(PieceToChar(piece));
1671 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
1675 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1677 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1681 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1682 /* Use promotion suffix style "=Q" */
1683 if (promoChar != NULLCHAR && promoChar != 'x') {
1685 *outp++ = ToUpper(promoChar);
1692 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1701 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1703 int preyStackPointer, chaseStackPointer;
1706 char rf, ff, rt, ft;
1716 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1718 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1719 int rf, int ff, int rt, int ft,
1722 void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1728 { // For adding captures that can lead to chase indictment to the chaseStack
1729 if(board[rt][ft] == EmptySquare) return; // non-capture
1730 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
1731 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
1732 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
1733 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1734 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1735 chaseStack[chaseStackPointer].rf = rf;
1736 chaseStack[chaseStackPointer].ff = ff;
1737 chaseStack[chaseStackPointer].rt = rt;
1738 chaseStack[chaseStackPointer].ft = ft;
1739 chaseStackPointer++;
1742 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1743 int rf, int ff, int rt, int ft,
1746 void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1752 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1754 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1756 if(board[rt][ft] == EmptySquare) return; // no capture
1757 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1758 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
1760 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1761 for(i=0; i<chaseStackPointer; i++) {
1762 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1763 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
1764 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1765 chaseStack[i] = chaseStack[--chaseStackPointer];
1771 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1772 int rf, int ff, int rt, int ft,
1775 void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
1781 { // for determining if a piece (given through the closure) is protected
1782 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1784 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
1785 if(appData.debugMode && board[rt][ft] != EmptySquare)
1786 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1789 extern char moveList[MAX_MOVES][MOVE_LEN];
1791 int PerpetualChase(int first, int last)
1792 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1795 ChessSquare captured;
1797 preyStackPointer = 0; // clear stack of chased pieces
1798 for(i=first; i<last; i+=2) { // for all positions with same side to move
1799 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1800 chaseStackPointer = 0; // clear stack that is going to hold possible chases
1801 // determine all captures possible after the move, and put them on chaseStack
1802 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl);
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, ": all capts\n");
1809 // determine all captures possible before the move, and delete them from chaseStack
1810 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1811 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1812 cl.rt = moveList[i][3]-ONE;
1813 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1814 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl);
1815 if(appData.debugMode) { int n;
1816 for(n=0; n<chaseStackPointer; n++)
1817 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1818 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1819 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1821 // chaseSack now contains all captures made possible by the move
1822 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1823 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1824 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1826 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1827 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
1829 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1830 continue; // C or H attack on R is always chase; leave on chaseStack
1832 if(attacker == victim) {
1833 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
1834 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1835 // we can capture back with equal piece, so this is no chase but a sacrifice
1836 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1837 j--; /* ! */ continue;
1842 // the attack is on a lower piece, or on a pinned or blocked equal one
1843 // test if the victim is protected by a true protector. First make the capture.
1844 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1845 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1846 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1847 // Then test if the opponent can recapture
1848 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
1849 cl.rt = chaseStack[j].rt;
1850 cl.ft = chaseStack[j].ft;
1851 if(appData.debugMode) {
1852 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1854 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl); // try all moves
1855 // unmake the capture
1856 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1857 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1858 // if a recapture was found, piece is protected, and we are not chasing it.
1859 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1860 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1864 // chaseStack now contains all moves that chased
1865 if(appData.debugMode) { int n;
1866 for(n=0; n<chaseStackPointer; n++)
1867 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1868 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1869 fprintf(debugFP, ": chases\n");
1871 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1872 for(j=0; j<chaseStackPointer; j++) {
1873 preyStack[j].rank = chaseStack[j].rt;
1874 preyStack[j].file = chaseStack[j].ft;
1876 preyStackPointer = chaseStackPointer;
1879 for(j=0; j<chaseStackPointer; j++) {
1880 for(k=0; k<preyStackPointer; k++) {
1881 // search the victim of each chase move on the preyStack (first occurrence)
1882 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1883 if(k < tail) break; // piece was already identified as still being chased
1884 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1885 preyStack[tail] = preyStack[k]; // by swapping
1886 preyStack[k] = preyStack[preyStackPointer];
1892 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1893 if(appData.debugMode) { int n;
1894 for(n=0; n<preyStackPointer; n++)
1895 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1896 fprintf(debugFP, "always chased upto ply %d\n", i);
1898 // now adjust the location of the chased pieces according to opponent move
1899 for(j=0; j<preyStackPointer; j++) {
1900 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1901 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1902 preyStack[j].rank = moveList[i+1][3]-ONE;
1903 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1908 return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased