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 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 ? 3 : 1;
387 for (rf = 0; rf < BOARD_HEIGHT; rf++)
388 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
392 if(board[rf][ff] == EmptySquare) continue;
393 if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
395 m = 0; piece = board[rf][ff];
396 if(PieceToChar(piece) == '~')
397 piece = (ChessSquare) ( DEMOTED piece );
398 if(filter != EmptySquare && piece != filter) continue;
399 if(IS_SHOGI(gameInfo.variant))
400 piece = (ChessSquare) ( SHOGI piece );
402 switch ((int)piece) {
403 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
405 /* can't happen ([HGM] except for faries...) */
409 if(gameInfo.variant == VariantXiangqi) {
410 /* [HGM] capture and move straight ahead in Xiangqi */
411 if (rf < BOARD_HEIGHT-1 &&
412 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
413 callback(board, flags, NormalMove,
414 rf, ff, rf + 1, ff, closure);
416 /* and move sideways when across the river */
417 for (s = -1; s <= 1; s += 2) {
418 if (rf >= BOARD_HEIGHT>>1 &&
419 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
420 !WhitePiece(board[rf][ff+s]) ) {
421 callback(board, flags, NormalMove,
422 rf, ff, rf, ff+s, closure);
427 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
428 callback(board, flags,
429 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
430 rf, ff, rf + 1, ff, closure);
432 if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
433 gameInfo.variant != VariantShatranj && /* [HGM] */
434 gameInfo.variant != VariantCourier && /* [HGM] */
435 board[rf+2][ff] == EmptySquare ) {
436 callback(board, flags, NormalMove,
437 rf, ff, rf+2, ff, closure);
439 for (s = -1; s <= 1; s += 2) {
440 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
441 ((flags & F_KRIEGSPIEL_CAPTURE) ||
442 BlackPiece(board[rf + 1][ff + s]))) {
443 callback(board, flags,
444 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
445 rf, ff, rf + 1, ff + s, closure);
447 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
448 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
449 (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
450 board[rf][ff + s] == BlackPawn &&
451 board[rf+1][ff + s] == EmptySquare) {
452 callback(board, flags, WhiteCapturesEnPassant,
453 rf, ff, rf+1, ff + s, closure);
460 if(gameInfo.variant == VariantXiangqi) {
461 /* [HGM] capture straight ahead in Xiangqi */
462 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
463 callback(board, flags, NormalMove,
464 rf, ff, rf - 1, ff, closure);
466 /* and move sideways when across the river */
467 for (s = -1; s <= 1; s += 2) {
468 if (rf < BOARD_HEIGHT>>1 &&
469 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
470 !BlackPiece(board[rf][ff+s]) ) {
471 callback(board, flags, NormalMove,
472 rf, ff, rf, ff+s, closure);
477 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
478 callback(board, flags,
479 rf <= promoRank ? BlackPromotion : NormalMove,
480 rf, ff, rf - 1, ff, closure);
482 if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
483 gameInfo.variant != VariantShatranj && /* [HGM] */
484 gameInfo.variant != VariantCourier && /* [HGM] */
485 board[rf-2][ff] == EmptySquare) {
486 callback(board, flags, NormalMove,
487 rf, ff, rf-2, ff, closure);
489 for (s = -1; s <= 1; s += 2) {
490 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
491 ((flags & F_KRIEGSPIEL_CAPTURE) ||
492 WhitePiece(board[rf - 1][ff + s]))) {
493 callback(board, flags,
494 rf <= promoRank ? BlackPromotion : NormalMove,
495 rf, ff, rf - 1, ff + s, closure);
497 if (rf < BOARD_HEIGHT>>1) {
498 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
499 (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
500 board[rf][ff + s] == WhitePawn &&
501 board[rf-1][ff + s] == EmptySquare) {
502 callback(board, flags, BlackCapturesEnPassant,
503 rf, ff, rf-1, ff + s, closure);
513 for (i = -1; i <= 1; i += 2)
514 for (j = -1; j <= 1; j += 2)
515 for (s = 1; s <= 2; s++) {
518 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
519 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
520 && !SameColor(board[rf][ff], board[rt][ft]))
521 callback(board, flags, NormalMove,
522 rf, ff, rt, ft, closure);
526 case SHOGI WhiteKnight:
527 for (s = -1; s <= 1; s += 2) {
528 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
529 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
530 callback(board, flags, NormalMove,
531 rf, ff, rf + 2, ff + s, closure);
536 case SHOGI BlackKnight:
537 for (s = -1; s <= 1; s += 2) {
538 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
539 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
540 callback(board, flags, NormalMove,
541 rf, ff, rf - 2, ff + s, closure);
548 for (d = 0; d <= 1; d++)
549 for (s = -1; s <= 1; s += 2) {
552 rt = rf + (i * s) * d;
553 ft = ff + (i * s) * (1 - d);
554 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
555 if (m == 0 && board[rt][ft] == EmptySquare)
556 callback(board, flags, NormalMove,
557 rf, ff, rt, ft, closure);
558 if (m == 1 && board[rt][ft] != EmptySquare &&
559 !SameColor(board[rf][ff], board[rt][ft]) )
560 callback(board, flags, NormalMove,
561 rf, ff, rt, ft, closure);
562 if (board[rt][ft] != EmptySquare && m++) break;
567 /* Gold General (and all its promoted versions) . First do the */
568 /* diagonal forward steps, then proceed as normal Wazir */
569 case SHOGI (PROMOTED WhitePawn):
570 if(gameInfo.variant == VariantShogi) goto WhiteGold;
571 case SHOGI (PROMOTED BlackPawn):
572 if(gameInfo.variant == VariantShogi) goto BlackGold;
573 SlideVertical(board, flags, rf, ff, callback, closure);
576 case SHOGI (PROMOTED WhiteKnight):
577 if(gameInfo.variant == VariantShogi) goto WhiteGold;
578 case SHOGI BlackDrunk:
579 case SHOGI BlackAlfil:
580 Ferz(board, flags, rf, ff, callback, closure);
581 StepSideways(board, flags, rf, ff, callback, closure);
582 StepBackward(board, flags, rf, ff, callback, closure);
585 case SHOGI (PROMOTED BlackKnight):
586 if(gameInfo.variant == VariantShogi) goto BlackGold;
587 case SHOGI WhiteDrunk:
588 case SHOGI WhiteAlfil:
589 Ferz(board, flags, rf, ff, callback, closure);
590 StepSideways(board, flags, rf, ff, callback, closure);
591 StepForward(board, flags, rf, ff, callback, closure);
595 case SHOGI WhiteStag:
596 case SHOGI BlackStag:
597 if(gameInfo.variant == VariantShogi) goto BlackGold;
598 SlideVertical(board, flags, rf, ff, callback, closure);
599 Ferz(board, flags, rf, ff, callback, closure);
600 StepSideways(board, flags, rf, ff, callback, closure);
603 case SHOGI (PROMOTED WhiteQueen):
604 case SHOGI WhiteTokin:
605 case SHOGI WhiteWazir:
607 StepDiagForward(board, flags, rf, ff, callback, closure);
608 Wazir(board, flags, rf, ff, callback, closure);
611 case SHOGI (PROMOTED BlackQueen):
612 case SHOGI BlackTokin:
613 case SHOGI BlackWazir:
615 StepDiagBackward(board, flags, rf, ff, callback, closure);
616 Wazir(board, flags, rf, ff, callback, closure);
621 Wazir(board, flags, rf, ff, callback, closure);
624 case SHOGI WhiteMarshall:
625 case SHOGI BlackMarshall:
626 Ferz(board, flags, rf, ff, callback, closure);
627 for (d = 0; d <= 1; d++)
628 for (s = -2; s <= 2; s += 4) {
630 ft = ff + s * (1 - d);
631 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
632 if (!SameColor(board[rf][ff], board[rt][ft]) )
633 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
637 case SHOGI WhiteAngel:
638 case SHOGI BlackAngel:
639 Wazir(board, flags, rf, ff, callback, closure);
643 /* [HGM] support Shatranj pieces */
644 for (rs = -1; rs <= 1; rs += 2)
645 for (fs = -1; fs <= 1; fs += 2) {
648 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
649 && ( gameInfo.variant != VariantXiangqi ||
650 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
652 && !SameColor(board[rf][ff], board[rt][ft]))
653 callback(board, flags, NormalMove,
654 rf, ff, rt, ft, closure);
655 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
656 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
657 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
659 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
660 && !SameColor(board[rf][ff], board[rt][ft]))
661 callback(board, flags, NormalMove,
662 rf, ff, rt, ft, closure);
664 if(gameInfo.variant == VariantSpartan)
665 for(fs = -1; fs <= 1; fs += 2) {
667 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
668 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
672 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
675 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
676 for (s = -2; s <= 2; s += 4) {
678 ft = ff + s * (1 - d);
679 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
680 if (SameColor(board[rf][ff], board[rt][ft])) continue;
681 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
684 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
685 case SHOGI WhiteCardinal:
686 case SHOGI BlackCardinal:
687 case SHOGI WhitePCardinal:
688 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 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
739 for (s = -2; s <= 2; s += 4) {
741 ft = ff + s * (1 - d);
742 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT || board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
743 if (SameColor(board[rf][ff], board[rt][ft])) continue;
744 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
746 if(gameInfo.variant == VariantSpartan) rookRange = 2; // in Spartan Chess restrict range to modern Dababba
749 /* Shogi Dragon King has to continue as Ferz after Rook moves */
750 case SHOGI WhiteDragon:
751 case SHOGI BlackDragon:
752 case SHOGI WhitePDragon:
753 case SHOGI BlackPDragon:
754 Rook(board, flags, rf, ff, callback, closure);
755 Ferz(board, flags, rf, ff, callback, closure);
759 /* Capablanca Chancellor sets flag to continue as Knight */
762 Rook(board, flags, rf, ff, callback, closure);
763 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
764 Ferz(board, flags, rf, ff, callback, closure);
766 Knight(board, flags, rf, ff, callback, closure);
769 /* Shogi Rooks are ordinary Rooks */
770 case SHOGI WhiteRook:
771 case SHOGI BlackRook:
772 case SHOGI WhitePRook:
773 case SHOGI BlackPRook:
777 Rook(board, flags, rf, ff, callback, closure);
782 case SHOGI WhiteMother:
783 case SHOGI BlackMother:
785 Rook(board, flags, rf, ff, callback, closure);
786 Bishop(board, flags, rf, ff, callback, closure);
789 case SHOGI WhitePawn:
790 StepForward(board, flags, rf, ff, callback, closure);
793 case SHOGI BlackPawn:
794 StepBackward(board, flags, rf, ff, callback, closure);
798 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
799 case SHOGI WhiteFerz:
800 Ferz(board, flags, rf, ff, callback, closure);
801 StepForward(board, flags, rf, ff, callback, closure);
805 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
806 case SHOGI BlackFerz:
807 StepBackward(board, flags, rf, ff, callback, closure);
811 /* [HGM] support Shatranj pieces */
812 Ferz(board, flags, rf, ff, callback, closure);
817 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
820 case SHOGI WhiteMonarch:
821 case SHOGI BlackMonarch:
822 case SHOGI WhiteKing:
823 case SHOGI BlackKing:
826 Ferz(board, flags, rf, ff, callback, closure);
827 Wazir(board, flags, rf, ff, callback, closure);
830 case WhiteNightrider:
831 case BlackNightrider:
832 for (i = -1; i <= 1; i += 2)
833 for (j = -1; j <= 1; j += 2)
834 for (s = 1; s <= 2; s++) { int k;
838 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
839 if (SameColor(board[rf][ff], board[rt][ft])) break;
840 callback(board, flags, NormalMove,
841 rf, ff, rt, ft, closure);
842 if (board[rt][ft] != EmptySquare) break;
848 Bishop(board, flags, rf, ff, callback, closure);
849 Rook(board, flags, rf, ff, callback, closure);
850 Knight(board, flags, rf, ff, callback, closure);
853 // Use Lance as Berolina / Spartan Pawn.
855 if(gameInfo.variant == VariantSuper) goto Amazon;
856 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
857 callback(board, flags,
858 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
859 rf, ff, rf + 1, ff, closure);
860 for (s = -1; s <= 1; s += 2) {
861 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
862 callback(board, flags,
863 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
864 rf, ff, rf + 1, ff + s, closure);
865 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
866 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
871 if(gameInfo.variant == VariantSuper) goto Amazon;
872 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
873 callback(board, flags,
874 rf <= promoRank ? BlackPromotion : NormalMove,
875 rf, ff, rf - 1, ff, closure);
876 for (s = -1; s <= 1; s += 2) {
877 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
878 callback(board, flags,
879 rf <= promoRank ? BlackPromotion : NormalMove,
880 rf, ff, rf - 1, ff + s, closure);
881 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
882 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
886 case SHOGI WhiteNothing:
887 case SHOGI BlackNothing:
888 case SHOGI WhiteLion:
889 case SHOGI BlackLion:
892 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
893 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
894 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
895 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
896 rf, ff, rt, ft, closure);
900 case SHOGI WhiteFalcon:
901 case SHOGI BlackFalcon:
902 case SHOGI WhitePDagger:
903 case SHOGI BlackPDagger:
904 SlideSideways(board, flags, rf, ff, callback, closure);
905 StepVertical(board, flags, rf, ff, callback, closure);
908 case SHOGI WhiteCobra:
909 case SHOGI BlackCobra:
910 StepVertical(board, flags, rf, ff, callback, closure);
913 case SHOGI (PROMOTED WhiteFerz):
914 if(gameInfo.variant == VariantShogi) goto WhiteGold;
915 case SHOGI (PROMOTED BlackFerz):
916 if(gameInfo.variant == VariantShogi) goto BlackGold;
917 case SHOGI WhitePSword:
918 case SHOGI BlackPSword:
919 SlideVertical(board, flags, rf, ff, callback, closure);
920 StepSideways(board, flags, rf, ff, callback, closure);
923 case SHOGI WhiteUnicorn:
924 case SHOGI BlackUnicorn:
925 Ferz(board, flags, rf, ff, callback, closure);
926 StepVertical(board, flags, rf, ff, callback, closure);
930 StepDiagForward(board, flags, rf, ff, callback, closure);
931 StepVertical(board, flags, rf, ff, callback, closure);
935 StepDiagBackward(board, flags, rf, ff, callback, closure);
936 StepVertical(board, flags, rf, ff, callback, closure);
939 case SHOGI WhiteHCrown:
940 case SHOGI BlackHCrown:
941 Bishop(board, flags, rf, ff, callback, closure);
942 SlideSideways(board, flags, rf, ff, callback, closure);
945 case SHOGI WhiteCrown:
946 case SHOGI BlackCrown:
947 Bishop(board, flags, rf, ff, callback, closure);
948 SlideVertical(board, flags, rf, ff, callback, closure);
951 case SHOGI WhiteHorned:
952 Sting(board, flags, rf, ff, 1, 0, callback, closure);
953 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
954 if(killX >= 0) break;
955 Bishop(board, flags, rf, ff, callback, closure);
956 SlideSideways(board, flags, rf, ff, callback, closure);
957 SlideBackward(board, flags, rf, ff, callback, closure);
960 case SHOGI BlackHorned:
961 Sting(board, flags, rf, ff, -1, 0, callback, closure);
962 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
963 if(killX >= 0) break;
964 Bishop(board, flags, rf, ff, callback, closure);
965 SlideSideways(board, flags, rf, ff, callback, closure);
966 SlideForward(board, flags, rf, ff, callback, closure);
969 case SHOGI WhiteEagle:
970 Sting(board, flags, rf, ff, 1, 1, callback, closure);
971 Sting(board, flags, rf, ff, 1, -1, callback, closure);
972 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
973 if(killX >= 0) break;
974 Rook(board, flags, rf, ff, callback, closure);
975 SlideDiagBackward(board, flags, rf, ff, callback, closure);
978 case SHOGI BlackEagle:
979 Sting(board, flags, rf, ff, -1, 1, callback, closure);
980 Sting(board, flags, rf, ff, -1, -1, callback, closure);
981 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
982 if(killX >= 0) break;
983 Rook(board, flags, rf, ff, callback, closure);
984 SlideDiagForward(board, flags, rf, ff, callback, closure);
987 case SHOGI WhiteDolphin:
988 case SHOGI BlackHorse:
989 SlideDiagBackward(board, flags, rf, ff, callback, closure);
990 SlideVertical(board, flags, rf, ff, callback, closure);
993 case SHOGI BlackDolphin:
994 case SHOGI WhiteHorse:
995 SlideDiagForward(board, flags, rf, ff, callback, closure);
996 SlideVertical(board, flags, rf, ff, callback, closure);
999 case SHOGI WhiteLance:
1000 SlideForward(board, flags, rf, ff, callback, closure);
1003 case SHOGI BlackLance:
1004 SlideBackward(board, flags, rf, ff, callback, closure);
1007 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1011 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1024 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1025 Board xqCheckers, nullBoard;
1027 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1028 int rf, int ff, int rt, int ft,
1032 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1034 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1036 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1038 if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1040 if (!(flags & F_IGNORE_CHECK) ) {
1041 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1044 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1045 kings += (board[r][f] == BlackKing);
1049 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1051 check = CheckTest(board, flags, rf, ff, rt, ft,
1052 kind == WhiteCapturesEnPassant ||
1053 kind == BlackCapturesEnPassant);
1054 if(promo) board[rf][ff] = BlackLance;
1057 if (flags & F_ATOMIC_CAPTURE) {
1058 if (board[rt][ft] != EmptySquare ||
1059 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1061 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1062 if (board[rf][ff] == king) return;
1063 for (r = rt-1; r <= rt+1; r++) {
1064 for (f = ft-1; f <= ft+1; f++) {
1065 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1066 board[r][f] == king) return;
1071 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1078 int captures; // [HGM] losers
1079 } LegalityTestClosure;
1082 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1083 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1084 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1085 moves that would destroy your own king. The CASTLE_OK flags are
1086 true if castling is not yet ruled out by a move of the king or
1087 rook. Return TRUE if the player on move is currently in check and
1088 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1090 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1093 int ff, ft, k, left, right, swap;
1094 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1095 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1096 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1100 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1101 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1102 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1104 if (inCheck) return TRUE;
1106 /* Generate castling moves */
1107 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1108 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1111 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1112 if ((flags & F_WHITE_ON_MOVE) &&
1113 (flags & F_WHITE_KCASTLE_OK) &&
1114 board[0][ff] == wKing &&
1115 board[0][ff + 1] == EmptySquare &&
1116 board[0][ff + 2] == EmptySquare &&
1117 board[0][BOARD_RGHT-3] == EmptySquare &&
1118 board[0][BOARD_RGHT-2] == EmptySquare &&
1119 board[0][BOARD_RGHT-1] == WhiteRook &&
1120 castlingRights[0] != NoRights && /* [HGM] check rights */
1121 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1123 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1124 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1125 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1126 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1128 callback(board, flags,
1129 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1130 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1132 if ((flags & F_WHITE_ON_MOVE) &&
1133 (flags & F_WHITE_QCASTLE_OK) &&
1134 board[0][ff] == wKing &&
1135 board[0][ff - 1] == EmptySquare &&
1136 board[0][ff - 2] == EmptySquare &&
1137 board[0][BOARD_LEFT+2] == EmptySquare &&
1138 board[0][BOARD_LEFT+1] == EmptySquare &&
1139 board[0][BOARD_LEFT+0] == WhiteRook &&
1140 castlingRights[1] != NoRights && /* [HGM] check rights */
1141 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1143 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1144 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1145 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1147 callback(board, flags,
1148 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1149 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1151 if (!(flags & F_WHITE_ON_MOVE) &&
1152 (flags & F_BLACK_KCASTLE_OK) &&
1153 board[BOARD_HEIGHT-1][ff] == bKing &&
1154 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1155 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1156 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1157 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1158 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1159 castlingRights[3] != NoRights && /* [HGM] check rights */
1160 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1162 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1163 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1164 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1165 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1167 callback(board, flags,
1168 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1169 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1171 if (!(flags & F_WHITE_ON_MOVE) &&
1172 (flags & F_BLACK_QCASTLE_OK) &&
1173 board[BOARD_HEIGHT-1][ff] == bKing &&
1174 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1175 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1176 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1177 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1178 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1179 castlingRights[4] != NoRights && /* [HGM] check rights */
1180 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1182 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1183 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1184 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1186 callback(board, flags,
1187 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1188 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1192 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1194 /* generate all potential FRC castling moves (KxR), ignoring flags */
1195 /* [HGM] test if the Rooks we find have castling rights */
1196 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1199 if ((flags & F_WHITE_ON_MOVE) != 0) {
1200 ff = castlingRights[2]; /* King file if we have any rights */
1201 if(ff != NoRights && board[0][ff] == WhiteKing) {
1202 if (appData.debugMode) {
1203 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1204 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1206 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1208 right = BOARD_RGHT-2;
1209 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1210 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1211 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1212 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1213 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1214 if(ft != NoRights && board[0][ft] == WhiteRook)
1215 callback(board, flags, WhiteHSideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
1217 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1218 left = BOARD_LEFT+2;
1220 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1221 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1222 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1223 if(ff > BOARD_LEFT+2)
1224 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1225 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1226 if(ft != NoRights && board[0][ft] == WhiteRook)
1227 callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
1230 ff = castlingRights[5]; /* King file if we have any rights */
1231 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1232 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1234 right = BOARD_RGHT-2;
1235 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1236 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1237 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1238 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1239 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1240 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
1241 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
1243 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1244 left = BOARD_LEFT+2;
1246 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1247 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1248 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1249 if(ff > BOARD_LEFT+2)
1250 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1251 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1252 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
1253 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
1269 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1270 int rf, int ff, int rt, int ft,
1275 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1277 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1279 if (rt == cl->rking && ft == cl->fking) {
1280 if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1282 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1284 if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1285 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1286 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1290 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1291 he leave himself in check? Or if rf == -1, is the player on move
1292 in check now? enPassant must be TRUE if the indicated move is an
1293 e.p. capture. The possibility of castling out of a check along the
1294 back rank is not accounted for (i.e., we still return nonzero), as
1295 this is illegal anyway. Return value is the number of times the
1296 king is in check. */
1298 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1300 CheckTestClosure cl;
1301 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1302 ChessSquare captured = EmptySquare, ep, trampled;
1303 /* Suppress warnings on uninitialized variables */
1305 if(gameInfo.variant == VariantXiangqi)
1306 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1307 if(gameInfo.variant == VariantKnightmate)
1308 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1312 captured = board[rf][ft];
1313 board[rf][ft] = EmptySquare;
1315 captured = board[rt][ft];
1316 if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
1318 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1319 board[rt][ft] = board[rf][ff];
1320 board[rf][ff] = EmptySquare;
1322 ep = board[EP_STATUS];
1323 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1324 ChessSquare victim = killX < 0 ? EmptySquare : trampled;
1325 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1326 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1327 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
1328 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1332 /* For compatibility with ICS wild 9, we scan the board in the
1333 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1334 and we test only whether that one is in check. */
1335 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1336 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1337 if (board[cl.rking][cl.fking] == king) {
1339 if(gameInfo.variant == VariantXiangqi) {
1340 /* [HGM] In Xiangqi opposing Kings means check as well */
1342 dir = (king >= BlackPawn) ? -1 : 1;
1343 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1344 board[i][cl.fking] == EmptySquare; i+=dir );
1345 if(i>=0 && i<BOARD_HEIGHT &&
1346 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1349 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1350 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1351 goto undo_move; /* 2-level break */
1358 if(rf != DROP_RANK) // [HGM] drop
1359 board[rf][ff] = board[rt][ft];
1361 board[rf][ft] = captured;
1362 board[rt][ft] = EmptySquare;
1364 if(killX >= 0) board[killY][killX] = trampled;
1365 board[rt][ft] = captured;
1367 board[EP_STATUS] = ep;
1370 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1374 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1375 { // [HGM] put drop legality testing in separate routine for clarity
1377 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1378 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1379 n = PieceToNumber(piece);
1380 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1381 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1382 return ImpossibleMove; // piece not available
1383 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1384 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1385 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1386 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1387 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1388 if(piece == WhitePawn || piece == BlackPawn) {
1390 for(r=1; r<BOARD_HEIGHT-1; r++)
1391 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
1392 // should still test if we mate with this Pawn
1394 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1395 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1397 if( (piece == WhitePawn || piece == BlackPawn) &&
1398 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1399 return IllegalMove; /* no pawn drops on 1st/8th */
1401 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1402 if (!(flags & F_IGNORE_CHECK) &&
1403 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1404 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1407 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1408 int rf, int ff, int rt, int ft,
1412 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1414 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1416 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1417 cl->captures++; // [HGM] losers: count legal captures
1418 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1423 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1425 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1427 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1428 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1429 piece = filterPiece = board[rf][ff];
1430 if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1432 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1433 /* (perhaps we should disallow moves that obviously leave us in check?) */
1434 if(piece == WhiteFalcon || piece == BlackFalcon ||
1435 piece == WhiteCobra || piece == BlackCobra)
1436 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1440 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1441 cl.ft = fFilter = ft;
1442 cl.kind = IllegalMove;
1443 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1444 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1445 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1446 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1447 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1448 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1450 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1451 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1452 if(board[rf][ff] < BlackPawn) { // white
1453 if(rf != 0) return IllegalMove; // must be on back rank
1454 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1455 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1457 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1458 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1459 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1462 if(gameInfo.variant == VariantChu) {
1463 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1464 if(promoChar != '+')
1465 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1466 if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
1467 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1469 if(gameInfo.variant == VariantShogi) {
1470 /* [HGM] Shogi promotions. '=' means defer */
1471 if(rf != DROP_RANK && cl.kind == NormalMove) {
1472 ChessSquare piece = board[rf][ff];
1474 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1475 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1476 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1477 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1478 promoChar = '+'; // allowed ICS notations
1479 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1480 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1481 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1482 else if(flags & F_WHITE_ON_MOVE) {
1483 if( (int) piece < (int) WhiteWazir &&
1484 (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1485 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1486 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1487 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1488 else /* promotion optional, default is defer */
1489 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1490 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1492 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1493 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1494 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1495 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1496 else /* promotion optional, default is defer */
1497 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1498 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1502 if (promoChar != NULLCHAR) {
1503 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1504 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1505 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1506 if(piece == EmptySquare)
1507 cl.kind = ImpossibleMove; // non-existing piece
1508 if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1509 if(promoChar != PieceToChar(BlackKing)) {
1510 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1511 if(piece == BlackLance) cl.kind = ImpossibleMove;
1512 } else { // promotion to King allowed only if we do not haave two yet
1513 int r, f, kings = 0;
1514 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1515 if(kings == 2) cl.kind = IllegalMove;
1517 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1518 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1519 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1520 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1521 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1522 cl.kind = IllegalMove; // promotion to King usually not allowed
1524 cl.kind = IllegalMove;
1534 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1535 int rf, int ff, int rt, int ft,
1539 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1541 register MateTestClosure *cl = (MateTestClosure *) closure;
1546 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1548 MateTest (Board board, int flags)
1551 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1552 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1554 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1555 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1556 nrKing += (board[r][f] == king); // stm has king
1557 if( board[r][f] != EmptySquare ) {
1558 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1563 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1564 case VariantShatranj:
1565 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1569 if(nrKing == 0) return MT_NOKING;
1572 if(myPieces == 1) return MT_BARE;
1575 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1576 // [HGM] 3check: yet to do!
1578 return inCheck ? MT_CHECK : MT_NONE;
1580 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1581 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1582 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1583 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1584 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1585 if(board[n][holdings] != EmptySquare) {
1586 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1587 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1590 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1591 return myPieces == hisPieces ? MT_STALEMATE :
1592 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1593 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1594 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1596 return inCheck ? MT_CHECKMATE
1597 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || gameInfo.variant == VariantShogi) ?
1598 MT_STAINMATE : MT_STALEMATE;
1603 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1604 int rf, int ff, int rt, int ft,
1608 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1610 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1611 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1613 // [HGM] wild: for wild-card pieces rt and rf are dummies
1614 if(piece == WhiteFalcon || piece == BlackFalcon ||
1615 piece == WhiteCobra || piece == BlackCobra)
1618 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1619 || PieceToChar(board[rf][ff]) == '~'
1620 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1622 (cl->rfIn == -1 || cl->rfIn == rf) &&
1623 (cl->ffIn == -1 || cl->ffIn == ff) &&
1624 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1625 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1628 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1629 // [HGM] oneclick: if multiple moves, be sure we remember capture
1630 cl->piece = board[rf][ff];
1633 cl->rt = wildCard ? cl->rtIn : rt;
1634 cl->ft = wildCard ? cl->ftIn : ft;
1637 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1642 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
1644 int illegal = 0; char c = closure->promoCharIn;
1646 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1647 closure->count = closure->captures = 0;
1648 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1649 closure->kind = ImpossibleMove;
1650 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
1651 fFilter = closure->ftIn;
1652 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
1653 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1654 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
1655 closure->count = closure->captures = 0;
1656 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1657 closure->kind = ImpossibleMove;
1658 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1661 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1662 if (closure->count == 0) {
1663 /* See if it's an illegal move due to check */
1665 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1666 if (closure->count == 0) {
1667 /* No, it's not even that */
1668 if(!appData.testLegality && closure->pieceIn != EmptySquare) {
1669 int f, r; // if there is only a single piece of the requested type on the board, use that
1670 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
1671 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1672 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
1673 if(closure->count > 1) illegal = 0; // ambiguous
1675 if(closure->count == 0) {
1676 if (appData.debugMode) { int i, j;
1677 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1678 for(j=0; j<BOARD_WIDTH; j++)
1679 fprintf(debugFP, "%3d", (int) board[i][j]);
1680 fprintf(debugFP, "\n");
1688 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1689 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1690 if(closure->piece < BlackPawn) { // white
1691 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1692 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
1693 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1695 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1696 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
1697 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1700 if(gameInfo.variant == VariantChu) {
1701 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
1703 if(gameInfo.variant == VariantShogi) {
1704 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1705 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1706 ChessSquare piece = closure->piece;
1707 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1708 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1709 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1710 c = '+'; // allowed ICS notations
1711 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1712 else if(flags & F_WHITE_ON_MOVE) {
1713 if( (int) piece < (int) WhiteWazir &&
1714 (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1715 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1716 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1717 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1718 else /* promotion optional, default is defer */
1719 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1720 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1722 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1723 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1724 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1725 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1726 else /* promotion optional, default is defer */
1727 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1728 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1731 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1732 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1734 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1735 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1736 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1737 gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
1738 c = PieceToChar(BlackFerz);
1739 else if(gameInfo.variant == VariantGreat)
1740 c = PieceToChar(BlackMan);
1741 else if(gameInfo.variant == VariantGrand)
1742 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
1744 c = PieceToChar(BlackQueen);
1745 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1746 } else if (c != NULLCHAR) closure->kind = IllegalMove;
1748 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1749 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1750 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1751 if (closure->count > 1) {
1752 closure->kind = AmbiguousMove;
1755 /* Note: If more than one illegal move matches, but no legal
1756 moves, we return IllegalMove, not AmbiguousMove. Caller
1757 can look at closure->count to detect this.
1759 closure->kind = IllegalMove;
1773 } CoordsToAlgebraicClosure;
1775 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1776 ChessMove kind, int rf, int ff,
1777 int rt, int ft, VOIDSTAR closure));
1780 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1782 register CoordsToAlgebraicClosure *cl =
1783 (CoordsToAlgebraicClosure *) closure;
1785 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1786 (board[rf][ff] == cl->piece
1787 || PieceToChar(board[rf][ff]) == '~' &&
1788 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1792 cl->kind = kind; /* this is the move we want */
1794 cl->file++; /* need file to rule out this move */
1798 cl->rank++; /* need rank to rule out this move */
1800 cl->either++; /* rank or file will rule out this move */
1806 /* Convert coordinates to normal algebraic notation.
1807 promoChar must be NULLCHAR or 'x' if not a promotion.
1810 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
1814 char *outp = out, c;
1815 CoordsToAlgebraicClosure cl;
1817 if (rf == DROP_RANK) {
1818 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
1819 /* Bughouse piece drop */
1820 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1825 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1827 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1830 if (promoChar == 'x') promoChar = NULLCHAR;
1831 piece = board[rf][ff];
1832 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1837 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1838 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1839 /* Keep short notation if move is illegal only because it
1840 leaves the player in check, but still return IllegalMove */
1841 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1842 if (kind == IllegalMove) break;
1847 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1848 /* Non-capture; use style "e5" */
1851 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1853 /* Capture; use style "exd5" */
1854 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1855 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
1859 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1861 /* Use promotion suffix style "=Q" */
1863 if (promoChar != NULLCHAR) {
1864 if(IS_SHOGI(gameInfo.variant)) {
1865 /* [HGM] ... but not in Shogi! */
1866 *outp++ = promoChar == '=' ? '=' : '+';
1869 *outp++ = ToUpper(promoChar);
1878 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1879 /* Code added by Tord: FRC castling. */
1880 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1881 (piece == BlackKing && board[rt][ft] == BlackRook)) {
1883 safeStrCpy(out, "O-O", MOVE_LEN);
1885 safeStrCpy(out, "O-O-O", MOVE_LEN);
1886 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1888 /* End of code added by Tord */
1889 /* Test for castling or ICS wild castling */
1890 /* Use style "O-O" (oh-oh) for PGN compatibility */
1891 else if (rf == rt &&
1892 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1893 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
1894 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1895 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1896 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1897 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1899 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1901 /* This notation is always unambiguous, unless there are
1902 kings on both the d and e files, with "wild castling"
1903 possible for the king on the d file and normal castling
1904 possible for the other. ICS rules for wild 9
1905 effectively make castling illegal for either king in
1906 this situation. So I am not going to worry about it;
1907 I'll just generate an ambiguous O-O in this case.
1909 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1912 /* else fall through */
1917 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1918 cl.ft = fFilter = ft;
1920 cl.kind = IllegalMove;
1921 cl.rank = cl.file = cl.either = 0;
1922 c = PieceToChar(piece) ;
1923 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
1925 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1926 /* Generate pretty moves for moving into check, but
1927 still return IllegalMove.
1929 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
1930 if (cl.kind == IllegalMove) break;
1931 cl.kind = IllegalMove;
1934 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1935 else "Ngf3" or "Ngxf7",
1936 else "N1f3" or "N5xf7",
1937 else "Ng1f3" or "Ng5xf7".
1939 if( c == '~' || c == '+') {
1940 /* [HGM] print nonexistent piece as its demoted version */
1941 piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
1943 if(c=='+') *outp++ = c;
1944 *outp++ = ToUpper(PieceToChar(piece));
1946 if (cl.file || (cl.either && !cl.rank)) {
1952 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1955 if(board[rt][ft] != EmptySquare)
1961 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1962 if (IS_SHOGI(gameInfo.variant)) {
1963 /* [HGM] in Shogi non-pawns can promote */
1964 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
1966 else if (gameInfo.variant != VariantSuper && promoChar &&
1967 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
1969 *outp++ = ToUpper(promoChar);
1971 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
1973 *outp++ = ToUpper(promoChar);
1979 /* Moving a nonexistent piece */
1983 /* Not a legal move, even ignoring check.
1984 If there was a piece on the from square,
1985 use style "Ng1g3" or "Ng1xe8";
1986 if there was a pawn or nothing (!),
1987 use style "g1g3" or "g1xe8". Use "x"
1988 if a piece was on the to square, even
1989 a piece of the same color.
1993 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1995 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
1996 c += (board[r][f] == piece); // count on-board pieces of given type
1997 *outp++ = ToUpper(PieceToChar(piece));
1999 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2003 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2005 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2009 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2010 /* Use promotion suffix style "=Q" */
2011 if (promoChar != NULLCHAR && promoChar != 'x') {
2013 *outp++ = ToUpper(promoChar);
2020 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2029 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2031 int preyStackPointer, chaseStackPointer;
2034 unsigned char rf, ff, rt, ft;
2038 unsigned char rank, file;
2044 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2046 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2047 int rf, int ff, int rt, int ft,
2051 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2052 { // For adding captures that can lead to chase indictment to the chaseStack
2053 if(board[rt][ft] == EmptySquare) return; // non-capture
2054 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2055 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2056 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2057 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2058 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2059 chaseStack[chaseStackPointer].rf = rf;
2060 chaseStack[chaseStackPointer].ff = ff;
2061 chaseStack[chaseStackPointer].rt = rt;
2062 chaseStack[chaseStackPointer].ft = ft;
2063 chaseStackPointer++;
2066 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2067 int rf, int ff, int rt, int ft,
2071 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2072 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2074 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2076 if(board[rt][ft] == EmptySquare) return; // no capture
2077 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2078 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2080 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2081 for(i=0; i<chaseStackPointer; i++) {
2082 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2083 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2084 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2085 chaseStack[i] = chaseStack[--chaseStackPointer];
2091 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2092 int rf, int ff, int rt, int ft,
2096 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2097 { // for determining if a piece (given through the closure) is protected
2098 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2100 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2101 if(appData.debugMode && board[rt][ft] != EmptySquare)
2102 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2105 extern char moveList[MAX_MOVES][MOVE_LEN];
2108 PerpetualChase (int first, int last)
2109 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2112 ChessSquare captured;
2114 preyStackPointer = 0; // clear stack of chased pieces
2115 for(i=first; i<last; i+=2) { // for all positions with same side to move
2116 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2117 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2118 // determine all captures possible after the move, and put them on chaseStack
2119 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2120 if(appData.debugMode) { int n;
2121 for(n=0; n<chaseStackPointer; n++)
2122 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2123 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2124 fprintf(debugFP, ": all capts\n");
2126 // determine all captures possible before the move, and delete them from chaseStack
2127 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2128 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2129 cl.rt = moveList[i][3]-ONE;
2130 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2131 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2132 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2133 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2134 if(appData.debugMode) { int n;
2135 for(n=0; n<chaseStackPointer; n++)
2136 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2137 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2138 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2140 // chaseSack now contains all captures made possible by the move
2141 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2142 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2143 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2145 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2146 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2148 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2149 continue; // C or H attack on R is always chase; leave on chaseStack
2151 if(attacker == victim) {
2152 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2153 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2154 // we can capture back with equal piece, so this is no chase but a sacrifice
2155 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2156 j--; /* ! */ continue;
2161 // the attack is on a lower piece, or on a pinned or blocked equal one
2162 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2163 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2164 // test if the victim is protected by a true protector. First make the capture.
2165 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2166 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2167 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2168 // Then test if the opponent can recapture
2169 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2170 cl.rt = chaseStack[j].rt;
2171 cl.ft = chaseStack[j].ft;
2172 if(appData.debugMode) {
2173 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2175 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2176 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2177 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2178 // unmake the capture
2179 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2180 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2181 // if a recapture was found, piece is protected, and we are not chasing it.
2182 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2183 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2187 // chaseStack now contains all moves that chased
2188 if(appData.debugMode) { int n;
2189 for(n=0; n<chaseStackPointer; n++)
2190 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2191 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2192 fprintf(debugFP, ": chases\n");
2194 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2195 for(j=0; j<chaseStackPointer; j++) {
2196 preyStack[j].rank = chaseStack[j].rt;
2197 preyStack[j].file = chaseStack[j].ft;
2199 preyStackPointer = chaseStackPointer;
2202 for(j=0; j<chaseStackPointer; j++) {
2203 for(k=0; k<preyStackPointer; k++) {
2204 // search the victim of each chase move on the preyStack (first occurrence)
2205 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2206 if(k < tail) break; // piece was already identified as still being chased
2207 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2208 preyStack[tail] = preyStack[k]; // by swapping
2209 preyStack[k] = preyStack[preyStackPointer];
2215 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2216 if(appData.debugMode) { int n;
2217 for(n=0; n<preyStackPointer; n++)
2218 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2219 fprintf(debugFP, "always chased upto ply %d\n", i);
2221 // now adjust the location of the chased pieces according to opponent move
2222 for(j=0; j<preyStackPointer; j++) {
2223 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2224 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2225 preyStack[j].rank = moveList[i+1][3]-ONE;
2226 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2231 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2232 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the