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, 2012, 2013, 2014 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 */
76 WhitePiece (ChessSquare piece)
78 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
82 BlackPiece (ChessSquare piece)
84 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
89 SameColor (ChessSquare piece1, ChessSquare piece2)
91 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
92 (int) piece1 < (int) BlackPawn &&
93 (int) piece2 >= (int) WhitePawn &&
94 (int) piece2 < (int) BlackPawn)
95 || ((int) piece1 >= (int) BlackPawn &&
96 (int) piece1 < (int) EmptySquare &&
97 (int) piece2 >= (int) BlackPawn &&
98 (int) piece2 < (int) EmptySquare);
101 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
104 char pieceToChar[] = {
105 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
106 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
107 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
108 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
110 char pieceNickName[EmptySquare];
113 PieceToChar (ChessSquare p)
115 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
116 return pieceToChar[(int) p];
120 PieceToNumber (ChessSquare p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
123 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
125 while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
133 if(c == '.') return EmptySquare;
134 for(i=0; i< (int) EmptySquare; i++)
135 if(pieceNickName[i] == c) return (ChessSquare) i;
136 for(i=0; i< (int) EmptySquare; i++)
137 if(pieceToChar[i] == c) return (ChessSquare) i;
142 CopyBoard (Board to, Board from)
146 for (i = 0; i < BOARD_HEIGHT; i++)
147 for (j = 0; j < BOARD_WIDTH; j++)
148 to[i][j] = from[i][j];
149 for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
150 to[VIRGIN][j] = from[VIRGIN][j],
151 to[CASTLING][j] = from[CASTLING][j];
152 to[HOLDINGS_SET] = 0; // flag used in ICS play
156 CompareBoards (Board board1, Board board2)
160 for (i = 0; i < BOARD_HEIGHT; i++)
161 for (j = 0; j < BOARD_WIDTH; j++) {
162 if (board1[i][j] != board2[i][j])
168 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
171 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
176 if (rt >= BOARD_HEIGHT) break;
177 if (SameColor(board[rf][ff], board[rt][ft])) break;
178 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
179 if (board[rt][ft] != EmptySquare) break;
184 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
190 if (SameColor(board[rf][ff], board[rt][ft])) break;
191 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
192 if (board[rt][ft] != EmptySquare) break;
197 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
199 SlideForward(board, flags, rf, ff, callback, closure);
200 SlideBackward(board, flags, rf, ff, callback, closure);
204 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
206 int i, s, rt = rf, ft;
207 for(s = -1; s <= 1; s+= 2) {
210 if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
211 if (SameColor(board[rf][ff], board[rt][ft])) break;
212 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
213 if (board[rt][ft] != EmptySquare) break;
219 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
222 for(s = -1; s <= 1; s+= 2) {
226 if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
227 if (SameColor(board[rf][ff], board[rt][ft])) break;
228 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
229 if (board[rt][ft] != EmptySquare) break;
235 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
238 for(s = -1; s <= 1; s+= 2) {
242 if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
243 if (SameColor(board[rf][ff], board[rt][ft])) break;
244 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
245 if (board[rt][ft] != EmptySquare) break;
251 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
253 SlideVertical(board, flags, rf, ff, callback, closure);
254 SlideSideways(board, flags, rf, ff, callback, closure);
258 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
260 SlideDiagForward(board, flags, rf, ff, callback, closure);
261 SlideDiagBackward(board, flags, rf, ff, callback, closure);
265 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
266 { // Lion-like move of Horned Falcon and Souring Eagle
267 int ft = ff + dx, rt = rf + dy;
268 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
269 if (!SameColor(board[rf][ff], board[rt][ft]))
270 callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
272 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
273 if (!SameColor(board[rf][ff], board[rt][ft]))
274 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
278 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
280 int ft = ff, rt = rf + 1;
281 if (rt >= BOARD_HEIGHT) return;
282 if (SameColor(board[rf][ff], board[rt][ft])) return;
283 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
287 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
289 int ft = ff, rt = rf - 1;
291 if (SameColor(board[rf][ff], board[rt][ft])) return;
292 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
296 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
300 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
301 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
303 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
304 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
308 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
311 if (rt >= BOARD_HEIGHT) return;
313 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
314 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
316 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
317 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
321 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
326 if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
327 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
329 if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
330 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
334 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
336 StepForward(board, flags, rf, ff, callback, closure);
337 StepBackward(board, flags, rf, ff, callback, closure);
341 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
343 StepDiagForward(board, flags, rf, ff, callback, closure);
344 StepDiagBackward(board, flags, rf, ff, callback, closure);
348 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
350 StepVertical(board, flags, rf, ff, callback, closure);
351 StepSideways(board, flags, rf, ff, callback, closure);
355 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
358 for (i = -1; i <= 1; i += 2)
359 for (j = -1; j <= 1; j += 2)
360 for (s = 1; s <= 2; s++) {
363 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
364 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
365 && !SameColor(board[rf][ff], board[rt][ft]))
366 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
370 /* Call callback once for each pseudo-legal move in the given
371 position, except castling moves. A move is pseudo-legal if it is
372 legal, or if it would be legal except that it leaves the king in
373 check. In the arguments, epfile is EP_NONE if the previous move
374 was not a double pawn push, or the file 0..7 if it was, or
375 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
376 Promotion moves generated are to Queen only.
379 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
380 // speed: only do moves with this piece type
383 int i, j, d, s, fs, rs, rt, ft, m;
384 int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
385 int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
387 for (rf = 0; rf < BOARD_HEIGHT; rf++)
388 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
391 if(board[rf][ff] == EmptySquare) continue;
392 if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
393 m = 0; piece = board[rf][ff];
394 if(PieceToChar(piece) == '~')
395 piece = (ChessSquare) ( DEMOTED piece );
396 if(filter != EmptySquare && piece != filter) continue;
397 if(IS_SHOGI(gameInfo.variant))
398 piece = (ChessSquare) ( SHOGI piece );
400 switch ((int)piece) {
401 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
403 /* can't happen ([HGM] except for faries...) */
407 if(gameInfo.variant == VariantXiangqi) {
408 /* [HGM] capture and move straight ahead in Xiangqi */
409 if (rf < BOARD_HEIGHT-1 &&
410 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
411 callback(board, flags, NormalMove,
412 rf, ff, rf + 1, ff, closure);
414 /* and move sideways when across the river */
415 for (s = -1; s <= 1; s += 2) {
416 if (rf >= BOARD_HEIGHT>>1 &&
417 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
418 !WhitePiece(board[rf][ff+s]) ) {
419 callback(board, flags, NormalMove,
420 rf, ff, rf, ff+s, closure);
425 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
426 callback(board, flags,
427 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
428 rf, ff, rf + 1, ff, closure);
430 if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
431 gameInfo.variant != VariantShatranj && /* [HGM] */
432 gameInfo.variant != VariantCourier && /* [HGM] */
433 board[rf+2][ff] == EmptySquare ) {
434 callback(board, flags, NormalMove,
435 rf, ff, rf+2, ff, closure);
437 for (s = -1; s <= 1; s += 2) {
438 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
439 ((flags & F_KRIEGSPIEL_CAPTURE) ||
440 BlackPiece(board[rf + 1][ff + s]))) {
441 callback(board, flags,
442 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
443 rf, ff, rf + 1, ff + s, closure);
445 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
446 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
447 (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
448 board[rf][ff + s] == BlackPawn &&
449 board[rf+1][ff + s] == EmptySquare) {
450 callback(board, flags, WhiteCapturesEnPassant,
451 rf, ff, rf+1, ff + s, closure);
458 if(gameInfo.variant == VariantXiangqi) {
459 /* [HGM] capture straight ahead in Xiangqi */
460 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
461 callback(board, flags, NormalMove,
462 rf, ff, rf - 1, ff, closure);
464 /* and move sideways when across the river */
465 for (s = -1; s <= 1; s += 2) {
466 if (rf < BOARD_HEIGHT>>1 &&
467 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
468 !BlackPiece(board[rf][ff+s]) ) {
469 callback(board, flags, NormalMove,
470 rf, ff, rf, ff+s, closure);
475 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
476 callback(board, flags,
477 rf <= promoRank ? BlackPromotion : NormalMove,
478 rf, ff, rf - 1, ff, closure);
480 if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
481 gameInfo.variant != VariantShatranj && /* [HGM] */
482 gameInfo.variant != VariantCourier && /* [HGM] */
483 board[rf-2][ff] == EmptySquare) {
484 callback(board, flags, NormalMove,
485 rf, ff, rf-2, ff, closure);
487 for (s = -1; s <= 1; s += 2) {
488 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
489 ((flags & F_KRIEGSPIEL_CAPTURE) ||
490 WhitePiece(board[rf - 1][ff + s]))) {
491 callback(board, flags,
492 rf <= promoRank ? BlackPromotion : NormalMove,
493 rf, ff, rf - 1, ff + s, closure);
495 if (rf < BOARD_HEIGHT>>1) {
496 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
497 (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
498 board[rf][ff + s] == WhitePawn &&
499 board[rf-1][ff + s] == EmptySquare) {
500 callback(board, flags, BlackCapturesEnPassant,
501 rf, ff, rf-1, ff + s, closure);
511 for (i = -1; i <= 1; i += 2)
512 for (j = -1; j <= 1; j += 2)
513 for (s = 1; s <= 2; s++) {
516 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
517 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
518 && !SameColor(board[rf][ff], board[rt][ft]))
519 callback(board, flags, NormalMove,
520 rf, ff, rt, ft, closure);
524 case SHOGI WhiteKnight:
525 for (s = -1; s <= 1; s += 2) {
526 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
527 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
528 callback(board, flags, NormalMove,
529 rf, ff, rf + 2, ff + s, closure);
534 case SHOGI BlackKnight:
535 for (s = -1; s <= 1; s += 2) {
536 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
537 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
538 callback(board, flags, NormalMove,
539 rf, ff, rf - 2, ff + s, closure);
546 for (d = 0; d <= 1; d++)
547 for (s = -1; s <= 1; s += 2) {
550 rt = rf + (i * s) * d;
551 ft = ff + (i * s) * (1 - d);
552 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
553 if (m == 0 && board[rt][ft] == EmptySquare)
554 callback(board, flags, NormalMove,
555 rf, ff, rt, ft, closure);
556 if (m == 1 && board[rt][ft] != EmptySquare &&
557 !SameColor(board[rf][ff], board[rt][ft]) )
558 callback(board, flags, NormalMove,
559 rf, ff, rt, ft, closure);
560 if (board[rt][ft] != EmptySquare && m++) break;
565 /* Gold General (and all its promoted versions) . First do the */
566 /* diagonal forward steps, then proceed as normal Wazir */
567 case SHOGI (PROMOTED WhitePawn):
568 if(gameInfo.variant == VariantShogi) goto WhiteGold;
569 case SHOGI (PROMOTED BlackPawn):
570 if(gameInfo.variant == VariantShogi) goto BlackGold;
571 SlideVertical(board, flags, rf, ff, callback, closure);
574 case SHOGI (PROMOTED WhiteKnight):
575 if(gameInfo.variant == VariantShogi) goto WhiteGold;
576 case SHOGI BlackDrunk:
577 case SHOGI BlackAlfil:
578 Ferz(board, flags, rf, ff, callback, closure);
579 StepSideways(board, flags, rf, ff, callback, closure);
580 StepBackward(board, flags, rf, ff, callback, closure);
583 case SHOGI (PROMOTED BlackKnight):
584 if(gameInfo.variant == VariantShogi) goto BlackGold;
585 case SHOGI WhiteDrunk:
586 case SHOGI WhiteAlfil:
587 Ferz(board, flags, rf, ff, callback, closure);
588 StepSideways(board, flags, rf, ff, callback, closure);
589 StepForward(board, flags, rf, ff, callback, closure);
593 case SHOGI WhiteStag:
594 case SHOGI BlackStag:
595 if(gameInfo.variant == VariantShogi) goto BlackGold;
596 SlideVertical(board, flags, rf, ff, callback, closure);
597 Ferz(board, flags, rf, ff, callback, closure);
598 StepSideways(board, flags, rf, ff, callback, closure);
601 case SHOGI (PROMOTED WhiteQueen):
602 case SHOGI WhiteTokin:
603 case SHOGI WhiteWazir:
605 StepDiagForward(board, flags, rf, ff, callback, closure);
606 Wazir(board, flags, rf, ff, callback, closure);
609 case SHOGI (PROMOTED BlackQueen):
610 case SHOGI BlackTokin:
611 case SHOGI BlackWazir:
613 StepDiagBackward(board, flags, rf, ff, callback, closure);
614 Wazir(board, flags, rf, ff, callback, closure);
619 Wazir(board, flags, rf, ff, callback, closure);
622 case SHOGI WhiteMarshall:
623 case SHOGI BlackMarshall:
624 Ferz(board, flags, rf, ff, callback, closure);
625 for (d = 0; d <= 1; d++)
626 for (s = -2; s <= 2; s += 4) {
628 ft = ff + s * (1 - d);
629 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
630 if (!SameColor(board[rf][ff], board[rt][ft]) )
631 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
635 case SHOGI WhiteAngel:
636 case SHOGI BlackAngel:
637 Wazir(board, flags, rf, ff, callback, closure);
641 /* [HGM] support Shatranj pieces */
642 for (rs = -1; rs <= 1; rs += 2)
643 for (fs = -1; fs <= 1; fs += 2) {
646 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
647 && ( gameInfo.variant != VariantXiangqi ||
648 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
650 && !SameColor(board[rf][ff], board[rt][ft]))
651 callback(board, flags, NormalMove,
652 rf, ff, rt, ft, closure);
653 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
654 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
655 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
657 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
658 && !SameColor(board[rf][ff], board[rt][ft]))
659 callback(board, flags, NormalMove,
660 rf, ff, rt, ft, closure);
662 if(gameInfo.variant == VariantSpartan)
663 for(fs = -1; fs <= 1; fs += 2) {
665 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
666 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
670 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
673 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
674 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
675 for (s = -2; s <= 2; s += 4) {
677 ft = ff + s * (1 - d);
678 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
679 if (SameColor(board[rf][ff], board[rt][ft])) continue;
680 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
683 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
684 case SHOGI WhiteCardinal:
685 case SHOGI BlackCardinal:
686 case SHOGI WhitePCardinal:
687 case SHOGI BlackPCardinal:
689 Bishop(board, flags, rf, ff, callback, closure);
690 Wazir(board, flags, rf, ff, callback, closure);
693 /* Capablanca Archbishop continues as Knight */
696 Knight(board, flags, rf, ff, callback, closure);
698 /* Shogi Bishops are ordinary Bishops */
699 case SHOGI WhiteBishop:
700 case SHOGI BlackBishop:
701 case SHOGI WhitePBishop:
702 case SHOGI BlackPBishop:
705 Bishop(board, flags, rf, ff, callback, closure);
708 /* Shogi Lance is unlike anything, and asymmetric at that */
709 case SHOGI WhiteQueen:
710 if(gameInfo.variant == VariantChu) goto doQueen;
714 if (rt >= BOARD_HEIGHT) break;
715 if (SameColor(board[rf][ff], board[rt][ft])) break;
716 callback(board, flags, NormalMove,
717 rf, ff, rt, ft, closure);
718 if (board[rt][ft] != EmptySquare) break;
722 case SHOGI BlackQueen:
723 if(gameInfo.variant == VariantChu) goto doQueen;
728 if (SameColor(board[rf][ff], board[rt][ft])) break;
729 callback(board, flags, NormalMove,
730 rf, ff, rt, ft, closure);
731 if (board[rt][ft] != EmptySquare) break;
735 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
738 if(gameInfo.variant == VariantChuChess) goto DragonKing;
739 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
740 for (s = -2; s <= 2; s += 4) {
742 ft = ff + s * (1 - d);
743 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
744 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
745 if (SameColor(board[rf][ff], board[rt][ft])) continue;
746 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
748 if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
749 Wazir(board, flags, rf, ff, callback, closure);
751 Rook(board, flags, rf, ff, callback, closure);
754 /* Shogi Dragon King has to continue as Ferz after Rook moves */
755 case SHOGI WhiteDragon:
756 case SHOGI BlackDragon:
757 case SHOGI WhitePDragon:
758 case SHOGI BlackPDragon:
760 Rook(board, flags, rf, ff, callback, closure);
761 Ferz(board, flags, rf, ff, callback, closure);
765 /* Capablanca Chancellor sets flag to continue as Knight */
768 Rook(board, flags, rf, ff, callback, closure);
769 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
770 Ferz(board, flags, rf, ff, callback, closure);
772 Knight(board, flags, rf, ff, callback, closure);
775 /* Shogi Rooks are ordinary Rooks */
776 case SHOGI WhiteRook:
777 case SHOGI BlackRook:
778 case SHOGI WhitePRook:
779 case SHOGI BlackPRook:
782 Rook(board, flags, rf, ff, callback, closure);
787 case SHOGI WhiteMother:
788 case SHOGI BlackMother:
790 Rook(board, flags, rf, ff, callback, closure);
791 Bishop(board, flags, rf, ff, callback, closure);
794 case SHOGI WhitePawn:
795 StepForward(board, flags, rf, ff, callback, closure);
798 case SHOGI BlackPawn:
799 StepBackward(board, flags, rf, ff, callback, closure);
803 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
804 case SHOGI WhiteFerz:
805 Ferz(board, flags, rf, ff, callback, closure);
806 StepForward(board, flags, rf, ff, callback, closure);
810 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
811 case SHOGI BlackFerz:
812 StepBackward(board, flags, rf, ff, callback, closure);
816 /* [HGM] support Shatranj pieces */
817 Ferz(board, flags, rf, ff, callback, closure);
822 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
825 case SHOGI WhiteMonarch:
826 case SHOGI BlackMonarch:
827 case SHOGI WhiteKing:
828 case SHOGI BlackKing:
831 Ferz(board, flags, rf, ff, callback, closure);
832 Wazir(board, flags, rf, ff, callback, closure);
835 case WhiteNightrider:
836 case BlackNightrider:
837 for (i = -1; i <= 1; i += 2)
838 for (j = -1; j <= 1; j += 2)
839 for (s = 1; s <= 2; s++) { int k;
843 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
844 if (SameColor(board[rf][ff], board[rt][ft])) break;
845 callback(board, flags, NormalMove,
846 rf, ff, rt, ft, closure);
847 if (board[rt][ft] != EmptySquare) break;
853 Bishop(board, flags, rf, ff, callback, closure);
854 Rook(board, flags, rf, ff, callback, closure);
855 Knight(board, flags, rf, ff, callback, closure);
858 // Use Lance as Berolina / Spartan Pawn.
860 if(gameInfo.variant == VariantSuper) goto Amazon;
861 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
862 callback(board, flags,
863 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
864 rf, ff, rf + 1, ff, closure);
865 for (s = -1; s <= 1; s += 2) {
866 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
867 callback(board, flags,
868 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
869 rf, ff, rf + 1, ff + s, closure);
870 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
871 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
876 if(gameInfo.variant == VariantSuper) goto Amazon;
877 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
878 callback(board, flags,
879 rf <= promoRank ? BlackPromotion : NormalMove,
880 rf, ff, rf - 1, ff, closure);
881 for (s = -1; s <= 1; s += 2) {
882 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
883 callback(board, flags,
884 rf <= promoRank ? BlackPromotion : NormalMove,
885 rf, ff, rf - 1, ff + s, closure);
886 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
887 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
891 case SHOGI WhiteNothing:
892 case SHOGI BlackNothing:
893 case SHOGI WhiteLion:
894 case SHOGI BlackLion:
897 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
898 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
899 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
900 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
901 rf, ff, rt, ft, closure);
905 case SHOGI WhiteFalcon:
906 case SHOGI BlackFalcon:
907 case SHOGI WhitePDagger:
908 case SHOGI BlackPDagger:
909 SlideSideways(board, flags, rf, ff, callback, closure);
910 StepVertical(board, flags, rf, ff, callback, closure);
913 case SHOGI WhiteCobra:
914 case SHOGI BlackCobra:
915 StepVertical(board, flags, rf, ff, callback, closure);
918 case SHOGI (PROMOTED WhiteFerz):
919 if(gameInfo.variant == VariantShogi) goto WhiteGold;
920 case SHOGI (PROMOTED BlackFerz):
921 if(gameInfo.variant == VariantShogi) goto BlackGold;
922 case SHOGI WhitePSword:
923 case SHOGI BlackPSword:
924 SlideVertical(board, flags, rf, ff, callback, closure);
925 StepSideways(board, flags, rf, ff, callback, closure);
928 case SHOGI WhiteUnicorn:
929 case SHOGI BlackUnicorn:
930 Ferz(board, flags, rf, ff, callback, closure);
931 StepVertical(board, flags, rf, ff, callback, closure);
935 StepDiagForward(board, flags, rf, ff, callback, closure);
936 StepVertical(board, flags, rf, ff, callback, closure);
940 StepDiagBackward(board, flags, rf, ff, callback, closure);
941 StepVertical(board, flags, rf, ff, callback, closure);
944 case SHOGI WhiteHCrown:
945 case SHOGI BlackHCrown:
946 Bishop(board, flags, rf, ff, callback, closure);
947 SlideSideways(board, flags, rf, ff, callback, closure);
950 case SHOGI WhiteCrown:
951 case SHOGI BlackCrown:
952 Bishop(board, flags, rf, ff, callback, closure);
953 SlideVertical(board, flags, rf, ff, callback, closure);
956 case SHOGI WhiteHorned:
957 Sting(board, flags, rf, ff, 1, 0, callback, closure);
958 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
959 if(killX >= 0) break;
960 Bishop(board, flags, rf, ff, callback, closure);
961 SlideSideways(board, flags, rf, ff, callback, closure);
962 SlideBackward(board, flags, rf, ff, callback, closure);
965 case SHOGI BlackHorned:
966 Sting(board, flags, rf, ff, -1, 0, callback, closure);
967 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
968 if(killX >= 0) break;
969 Bishop(board, flags, rf, ff, callback, closure);
970 SlideSideways(board, flags, rf, ff, callback, closure);
971 SlideForward(board, flags, rf, ff, callback, closure);
974 case SHOGI WhiteEagle:
975 Sting(board, flags, rf, ff, 1, 1, callback, closure);
976 Sting(board, flags, rf, ff, 1, -1, callback, closure);
977 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
978 if(killX >= 0) break;
979 Rook(board, flags, rf, ff, callback, closure);
980 SlideDiagBackward(board, flags, rf, ff, callback, closure);
983 case SHOGI BlackEagle:
984 Sting(board, flags, rf, ff, -1, 1, callback, closure);
985 Sting(board, flags, rf, ff, -1, -1, callback, closure);
986 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
987 if(killX >= 0) break;
988 Rook(board, flags, rf, ff, callback, closure);
989 SlideDiagForward(board, flags, rf, ff, callback, closure);
992 case SHOGI WhiteDolphin:
993 case SHOGI BlackHorse:
994 SlideDiagBackward(board, flags, rf, ff, callback, closure);
995 SlideVertical(board, flags, rf, ff, callback, closure);
998 case SHOGI BlackDolphin:
999 case SHOGI WhiteHorse:
1000 SlideDiagForward(board, flags, rf, ff, callback, closure);
1001 SlideVertical(board, flags, rf, ff, callback, closure);
1004 case SHOGI WhiteLance:
1005 SlideForward(board, flags, rf, ff, callback, closure);
1008 case SHOGI BlackLance:
1009 SlideBackward(board, flags, rf, ff, callback, closure);
1012 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1016 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1029 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1030 Board xqCheckers, nullBoard;
1032 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1033 int rf, int ff, int rt, int ft,
1037 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1039 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1041 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1043 if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1045 if (!(flags & F_IGNORE_CHECK) ) {
1046 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1049 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1050 kings += (board[r][f] == BlackKing);
1054 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1056 check = CheckTest(board, flags, rf, ff, rt, ft,
1057 kind == WhiteCapturesEnPassant ||
1058 kind == BlackCapturesEnPassant);
1059 if(promo) board[rf][ff] = BlackLance;
1062 if (flags & F_ATOMIC_CAPTURE) {
1063 if (board[rt][ft] != EmptySquare ||
1064 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1066 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1067 if (board[rf][ff] == king) return;
1068 for (r = rt-1; r <= rt+1; r++) {
1069 for (f = ft-1; f <= ft+1; f++) {
1070 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1071 board[r][f] == king) return;
1076 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1083 int captures; // [HGM] losers
1084 } LegalityTestClosure;
1087 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1088 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1089 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1090 moves that would destroy your own king. The CASTLE_OK flags are
1091 true if castling is not yet ruled out by a move of the king or
1092 rook. Return TRUE if the player on move is currently in check and
1093 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1095 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1098 int ff, ft, k, left, right, swap;
1099 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1100 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1101 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1105 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1106 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1107 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1109 if (inCheck) return TRUE;
1111 /* Generate castling moves */
1112 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1113 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1116 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1117 if ((flags & F_WHITE_ON_MOVE) &&
1118 (flags & F_WHITE_KCASTLE_OK) &&
1119 board[0][ff] == wKing &&
1120 board[0][ff + 1] == EmptySquare &&
1121 board[0][ff + 2] == EmptySquare &&
1122 board[0][BOARD_RGHT-3] == EmptySquare &&
1123 board[0][BOARD_RGHT-2] == EmptySquare &&
1124 board[0][BOARD_RGHT-1] == WhiteRook &&
1125 castlingRights[0] != NoRights && /* [HGM] check rights */
1126 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1128 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1129 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1130 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1131 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1133 callback(board, flags,
1134 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1135 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1137 if ((flags & F_WHITE_ON_MOVE) &&
1138 (flags & F_WHITE_QCASTLE_OK) &&
1139 board[0][ff] == wKing &&
1140 board[0][ff - 1] == EmptySquare &&
1141 board[0][ff - 2] == EmptySquare &&
1142 board[0][BOARD_LEFT+2] == EmptySquare &&
1143 board[0][BOARD_LEFT+1] == EmptySquare &&
1144 board[0][BOARD_LEFT+0] == WhiteRook &&
1145 castlingRights[1] != NoRights && /* [HGM] check rights */
1146 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1148 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1149 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1150 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1152 callback(board, flags,
1153 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1154 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1156 if (!(flags & F_WHITE_ON_MOVE) &&
1157 (flags & F_BLACK_KCASTLE_OK) &&
1158 board[BOARD_HEIGHT-1][ff] == bKing &&
1159 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1160 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1161 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1162 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1163 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1164 castlingRights[3] != NoRights && /* [HGM] check rights */
1165 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1167 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1168 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1169 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1170 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1172 callback(board, flags,
1173 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1174 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1176 if (!(flags & F_WHITE_ON_MOVE) &&
1177 (flags & F_BLACK_QCASTLE_OK) &&
1178 board[BOARD_HEIGHT-1][ff] == bKing &&
1179 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1180 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1181 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1182 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1183 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1184 castlingRights[4] != NoRights && /* [HGM] check rights */
1185 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1187 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1188 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1189 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1191 callback(board, flags,
1192 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1193 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1197 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1199 /* generate all potential FRC castling moves (KxR), ignoring flags */
1200 /* [HGM] test if the Rooks we find have castling rights */
1201 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1204 if ((flags & F_WHITE_ON_MOVE) != 0) {
1205 ff = castlingRights[2]; /* King file if we have any rights */
1206 if(ff != NoRights && board[0][ff] == WhiteKing) {
1207 if (appData.debugMode) {
1208 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1209 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1211 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1213 right = BOARD_RGHT-2;
1214 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1215 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1216 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1217 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1218 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1219 if(ft != NoRights && board[0][ft] == WhiteRook) {
1220 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1221 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1224 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1225 left = BOARD_LEFT+2;
1227 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1228 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1229 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1230 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1231 if(ff > BOARD_LEFT+2)
1232 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1233 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1234 if(ft != NoRights && board[0][ft] == WhiteRook) {
1235 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1236 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1240 ff = castlingRights[5]; /* King file if we have any rights */
1241 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1242 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1244 right = BOARD_RGHT-2;
1245 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1246 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1247 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1248 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1249 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1250 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1251 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1252 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1255 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1256 left = BOARD_LEFT+2;
1258 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1259 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1260 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1261 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1262 if(ff > BOARD_LEFT+2)
1263 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1264 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1265 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1266 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1267 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1284 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1285 int rf, int ff, int rt, int ft,
1290 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1292 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1294 if (rt == cl->rking && ft == cl->fking) {
1295 if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1297 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1299 if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1300 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1301 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1305 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1306 he leave himself in check? Or if rf == -1, is the player on move
1307 in check now? enPassant must be TRUE if the indicated move is an
1308 e.p. capture. The possibility of castling out of a check along the
1309 back rank is not accounted for (i.e., we still return nonzero), as
1310 this is illegal anyway. Return value is the number of times the
1311 king is in check. */
1313 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1315 CheckTestClosure cl;
1316 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1317 ChessSquare captured = EmptySquare, ep=0, trampled=0;
1318 /* Suppress warnings on uninitialized variables */
1320 if(gameInfo.variant == VariantXiangqi)
1321 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1322 if(gameInfo.variant == VariantKnightmate)
1323 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1324 if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check
1325 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1326 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1327 if(board[r][f] == k || board[r][f] == prince) {
1328 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1329 king = board[r][f]; // remember hich one we had
1336 captured = board[rf][ft];
1337 board[rf][ft] = EmptySquare;
1339 captured = board[rt][ft];
1340 if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
1342 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1343 board[rt][ft] = board[rf][ff];
1344 board[rf][ff] = EmptySquare;
1346 ep = board[EP_STATUS];
1347 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1348 ChessSquare victim = killX < 0 ? EmptySquare : trampled;
1349 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1350 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1351 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
1352 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1356 /* For compatibility with ICS wild 9, we scan the board in the
1357 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1358 and we test only whether that one is in check. */
1359 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1360 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1361 if (board[cl.rking][cl.fking] == king) {
1363 if(gameInfo.variant == VariantXiangqi) {
1364 /* [HGM] In Xiangqi opposing Kings means check as well */
1366 dir = (king >= BlackPawn) ? -1 : 1;
1367 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1368 board[i][cl.fking] == EmptySquare; i+=dir );
1369 if(i>=0 && i<BOARD_HEIGHT &&
1370 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1373 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1374 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1375 goto undo_move; /* 2-level break */
1382 if(rf != DROP_RANK) // [HGM] drop
1383 board[rf][ff] = board[rt][ft];
1385 board[rf][ft] = captured;
1386 board[rt][ft] = EmptySquare;
1388 if(killX >= 0) board[killY][killX] = trampled;
1389 board[rt][ft] = captured;
1391 board[EP_STATUS] = ep;
1394 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1398 HasLion (Board board, int flags)
1400 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1402 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1403 if(board[r][f] == lion) return 1;
1408 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1409 { // [HGM] put drop legality testing in separate routine for clarity
1411 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1412 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1413 n = PieceToNumber(piece);
1414 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1415 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1416 return ImpossibleMove; // piece not available
1417 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1418 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1419 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1420 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1421 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1422 if(piece == WhitePawn || piece == BlackPawn) {
1424 for(r=1; r<BOARD_HEIGHT-1; r++)
1425 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
1426 // should still test if we mate with this Pawn
1428 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1429 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1431 if( (piece == WhitePawn || piece == BlackPawn) &&
1432 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1433 return IllegalMove; /* no pawn drops on 1st/8th */
1435 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1436 if (!(flags & F_IGNORE_CHECK) &&
1437 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1438 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1441 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1442 int rf, int ff, int rt, int ft,
1446 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1448 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1450 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1451 cl->captures++; // [HGM] losers: count legal captures
1452 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1457 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1459 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1461 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1462 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1463 piece = filterPiece = board[rf][ff];
1464 if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1466 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1467 /* (perhaps we should disallow moves that obviously leave us in check?) */
1468 if((piece == WhiteFalcon || piece == BlackFalcon ||
1469 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu)
1470 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1474 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1475 cl.ft = fFilter = ft;
1476 cl.kind = IllegalMove;
1477 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1478 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1479 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1480 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1481 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1482 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1484 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1485 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1486 if(board[rf][ff] < BlackPawn) { // white
1487 if(rf != 0) return IllegalMove; // must be on back rank
1488 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1489 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1490 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1491 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1493 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1494 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1495 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1496 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1497 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1500 if(gameInfo.variant == VariantChu) {
1501 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1502 if(promoChar != '+')
1503 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1504 if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
1505 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1507 if(gameInfo.variant == VariantShogi) {
1508 /* [HGM] Shogi promotions. '=' means defer */
1509 if(rf != DROP_RANK && cl.kind == NormalMove) {
1510 ChessSquare piece = board[rf][ff];
1512 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1513 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1514 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1515 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1516 promoChar = '+'; // allowed ICS notations
1517 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1518 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1519 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1520 else if(flags & F_WHITE_ON_MOVE) {
1521 if( (int) piece < (int) WhiteWazir &&
1522 (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1523 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1524 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1525 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1526 else /* promotion optional, default is defer */
1527 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1528 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1530 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1531 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1532 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1533 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1534 else /* promotion optional, default is defer */
1535 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1536 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1540 if (promoChar != NULLCHAR) {
1541 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1542 ChessSquare piece = board[rf][ff];
1543 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1544 // should test if in zone, really
1545 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1547 if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1549 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1550 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1551 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1552 if(piece == EmptySquare)
1553 cl.kind = ImpossibleMove; // non-existing piece
1554 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1555 cl.kind = IllegalMove; // no two Lions
1556 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1557 if(promoChar != PieceToChar(BlackKing)) {
1558 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1559 if(piece == BlackLance) cl.kind = ImpossibleMove;
1560 } else { // promotion to King allowed only if we do not have two yet
1561 int r, f, kings = 0;
1562 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1563 if(kings == 2) cl.kind = IllegalMove;
1565 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1566 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1567 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1568 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1569 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1570 cl.kind = IllegalMove; // promotion to King usually not allowed
1572 cl.kind = IllegalMove;
1582 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1583 int rf, int ff, int rt, int ft,
1587 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1589 register MateTestClosure *cl = (MateTestClosure *) closure;
1594 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1596 MateTest (Board board, int flags)
1599 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1600 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1602 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1603 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1604 nrKing += (board[r][f] == king); // stm has king
1605 if( board[r][f] != EmptySquare ) {
1606 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1611 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1612 case VariantShatranj:
1613 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1617 if(nrKing == 0) return MT_NOKING;
1620 if(myPieces == 1) return MT_BARE;
1623 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1624 // [HGM] 3check: yet to do!
1626 return inCheck ? MT_CHECK : MT_NONE;
1628 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1629 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1630 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1631 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1632 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1633 if(board[n][holdings] != EmptySquare) {
1634 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1635 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1638 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1639 return myPieces == hisPieces ? MT_STALEMATE :
1640 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1641 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1642 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1644 return inCheck ? MT_CHECKMATE
1645 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
1646 MT_STAINMATE : MT_STALEMATE;
1651 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1652 int rf, int ff, int rt, int ft,
1656 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1658 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1659 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1661 // [HGM] wild: for wild-card pieces rt and rf are dummies
1662 if(piece == WhiteFalcon || piece == BlackFalcon ||
1663 piece == WhiteCobra || piece == BlackCobra)
1666 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1667 || PieceToChar(board[rf][ff]) == '~'
1668 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1670 (cl->rfIn == -1 || cl->rfIn == rf) &&
1671 (cl->ffIn == -1 || cl->ffIn == ff) &&
1672 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1673 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1676 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1677 // [HGM] oneclick: if multiple moves, be sure we remember capture
1678 cl->piece = board[rf][ff];
1681 cl->rt = wildCard ? cl->rtIn : rt;
1682 cl->ft = wildCard ? cl->ftIn : ft;
1685 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1690 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
1692 int illegal = 0; char c = closure->promoCharIn;
1694 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1695 closure->count = closure->captures = 0;
1696 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1697 closure->kind = ImpossibleMove;
1698 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
1699 fFilter = closure->ftIn;
1700 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
1701 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1702 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
1703 closure->count = closure->captures = 0;
1704 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1705 closure->kind = ImpossibleMove;
1706 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1709 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1710 if (closure->count == 0) {
1711 /* See if it's an illegal move due to check */
1713 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1714 if (closure->count == 0) {
1715 /* No, it's not even that */
1716 if(!appData.testLegality && closure->pieceIn != EmptySquare) {
1717 int f, r; // if there is only a single piece of the requested type on the board, use that
1718 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
1719 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1720 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
1721 if(closure->count > 1) illegal = 0; // ambiguous
1723 if(closure->count == 0) {
1724 if (appData.debugMode) { int i, j;
1725 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1726 for(j=0; j<BOARD_WIDTH; j++)
1727 fprintf(debugFP, "%3d", (int) board[i][j]);
1728 fprintf(debugFP, "\n");
1736 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1737 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1738 if(closure->piece < BlackPawn) { // white
1739 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1740 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
1741 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1742 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1743 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1745 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1746 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
1747 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1748 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1749 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1752 if(gameInfo.variant == VariantChu) {
1753 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
1755 if(gameInfo.variant == VariantShogi) {
1756 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1757 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1758 ChessSquare piece = closure->piece;
1759 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1760 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1761 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1762 c = '+'; // allowed ICS notations
1763 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1764 else if(flags & F_WHITE_ON_MOVE) {
1765 if( (int) piece < (int) WhiteWazir &&
1766 (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1767 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1768 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1769 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1770 else /* promotion optional, default is defer */
1771 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1772 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1774 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1775 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1776 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1777 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1778 else /* promotion optional, default is defer */
1779 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1780 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1783 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1784 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1786 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1787 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1788 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1789 gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
1790 c = PieceToChar(BlackFerz);
1791 else if(gameInfo.variant == VariantGreat)
1792 c = PieceToChar(BlackMan);
1793 else if(gameInfo.variant == VariantGrand)
1794 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
1796 c = PieceToChar(BlackQueen);
1797 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1798 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1799 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
1800 ChessSquare p = closure->piece;
1801 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
1802 closure->kind = ImpossibleMove; // used on non-promotable piece
1803 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1804 } else if (c != NULLCHAR) closure->kind = IllegalMove;
1806 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1807 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1808 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1809 if (closure->count > 1) {
1810 closure->kind = AmbiguousMove;
1813 /* Note: If more than one illegal move matches, but no legal
1814 moves, we return IllegalMove, not AmbiguousMove. Caller
1815 can look at closure->count to detect this.
1817 closure->kind = IllegalMove;
1831 } CoordsToAlgebraicClosure;
1833 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1834 ChessMove kind, int rf, int ff,
1835 int rt, int ft, VOIDSTAR closure));
1838 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1840 register CoordsToAlgebraicClosure *cl =
1841 (CoordsToAlgebraicClosure *) closure;
1843 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1844 (board[rf][ff] == cl->piece
1845 || PieceToChar(board[rf][ff]) == '~' &&
1846 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1850 cl->kind = kind; /* this is the move we want */
1852 cl->file++; /* need file to rule out this move */
1856 cl->rank++; /* need rank to rule out this move */
1858 cl->either++; /* rank or file will rule out this move */
1864 /* Convert coordinates to normal algebraic notation.
1865 promoChar must be NULLCHAR or 'x' if not a promotion.
1868 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
1872 char *outp = out, c;
1873 CoordsToAlgebraicClosure cl;
1875 if (rf == DROP_RANK) {
1876 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
1877 /* Bughouse piece drop */
1878 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1883 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1885 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1888 if (promoChar == 'x') promoChar = NULLCHAR;
1889 piece = board[rf][ff];
1890 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1895 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1896 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1897 /* Keep short notation if move is illegal only because it
1898 leaves the player in check, but still return IllegalMove */
1899 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1900 if (kind == IllegalMove) break;
1905 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1906 /* Non-capture; use style "e5" */
1909 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1911 /* Capture; use style "exd5" */
1912 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1913 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
1917 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1919 /* Use promotion suffix style "=Q" */
1921 if (promoChar != NULLCHAR) {
1922 if(IS_SHOGI(gameInfo.variant)) {
1923 /* [HGM] ... but not in Shogi! */
1924 *outp++ = promoChar == '=' ? '=' : '+';
1927 *outp++ = ToUpper(promoChar);
1936 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1937 /* Code added by Tord: FRC castling. */
1938 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1939 (piece == BlackKing && board[rt][ft] == BlackRook)) {
1941 safeStrCpy(out, "O-O", MOVE_LEN);
1943 safeStrCpy(out, "O-O-O", MOVE_LEN);
1944 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1946 /* End of code added by Tord */
1947 /* Test for castling or ICS wild castling */
1948 /* Use style "O-O" (oh-oh) for PGN compatibility */
1949 else if (rf == rt &&
1950 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1951 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
1952 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1953 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1954 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1955 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1957 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1959 /* This notation is always unambiguous, unless there are
1960 kings on both the d and e files, with "wild castling"
1961 possible for the king on the d file and normal castling
1962 possible for the other. ICS rules for wild 9
1963 effectively make castling illegal for either king in
1964 this situation. So I am not going to worry about it;
1965 I'll just generate an ambiguous O-O in this case.
1967 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1970 /* else fall through */
1975 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1976 cl.ft = fFilter = ft;
1978 cl.kind = IllegalMove;
1979 cl.rank = cl.file = cl.either = 0;
1980 c = PieceToChar(piece) ;
1981 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
1983 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1984 /* Generate pretty moves for moving into check, but
1985 still return IllegalMove.
1987 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
1988 if (cl.kind == IllegalMove) break;
1989 cl.kind = IllegalMove;
1992 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1993 else "Ngf3" or "Ngxf7",
1994 else "N1f3" or "N5xf7",
1995 else "Ng1f3" or "Ng5xf7".
1997 if( c == '~' || c == '+') {
1998 /* [HGM] print nonexistent piece as its demoted version */
1999 piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
2001 if(c=='+') *outp++ = c;
2002 *outp++ = ToUpper(PieceToChar(piece));
2004 if (cl.file || (cl.either && !cl.rank)) {
2010 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2013 if(board[rt][ft] != EmptySquare)
2019 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2020 if (IS_SHOGI(gameInfo.variant)) {
2021 /* [HGM] in Shogi non-pawns can promote */
2022 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2024 else if (gameInfo.variant == VariantChuChess && promoChar ||
2025 gameInfo.variant != VariantSuper && promoChar &&
2026 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2028 *outp++ = ToUpper(promoChar);
2030 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2032 *outp++ = ToUpper(promoChar);
2038 /* Moving a nonexistent piece */
2042 /* Not a legal move, even ignoring check.
2043 If there was a piece on the from square,
2044 use style "Ng1g3" or "Ng1xe8";
2045 if there was a pawn or nothing (!),
2046 use style "g1g3" or "g1xe8". Use "x"
2047 if a piece was on the to square, even
2048 a piece of the same color.
2052 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2054 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2055 c += (board[r][f] == piece); // count on-board pieces of given type
2056 *outp++ = ToUpper(PieceToChar(piece));
2058 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2062 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2064 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2068 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2069 /* Use promotion suffix style "=Q" */
2070 if (promoChar != NULLCHAR && promoChar != 'x') {
2072 *outp++ = ToUpper(promoChar);
2079 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2088 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2090 int preyStackPointer, chaseStackPointer;
2093 unsigned char rf, ff, rt, ft;
2097 unsigned char rank, file;
2103 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2105 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2106 int rf, int ff, int rt, int ft,
2110 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2111 { // For adding captures that can lead to chase indictment to the chaseStack
2112 if(board[rt][ft] == EmptySquare) return; // non-capture
2113 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2114 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2115 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2116 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2117 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2118 chaseStack[chaseStackPointer].rf = rf;
2119 chaseStack[chaseStackPointer].ff = ff;
2120 chaseStack[chaseStackPointer].rt = rt;
2121 chaseStack[chaseStackPointer].ft = ft;
2122 chaseStackPointer++;
2125 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2126 int rf, int ff, int rt, int ft,
2130 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2131 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2133 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2135 if(board[rt][ft] == EmptySquare) return; // no capture
2136 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2137 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2139 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2140 for(i=0; i<chaseStackPointer; i++) {
2141 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2142 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2143 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2144 chaseStack[i] = chaseStack[--chaseStackPointer];
2150 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2151 int rf, int ff, int rt, int ft,
2155 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2156 { // for determining if a piece (given through the closure) is protected
2157 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2159 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2160 if(appData.debugMode && board[rt][ft] != EmptySquare)
2161 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2164 extern char moveList[MAX_MOVES][MOVE_LEN];
2167 PerpetualChase (int first, int last)
2168 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2171 ChessSquare captured;
2173 preyStackPointer = 0; // clear stack of chased pieces
2174 for(i=first; i<last; i+=2) { // for all positions with same side to move
2175 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2176 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2177 // determine all captures possible after the move, and put them on chaseStack
2178 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2179 if(appData.debugMode) { int n;
2180 for(n=0; n<chaseStackPointer; n++)
2181 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2182 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2183 fprintf(debugFP, ": all capts\n");
2185 // determine all captures possible before the move, and delete them from chaseStack
2186 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2187 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2188 cl.rt = moveList[i][3]-ONE;
2189 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2190 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2191 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2192 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2193 if(appData.debugMode) { int n;
2194 for(n=0; n<chaseStackPointer; n++)
2195 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2196 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2197 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2199 // chaseSack now contains all captures made possible by the move
2200 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2201 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2202 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2204 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2205 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2207 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2208 continue; // C or H attack on R is always chase; leave on chaseStack
2210 if(attacker == victim) {
2211 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2212 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2213 // we can capture back with equal piece, so this is no chase but a sacrifice
2214 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2215 j--; /* ! */ continue;
2220 // the attack is on a lower piece, or on a pinned or blocked equal one
2221 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2222 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2223 // test if the victim is protected by a true protector. First make the capture.
2224 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2225 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2226 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2227 // Then test if the opponent can recapture
2228 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2229 cl.rt = chaseStack[j].rt;
2230 cl.ft = chaseStack[j].ft;
2231 if(appData.debugMode) {
2232 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2234 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2235 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2236 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2237 // unmake the capture
2238 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2239 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2240 // if a recapture was found, piece is protected, and we are not chasing it.
2241 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2242 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2246 // chaseStack now contains all moves that chased
2247 if(appData.debugMode) { int n;
2248 for(n=0; n<chaseStackPointer; n++)
2249 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2250 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2251 fprintf(debugFP, ": chases\n");
2253 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2254 for(j=0; j<chaseStackPointer; j++) {
2255 preyStack[j].rank = chaseStack[j].rt;
2256 preyStack[j].file = chaseStack[j].ft;
2258 preyStackPointer = chaseStackPointer;
2261 for(j=0; j<chaseStackPointer; j++) {
2262 for(k=0; k<preyStackPointer; k++) {
2263 // search the victim of each chase move on the preyStack (first occurrence)
2264 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2265 if(k < tail) break; // piece was already identified as still being chased
2266 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2267 preyStack[tail] = preyStack[k]; // by swapping
2268 preyStack[k] = preyStack[preyStackPointer];
2274 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2275 if(appData.debugMode) { int n;
2276 for(n=0; n<preyStackPointer; n++)
2277 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2278 fprintf(debugFP, "always chased upto ply %d\n", i);
2280 // now adjust the location of the chased pieces according to opponent move
2281 for(j=0; j<preyStackPointer; j++) {
2282 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2283 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2284 preyStack[j].rank = moveList[i+1][3]-ONE;
2285 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2290 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2291 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the