Implement Chu Shogi
[xboard.git] / moves.c
1 /*
2  * moves.c - Move generation and checking
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
9  *
10  * Enhancements Copyright 2005 Alessandro Scotti
11  *
12  * The following terms apply to Digital Equipment Corporation's copyright
13  * interest in XBoard:
14  * ------------------------------------------------------------------------
15  * All Rights Reserved
16  *
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.
24  *
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
31  * SOFTWARE.
32  * ------------------------------------------------------------------------
33  *
34  * The following terms apply to the enhanced version of XBoard
35  * distributed by the Free Software Foundation:
36  * ------------------------------------------------------------------------
37  *
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.
42  *
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.
47  *
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/.  *
50  *
51  *------------------------------------------------------------------------
52  ** See the file ChangeLog for a revision history.  */
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #if HAVE_STRING_H
58 # include <string.h>
59 #else /* not HAVE_STRING_H */
60 # include <strings.h>
61 #endif /* not HAVE_STRING_H */
62 #include "common.h"
63 #include "backend.h"
64 #include "moves.h"
65 #include "parser.h"
66
67 int WhitePiece P((ChessSquare));
68 int BlackPiece P((ChessSquare));
69 int SameColor P((ChessSquare, ChessSquare));
70 int PosFlags(int index);
71
72 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
73 int quickFlag;
74
75 int
76 WhitePiece (ChessSquare piece)
77 {
78     return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
79 }
80
81 int
82 BlackPiece (ChessSquare piece)
83 {
84     return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
85 }
86
87 #if 0
88 int
89 SameColor (ChessSquare piece1, ChessSquare piece2)
90 {
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);
99 }
100 #else
101 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
102 #endif
103
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',
109                         'x' };
110 char pieceNickName[EmptySquare];
111
112 char
113 PieceToChar (ChessSquare p)
114 {
115     if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
116     return pieceToChar[(int) p];
117 }
118
119 int
120 PieceToNumber (ChessSquare p)  /* [HGM] holdings: count piece type, ignoring non-participating piece types */
121 {
122     int i=0;
123     ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
124
125     while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
126     return i;
127 }
128
129 ChessSquare
130 CharToPiece (int c)
131 {
132      int 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;
138      return EmptySquare;
139 }
140
141 void
142 CopyBoard (Board to, Board from)
143 {
144     int i, j;
145
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
153 }
154
155 int
156 CompareBoards (Board board1, Board board2)
157 {
158     int i, j;
159
160     for (i = 0; i < BOARD_HEIGHT; i++)
161       for (j = 0; j < BOARD_WIDTH; j++) {
162           if (board1[i][j] != board2[i][j])
163             return FALSE;
164     }
165     return TRUE;
166 }
167
168
169 /* Call callback once for each pseudo-legal move in the given
170    position, except castling moves. A move is pseudo-legal if it is
171    legal, or if it would be legal except that it leaves the king in
172    check.  In the arguments, epfile is EP_NONE if the previous move
173    was not a double pawn push, or the file 0..7 if it was, or
174    EP_UNKNOWN if we don't know and want to allow all e.p. captures.
175    Promotion moves generated are to Queen only.
176 */
177 void
178 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
179 // speed: only do moves with this piece type
180 {
181     int rf, ff;
182     int i, j, d, s, fs, rs, rt, ft, m;
183     int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
184     int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 3 : 1;
185
186     for (rf = 0; rf < BOARD_HEIGHT; rf++)
187       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
188           ChessSquare piece;
189           int rookRange;
190
191           if(board[rf][ff] == EmptySquare) continue;
192           if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
193           rookRange = 1000;
194           m = 0; piece = board[rf][ff];
195           if(PieceToChar(piece) == '~')
196                  piece = (ChessSquare) ( DEMOTED piece );
197           if(filter != EmptySquare && piece != filter) continue;
198           if(gameInfo.variant == VariantShogi)
199                  piece = (ChessSquare) ( SHOGI piece );
200
201           switch ((int)piece) {
202             /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
203             default:
204               /* can't happen ([HGM] except for faries...) */
205               break;
206
207              case WhitePawn:
208               if(gameInfo.variant == VariantXiangqi) {
209                   /* [HGM] capture and move straight ahead in Xiangqi */
210                   if (rf < BOARD_HEIGHT-1 &&
211                            !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
212                            callback(board, flags, NormalMove,
213                                     rf, ff, rf + 1, ff, closure);
214                   }
215                   /* and move sideways when across the river */
216                   for (s = -1; s <= 1; s += 2) {
217                       if (rf >= BOARD_HEIGHT>>1 &&
218                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
219                           !WhitePiece(board[rf][ff+s]) ) {
220                            callback(board, flags, NormalMove,
221                                     rf, ff, rf, ff+s, closure);
222                       }
223                   }
224                   break;
225               }
226               if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
227                   callback(board, flags,
228                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
229                            rf, ff, rf + 1, ff, closure);
230               }
231               if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
232                   gameInfo.variant != VariantShatranj && /* [HGM] */
233                   gameInfo.variant != VariantCourier  && /* [HGM] */
234                   board[rf+2][ff] == EmptySquare ) {
235                       callback(board, flags, NormalMove,
236                                rf, ff, rf+2, ff, closure);
237               }
238               for (s = -1; s <= 1; s += 2) {
239                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
240                       ((flags & F_KRIEGSPIEL_CAPTURE) ||
241                        BlackPiece(board[rf + 1][ff + s]))) {
242                       callback(board, flags,
243                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
244                                rf, ff, rf + 1, ff + s, closure);
245                   }
246                   if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
247                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
248                           (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
249                           board[rf][ff + s] == BlackPawn &&
250                           board[rf+1][ff + s] == EmptySquare) {
251                           callback(board, flags, WhiteCapturesEnPassant,
252                                    rf, ff, rf+1, ff + s, closure);
253                       }
254                   }
255               }
256               break;
257
258             case BlackPawn:
259               if(gameInfo.variant == VariantXiangqi) {
260                   /* [HGM] capture straight ahead in Xiangqi */
261                   if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
262                            callback(board, flags, NormalMove,
263                                     rf, ff, rf - 1, ff, closure);
264                   }
265                   /* and move sideways when across the river */
266                   for (s = -1; s <= 1; s += 2) {
267                       if (rf < BOARD_HEIGHT>>1 &&
268                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
269                           !BlackPiece(board[rf][ff+s]) ) {
270                            callback(board, flags, NormalMove,
271                                     rf, ff, rf, ff+s, closure);
272                       }
273                   }
274                   break;
275               }
276               if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
277                   callback(board, flags,
278                            rf <= promoRank ? BlackPromotion : NormalMove,
279                            rf, ff, rf - 1, ff, closure);
280               }
281               if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
282                   gameInfo.variant != VariantShatranj && /* [HGM] */
283                   gameInfo.variant != VariantCourier  && /* [HGM] */
284                   board[rf-2][ff] == EmptySquare) {
285                   callback(board, flags, NormalMove,
286                            rf, ff, rf-2, ff, closure);
287               }
288               for (s = -1; s <= 1; s += 2) {
289                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
290                       ((flags & F_KRIEGSPIEL_CAPTURE) ||
291                        WhitePiece(board[rf - 1][ff + s]))) {
292                       callback(board, flags,
293                                rf <= promoRank ? BlackPromotion : NormalMove,
294                                rf, ff, rf - 1, ff + s, closure);
295                   }
296                   if (rf < BOARD_HEIGHT>>1) {
297                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
298                           (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
299                           board[rf][ff + s] == WhitePawn &&
300                           board[rf-1][ff + s] == EmptySquare) {
301                           callback(board, flags, BlackCapturesEnPassant,
302                                    rf, ff, rf-1, ff + s, closure);
303                       }
304                   }
305               }
306               break;
307
308             case WhiteUnicorn:
309             case BlackUnicorn:
310             case WhiteKnight:
311             case BlackKnight:
312             mounted:
313               for (i = -1; i <= 1; i += 2)
314                 for (j = -1; j <= 1; j += 2)
315                   for (s = 1; s <= 2; s++) {
316                       rt = rf + i*s;
317                       ft = ff + j*(3-s);
318                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
319                           && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
320                           && !SameColor(board[rf][ff], board[rt][ft]))
321                       callback(board, flags, NormalMove,
322                                rf, ff, rt, ft, closure);
323                   }
324               break;
325
326             case SHOGI WhiteKnight:
327               for (s = -1; s <= 1; s += 2) {
328                   if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
329                       !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
330                       callback(board, flags, NormalMove,
331                                rf, ff, rf + 2, ff + s, closure);
332                   }
333               }
334               break;
335
336             case SHOGI BlackKnight:
337               for (s = -1; s <= 1; s += 2) {
338                   if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
339                       !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
340                       callback(board, flags, NormalMove,
341                                rf, ff, rf - 2, ff + s, closure);
342                   }
343               }
344               break;
345
346             case WhiteCannon:
347             case BlackCannon:
348               for (d = 0; d <= 1; d++)
349                 for (s = -1; s <= 1; s += 2) {
350                   m = 0;
351                   for (i = 1;; i++) {
352                       rt = rf + (i * s) * d;
353                       ft = ff + (i * s) * (1 - d);
354                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
355                       if (m == 0 && board[rt][ft] == EmptySquare)
356                                  callback(board, flags, NormalMove,
357                                           rf, ff, rt, ft, closure);
358                       if (m == 1 && board[rt][ft] != EmptySquare &&
359                           !SameColor(board[rf][ff], board[rt][ft]) )
360                                  callback(board, flags, NormalMove,
361                                           rf, ff, rt, ft, closure);
362                       if (board[rt][ft] != EmptySquare && m++) break;
363                   }
364                 }
365               break;
366
367             /* Gold General (and all its promoted versions) . First do the */
368             /* diagonal forward steps, then proceed as normal Wazir        */
369             case SHOGI WhiteWazir:
370             case SHOGI (PROMOTED WhitePawn):
371             case SHOGI (PROMOTED WhiteKnight):
372             case SHOGI (PROMOTED WhiteQueen):
373             case SHOGI (PROMOTED WhiteFerz):
374               for (s = -1; s <= 1; s += 2) {
375                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
376                       !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
377                       callback(board, flags, NormalMove,
378                                rf, ff, rf + 1, ff + s, closure);
379                   }
380               }
381               goto finishGold;
382
383             case SHOGI BlackWazir:
384             case SHOGI (PROMOTED BlackPawn):
385             case SHOGI (PROMOTED BlackKnight):
386             case SHOGI (PROMOTED BlackQueen):
387             case SHOGI (PROMOTED BlackFerz):
388               for (s = -1; s <= 1; s += 2) {
389                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
390                       !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
391                       callback(board, flags, NormalMove,
392                                rf, ff, rf - 1, ff + s, closure);
393                   }
394               }
395
396             case WhiteWazir:
397             case BlackWazir:
398             finishGold:
399               for (d = 0; d <= 1; d++)
400                 for (s = -1; s <= 1; s += 2) {
401                       rt = rf + s * d;
402                       ft = ff + s * (1 - d);
403                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
404                           && !SameColor(board[rf][ff], board[rt][ft]) &&
405                           (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
406                                callback(board, flags, NormalMove,
407                                         rf, ff, rt, ft, closure);
408                       }
409               break;
410
411             case WhiteAlfil:
412             case BlackAlfil:
413                 /* [HGM] support Shatranj pieces */
414                 for (rs = -1; rs <= 1; rs += 2)
415                   for (fs = -1; fs <= 1; fs += 2) {
416                       rt = rf + 2 * rs;
417                       ft = ff + 2 * fs;
418                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
419                           && ( gameInfo.variant != VariantXiangqi ||
420                                board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
421
422                           && !SameColor(board[rf][ff], board[rt][ft]))
423                                callback(board, flags, NormalMove,
424                                         rf, ff, rt, ft, closure);
425                       if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier
426                                                              || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
427                       rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
428                       ft = ff + fs;
429                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
430                           && !SameColor(board[rf][ff], board[rt][ft]))
431                                callback(board, flags, NormalMove,
432                                         rf, ff, rt, ft, closure);
433                   }
434                 if(gameInfo.variant == VariantSpartan)
435                    for(fs = -1; fs <= 1; fs += 2) {
436                       ft = ff + fs;
437                       if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
438                                callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
439                    }
440                 break;
441
442             /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
443             case WhiteCardinal:
444             case BlackCardinal:
445               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
446                 for (s = -2; s <= 2; s += 4) {
447                       rt = rf + s * d;
448                       ft = ff + s * (1 - d);
449                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
450                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
451                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
452                   }
453
454             /* Shogi Dragon Horse has to continue with Wazir after Bishop */
455             case SHOGI WhiteCardinal:
456             case SHOGI BlackCardinal:
457               m++;
458
459             /* Capablanca Archbishop continues as Knight                  */
460             case WhiteAngel:
461             case BlackAngel:
462               m++;
463
464             /* Shogi Bishops are ordinary Bishops */
465             case SHOGI WhiteBishop:
466             case SHOGI BlackBishop:
467             case WhiteBishop:
468             case BlackBishop:
469               for (rs = -1; rs <= 1; rs += 2)
470                 for (fs = -1; fs <= 1; fs += 2)
471                   for (i = 1;; i++) {
472                       rt = rf + (i * rs);
473                       ft = ff + (i * fs);
474                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
475                       if (SameColor(board[rf][ff], board[rt][ft])) break;
476                       callback(board, flags, NormalMove,
477                                rf, ff, rt, ft, closure);
478                       if (board[rt][ft] != EmptySquare) break;
479                   }
480                 if(m==1) goto mounted;
481                 if(m==2) goto finishGold;
482                 /* Bishop falls through */
483               break;
484
485             /* Shogi Lance is unlike anything, and asymmetric at that */
486             case SHOGI WhiteQueen:
487               for(i = 1;; i++) {
488                       rt = rf + i;
489                       ft = ff;
490                       if (rt >= BOARD_HEIGHT) break;
491                       if (SameColor(board[rf][ff], board[rt][ft])) break;
492                       callback(board, flags, NormalMove,
493                                rf, ff, rt, ft, closure);
494                       if (board[rt][ft] != EmptySquare) break;
495               }
496               break;
497
498             case SHOGI BlackQueen:
499               for(i = 1;; i++) {
500                       rt = rf - i;
501                       ft = ff;
502                       if (rt < 0) break;
503                       if (SameColor(board[rf][ff], board[rt][ft])) break;
504                       callback(board, flags, NormalMove,
505                                rf, ff, rt, ft, closure);
506                       if (board[rt][ft] != EmptySquare) break;
507               }
508               break;
509
510             /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
511             case WhiteDragon:
512             case BlackDragon:
513               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
514                 for (s = -2; s <= 2; s += 4) {
515                       rt = rf + s * d;
516                       ft = ff + s * (1 - d);
517                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT || board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
518                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
519                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
520                   }
521               if(gameInfo.variant == VariantSpartan) rookRange = 2; // in Spartan Chess restrict range to modern Dababba
522               goto doRook;
523
524             /* Shogi Dragon King has to continue as Ferz after Rook moves */
525             case SHOGI WhiteDragon:
526             case SHOGI BlackDragon:
527               m++;
528
529             /* Capablanca Chancellor sets flag to continue as Knight      */
530             case WhiteMarshall:
531             case BlackMarshall:
532               m++;
533               m += (gameInfo.variant == VariantSpartan); // in Spartan Chess Chancellor is used for Dragon King.
534
535             /* Shogi Rooks are ordinary Rooks */
536             case SHOGI WhiteRook:
537             case SHOGI BlackRook:
538             case WhiteRook:
539             case BlackRook:
540           doRook:
541               for (d = 0; d <= 1; d++)
542                 for (s = -1; s <= 1; s += 2)
543                   for (i = 1;; i++) {
544                       rt = rf + (i * s) * d;
545                       ft = ff + (i * s) * (1 - d);
546                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
547                       if (SameColor(board[rf][ff], board[rt][ft])) break;
548                       callback(board, flags, NormalMove,
549                                rf, ff, rt, ft, closure);
550                       if (board[rt][ft] != EmptySquare || i == rookRange) break;
551                   }
552                 if(m==1) goto mounted;
553                 if(m==2) goto finishSilver;
554               break;
555
556             case WhiteQueen:
557             case BlackQueen:
558               for (rs = -1; rs <= 1; rs++)
559                 for (fs = -1; fs <= 1; fs++) {
560                     if (rs == 0 && fs == 0) continue;
561                     for (i = 1;; i++) {
562                         rt = rf + (i * rs);
563                         ft = ff + (i * fs);
564                         if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
565                         if (SameColor(board[rf][ff], board[rt][ft])) break;
566                         callback(board, flags, NormalMove,
567                                  rf, ff, rt, ft, closure);
568                         if (board[rt][ft] != EmptySquare) break;
569                     }
570                 }
571               break;
572
573             /* Shogi Pawn and Silver General: first the Pawn move,    */
574             /* then the General continues like a Ferz                 */
575             case WhiteMan:
576                 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
577             case SHOGI WhitePawn:
578             case SHOGI WhiteFerz:
579                   if (rf < BOARD_HEIGHT-1 &&
580                            !SameColor(board[rf][ff], board[rf + 1][ff]) )
581                            callback(board, flags, NormalMove,
582                                     rf, ff, rf + 1, ff, closure);
583               if(piece != SHOGI WhitePawn) goto finishSilver;
584               break;
585
586             case BlackMan:
587                 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
588             case SHOGI BlackPawn:
589             case SHOGI BlackFerz:
590                   if (rf > 0 &&
591                            !SameColor(board[rf][ff], board[rf - 1][ff]) )
592                            callback(board, flags, NormalMove,
593                                     rf, ff, rf - 1, ff, closure);
594               if(piece == SHOGI BlackPawn) break;
595
596             case WhiteFerz:
597             case BlackFerz:
598             finishSilver:
599                 /* [HGM] support Shatranj pieces */
600                 for (rs = -1; rs <= 1; rs += 2)
601                   for (fs = -1; fs <= 1; fs += 2) {
602                       rt = rf + rs;
603                       ft = ff + fs;
604                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
605                       if (!SameColor(board[rf][ff], board[rt][ft]) &&
606                           (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
607                                callback(board, flags, NormalMove,
608                                         rf, ff, rt, ft, closure);
609                   }
610                 break;
611
612             case WhiteSilver:
613             case BlackSilver:
614                 m++; // [HGM] superchess: use for Centaur
615             commoner:
616             case SHOGI WhiteKing:
617             case SHOGI BlackKing:
618             case WhiteKing:
619             case BlackKing:
620 //            walking:
621               for (i = -1; i <= 1; i++)
622                 for (j = -1; j <= 1; j++) {
623                     if (i == 0 && j == 0) continue;
624                     rt = rf + i;
625                     ft = ff + j;
626                     if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
627                     if (SameColor(board[rf][ff], board[rt][ft])) continue;
628                     callback(board, flags, NormalMove,
629                              rf, ff, rt, ft, closure);
630                 }
631                 if(m==1) goto mounted;
632               break;
633
634             case WhiteNightrider:
635             case BlackNightrider:
636               for (i = -1; i <= 1; i += 2)
637                 for (j = -1; j <= 1; j += 2)
638                   for (s = 1; s <= 2; s++) {  int k;
639                     for(k=1;; k++) {
640                       rt = rf + k*i*s;
641                       ft = ff + k*j*(3-s);
642                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
643                       if (SameColor(board[rf][ff], board[rt][ft])) break;
644                       callback(board, flags, NormalMove,
645                                rf, ff, rt, ft, closure);
646                       if (board[rt][ft] != EmptySquare) break;
647                     }
648                   }
649               break;
650
651             Amazon:
652               /* First do Bishop,then continue like Chancellor */
653               for (rs = -1; rs <= 1; rs += 2)
654                 for (fs = -1; fs <= 1; fs += 2)
655                   for (i = 1;; i++) {
656                       rt = rf + (i * rs);
657                       ft = ff + (i * fs);
658                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
659                       if (SameColor(board[rf][ff], board[rt][ft])) break;
660                       callback(board, flags, NormalMove,
661                                rf, ff, rt, ft, closure);
662                       if (board[rt][ft] != EmptySquare) break;
663                   }
664               m++;
665               goto doRook;
666
667             // Use Lance as Berolina / Spartan Pawn.
668             case WhiteLance:
669               if(gameInfo.variant == VariantSuper) goto Amazon;
670               if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
671                   callback(board, flags,
672                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
673                            rf, ff, rf + 1, ff, closure);
674               for (s = -1; s <= 1; s += 2) {
675                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
676                       callback(board, flags,
677                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
678                                rf, ff, rf + 1, ff + s, closure);
679                   if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
680                       callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
681               }
682               break;
683
684             case BlackLance:
685               if(gameInfo.variant == VariantSuper) goto Amazon;
686               if (rf > 0 && WhitePiece(board[rf - 1][ff]))
687                   callback(board, flags,
688                            rf <= promoRank ? BlackPromotion : NormalMove,
689                            rf, ff, rf - 1, ff, closure);
690               for (s = -1; s <= 1; s += 2) {
691                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
692                       callback(board, flags,
693                                rf <= promoRank ? BlackPromotion : NormalMove,
694                                rf, ff, rf - 1, ff + s, closure);
695                   if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
696                       callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
697               }
698             break;
699
700             case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
701             case BlackFalcon:
702             case WhiteCobra:
703             case BlackCobra:
704               callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
705               break;
706
707           }
708       }
709 }
710
711
712 typedef struct {
713     MoveCallback cb;
714     VOIDSTAR cl;
715 } GenLegalClosure;
716
717 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
718 Board xqCheckers, nullBoard;
719
720 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
721                                 int rf, int ff, int rt, int ft,
722                                 VOIDSTAR closure));
723
724 void
725 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
726 {
727     register GenLegalClosure *cl = (GenLegalClosure *) closure;
728
729     if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
730
731     if (!(flags & F_IGNORE_CHECK) ) {
732       int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
733       if(promo) {
734             int r, f, kings=0;
735             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT;       f++)
736                 kings += (board[r][f] == BlackKing);
737             if(kings >= 2)
738               promo = 0;
739             else
740                 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
741         }
742         check = CheckTest(board, flags, rf, ff, rt, ft,
743                   kind == WhiteCapturesEnPassant ||
744                   kind == BlackCapturesEnPassant);
745         if(promo) board[rf][ff] = BlackLance;
746       if(check) return;
747     }
748     if (flags & F_ATOMIC_CAPTURE) {
749       if (board[rt][ft] != EmptySquare ||
750           kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
751         int r, f;
752         ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
753         if (board[rf][ff] == king) return;
754         for (r = rt-1; r <= rt+1; r++) {
755           for (f = ft-1; f <= ft+1; f++) {
756             if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
757                 board[r][f] == king) return;
758           }
759         }
760       }
761     }
762     cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
763 }
764
765
766 typedef struct {
767     int rf, ff, rt, ft;
768     ChessMove kind;
769     int captures; // [HGM] losers
770 } LegalityTestClosure;
771
772
773 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
774    F_IGNORE_CHECK is set in the flags, omit moves that would leave the
775    king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
776    moves that would destroy your own king.  The CASTLE_OK flags are
777    true if castling is not yet ruled out by a move of the king or
778    rook.  Return TRUE if the player on move is currently in check and
779    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */
780 int
781 GenLegal (Board board, int  flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
782 {
783     GenLegalClosure cl;
784     int ff, ft, k, left, right, swap;
785     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
786     ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
787     int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
788
789     cl.cb = callback;
790     cl.cl = closure;
791     xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
792     if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
793     GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
794
795     if (inCheck) return TRUE;
796
797     /* Generate castling moves */
798     if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
799         wKing = WhiteUnicorn; bKing = BlackUnicorn;
800     }
801
802     for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
803         if ((flags & F_WHITE_ON_MOVE) &&
804             (flags & F_WHITE_KCASTLE_OK) &&
805             board[0][ff] == wKing &&
806             board[0][ff + 1] == EmptySquare &&
807             board[0][ff + 2] == EmptySquare &&
808             board[0][BOARD_RGHT-3] == EmptySquare &&
809             board[0][BOARD_RGHT-2] == EmptySquare &&
810             board[0][BOARD_RGHT-1] == WhiteRook &&
811             castlingRights[0] != NoRights && /* [HGM] check rights */
812             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
813             (ignoreCheck ||
814              (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
815               !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
816               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
817               !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
818
819             callback(board, flags,
820                      ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
821                      0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
822         }
823         if ((flags & F_WHITE_ON_MOVE) &&
824             (flags & F_WHITE_QCASTLE_OK) &&
825             board[0][ff] == wKing &&
826             board[0][ff - 1] == EmptySquare &&
827             board[0][ff - 2] == EmptySquare &&
828             board[0][BOARD_LEFT+2] == EmptySquare &&
829             board[0][BOARD_LEFT+1] == EmptySquare &&
830             board[0][BOARD_LEFT+0] == WhiteRook &&
831             castlingRights[1] != NoRights && /* [HGM] check rights */
832             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
833             (ignoreCheck ||
834              (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
835               !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
836               !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
837
838             callback(board, flags,
839                      ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
840                      0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
841         }
842         if (!(flags & F_WHITE_ON_MOVE) &&
843             (flags & F_BLACK_KCASTLE_OK) &&
844             board[BOARD_HEIGHT-1][ff] == bKing &&
845             board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
846             board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
847             board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
848             board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
849             board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
850             castlingRights[3] != NoRights && /* [HGM] check rights */
851             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
852             (ignoreCheck ||
853              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
854               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
855               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
856               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
857
858             callback(board, flags,
859                      ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
860                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
861         }
862         if (!(flags & F_WHITE_ON_MOVE) &&
863             (flags & F_BLACK_QCASTLE_OK) &&
864             board[BOARD_HEIGHT-1][ff] == bKing &&
865             board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
866             board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
867             board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
868             board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
869             board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
870             castlingRights[4] != NoRights && /* [HGM] check rights */
871             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
872             (ignoreCheck ||
873              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
874               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
875               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
876
877             callback(board, flags,
878                      ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
879                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
880         }
881     }
882
883   if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
884
885     /* generate all potential FRC castling moves (KxR), ignoring flags */
886     /* [HGM] test if the Rooks we find have castling rights */
887     /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
888
889
890     if ((flags & F_WHITE_ON_MOVE) != 0) {
891         ff = castlingRights[2]; /* King file if we have any rights */
892         if(ff != NoRights && board[0][ff] == WhiteKing) {
893     if (appData.debugMode) {
894         fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
895                 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
896     }
897             ft = castlingRights[0]; /* Rook file if we have H-side rights */
898             left  = ff+1;
899             right = BOARD_RGHT-2;
900             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
901             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
902                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
903             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
904                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
905             if(ft != NoRights && board[0][ft] == WhiteRook)
906                 callback(board, flags, WhiteHSideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
907
908             ft = castlingRights[1]; /* Rook file if we have A-side rights */
909             left  = BOARD_LEFT+2;
910             right = ff-1;
911             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
912             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
913                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
914             if(ff > BOARD_LEFT+2)
915             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
916                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
917             if(ft != NoRights && board[0][ft] == WhiteRook)
918                 callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
919         }
920     } else {
921         ff = castlingRights[5]; /* King file if we have any rights */
922         if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
923             ft = castlingRights[3]; /* Rook file if we have H-side rights */
924             left  = ff+1;
925             right = BOARD_RGHT-2;
926             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
927             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
928                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
929             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
930                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
931             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
932                 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
933
934             ft = castlingRights[4]; /* Rook file if we have A-side rights */
935             left  = BOARD_LEFT+2;
936             right = ff-1;
937             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
938             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
939                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
940             if(ff > BOARD_LEFT+2)
941             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
942                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
943             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
944                 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
945         }
946     }
947
948   }
949
950     return FALSE;
951 }
952
953
954 typedef struct {
955     int rking, fking;
956     int check;
957 } CheckTestClosure;
958
959
960 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
961                                  int rf, int ff, int rt, int ft,
962                                  VOIDSTAR closure));
963
964
965 void
966 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
967 {
968     register CheckTestClosure *cl = (CheckTestClosure *) closure;
969
970     if (rt == cl->rking && ft == cl->fking) {
971         if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
972         cl->check++;
973         xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
974     }
975 }
976
977
978 /* If the player on move were to move from (rf, ff) to (rt, ft), would
979    he leave himself in check?  Or if rf == -1, is the player on move
980    in check now?  enPassant must be TRUE if the indicated move is an
981    e.p. capture.  The possibility of castling out of a check along the
982    back rank is not accounted for (i.e., we still return nonzero), as
983    this is illegal anyway.  Return value is the number of times the
984    king is in check. */
985 int
986 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
987 {
988     CheckTestClosure cl;
989     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
990     ChessSquare captured = EmptySquare;
991     /*  Suppress warnings on uninitialized variables    */
992
993     if(gameInfo.variant == VariantXiangqi)
994         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
995     if(gameInfo.variant == VariantKnightmate)
996         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
997
998     if (rt >= 0) {
999         if (enPassant) {
1000             captured = board[rf][ft];
1001             board[rf][ft] = EmptySquare;
1002         } else {
1003             captured = board[rt][ft];
1004         }
1005         if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1006             board[rt][ft] = board[rf][ff];
1007             board[rf][ff] = EmptySquare;
1008         }
1009     }
1010
1011     /* For compatibility with ICS wild 9, we scan the board in the
1012        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1013        and we test only whether that one is in check. */
1014     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1015         for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1016           if (board[cl.rking][cl.fking] == king) {
1017               cl.check = 0;
1018               if(gameInfo.variant == VariantXiangqi) {
1019                   /* [HGM] In Xiangqi opposing Kings means check as well */
1020                   int i, dir;
1021                   dir = (king >= BlackPawn) ? -1 : 1;
1022                   for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1023                                 board[i][cl.fking] == EmptySquare; i+=dir );
1024                   if(i>=0 && i<BOARD_HEIGHT &&
1025                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1026                           cl.check++;
1027               }
1028               GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1029               if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1030                  goto undo_move;  /* 2-level break */
1031           }
1032       }
1033
1034   undo_move:
1035
1036     if (rt >= 0) {
1037         if(rf != DROP_RANK) // [HGM] drop
1038             board[rf][ff] = board[rt][ft];
1039         if (enPassant) {
1040             board[rf][ft] = captured;
1041             board[rt][ft] = EmptySquare;
1042         } else {
1043             board[rt][ft] = captured;
1044         }
1045     }
1046
1047     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1048 }
1049
1050 ChessMove
1051 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1052 {   // [HGM] put drop legality testing in separate routine for clarity
1053     int n;
1054 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1055     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1056     n = PieceToNumber(piece);
1057     if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1058         && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1059         return ImpossibleMove; // piece not available
1060     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1061         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1062            (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1063             piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1064             piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1065         if(piece == WhitePawn || piece == BlackPawn) {
1066             int r;
1067             for(r=1; r<BOARD_HEIGHT-1; r++)
1068                 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
1069             // should still test if we mate with this Pawn
1070         }
1071     } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1072         if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1073     } else {
1074         if( (piece == WhitePawn || piece == BlackPawn) &&
1075             (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1076             return IllegalMove; /* no pawn drops on 1st/8th */
1077     }
1078 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1079     if (!(flags & F_IGNORE_CHECK) &&
1080         CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1081     return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1082 }
1083
1084 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1085                                     int rf, int ff, int rt, int ft,
1086                                     VOIDSTAR closure));
1087
1088 void
1089 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1090 {
1091     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1092
1093     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1094         cl->captures++; // [HGM] losers: count legal captures
1095     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1096       cl->kind = kind;
1097 }
1098
1099 ChessMove
1100 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1101 {
1102     LegalityTestClosure cl; ChessSquare piece, filterPiece;
1103
1104     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1105     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1106     piece = filterPiece = board[rf][ff];
1107     if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1108
1109     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1110     /* (perhaps we should disallow moves that obviously leave us in check?)              */
1111     if(piece == WhiteFalcon || piece == BlackFalcon ||
1112        piece == WhiteCobra  || piece == BlackCobra)
1113         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1114
1115     cl.rf = rf;
1116     cl.ff = ff;
1117     cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1118     cl.ft = fFilter = ft;
1119     cl.kind = IllegalMove;
1120     cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1121     if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1122     GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1123     if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1124                 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1125         return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1126
1127     if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1128     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1129         if(board[rf][ff] < BlackPawn) { // white
1130             if(rf != 0) return IllegalMove; // must be on back rank
1131             if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1132             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1133         } else {
1134             if(rf != BOARD_HEIGHT-1) return IllegalMove;
1135             if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1136             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1137         }
1138     } else
1139     if(IS_SHOGI(gameInfo.variant)) {
1140         /* [HGM] Shogi promotions. '=' means defer */
1141         if(rf != DROP_RANK && cl.kind == NormalMove) {
1142             ChessSquare piece = board[rf][ff];
1143
1144             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1145             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1146                promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1147                promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1148                   promoChar = '+'; // allowed ICS notations
1149 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1150             if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1151                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1152             else if(flags & F_WHITE_ON_MOVE) {
1153                 if( (int) piece < (int) WhiteWazir &&
1154                      (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1155                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1156                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1157                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1158                     else /* promotion optional, default is defer */
1159                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1160                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1161             } else {
1162                 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1163                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1164                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1165                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1166                     else /* promotion optional, default is defer */
1167                        cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1168                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1169             }
1170         }
1171     } else
1172     if (promoChar != NULLCHAR) {
1173         if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1174         if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1175             ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1176             if(piece == EmptySquare)
1177                 cl.kind = ImpossibleMove; // non-existing piece
1178             if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1179                 if(promoChar != PieceToChar(BlackKing)) {
1180                     if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1181                     if(piece == BlackLance) cl.kind = ImpossibleMove;
1182                 } else { // promotion to King allowed only if we do not haave two yet
1183                     int r, f, kings = 0;
1184                     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1185                     if(kings == 2) cl.kind = IllegalMove;
1186                 }
1187             } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1188                           piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1189             else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1190              cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1191             else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1192              cl.kind = IllegalMove; // promotion to King usually not allowed
1193         } else {
1194             cl.kind = IllegalMove;
1195         }
1196     }
1197     return cl.kind;
1198 }
1199
1200 typedef struct {
1201     int count;
1202 } MateTestClosure;
1203
1204 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1205                                 int rf, int ff, int rt, int ft,
1206                                 VOIDSTAR closure));
1207
1208 void
1209 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1210 {
1211     register MateTestClosure *cl = (MateTestClosure *) closure;
1212
1213     cl->count++;
1214 }
1215
1216 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1217 int
1218 MateTest (Board board, int flags)
1219 {
1220     MateTestClosure cl;
1221     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1222     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1223
1224     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1225         // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1226         nrKing += (board[r][f] == king);   // stm has king
1227         if( board[r][f] != EmptySquare ) {
1228             if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1229                  myPieces++;
1230             else hisPieces++;
1231         }
1232     }
1233     switch(gameInfo.variant) { // [HGM] losers: extinction wins
1234         case VariantShatranj:
1235                 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1236         default:
1237                 break;
1238         case VariantAtomic:
1239                 if(nrKing == 0) return MT_NOKING;
1240                 break;
1241         case VariantLosers:
1242                 if(myPieces == 1) return MT_BARE;
1243     }
1244     cl.count = 0;
1245     inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1246     // [HGM] 3check: yet to do!
1247     if (cl.count > 0) {
1248         return inCheck ? MT_CHECK : MT_NONE;
1249     } else {
1250         if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1251                                  && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1252             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1253             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1254                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1255                     if(board[n][holdings] != EmptySquare) {
1256                         int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1257                         if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1258                     }
1259         }
1260         if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1261                 return myPieces == hisPieces ? MT_STALEMATE :
1262                                         myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1263         else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1264         else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1265
1266         return inCheck ? MT_CHECKMATE
1267                        : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || gameInfo.variant == VariantShogi) ?
1268                           MT_STAINMATE : MT_STALEMATE;
1269     }
1270 }
1271
1272
1273 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1274                                     int rf, int ff, int rt, int ft,
1275                                     VOIDSTAR closure));
1276
1277 void
1278 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1279 {
1280     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1281     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1282
1283     // [HGM] wild: for wild-card pieces rt and rf are dummies
1284     if(piece == WhiteFalcon || piece == BlackFalcon ||
1285        piece == WhiteCobra  || piece == BlackCobra)
1286         wildCard = TRUE;
1287
1288     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1289          || PieceToChar(board[rf][ff]) == '~'
1290               && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1291                                                                       ) &&
1292         (cl->rfIn == -1 || cl->rfIn == rf) &&
1293         (cl->ffIn == -1 || cl->ffIn == ff) &&
1294         (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1295         (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1296
1297         cl->count++;
1298         if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1299           // [HGM] oneclick: if multiple moves, be sure we remember capture
1300           cl->piece = board[rf][ff];
1301           cl->rf = rf;
1302           cl->ff = ff;
1303           cl->rt = wildCard ? cl->rtIn : rt;
1304           cl->ft = wildCard ? cl->ftIn : ft;
1305           cl->kind = kind;
1306         }
1307         cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1308     }
1309 }
1310
1311 void
1312 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
1313 {
1314     int illegal = 0; char c = closure->promoCharIn;
1315
1316     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1317     closure->count = closure->captures = 0;
1318     closure->rf = closure->ff = closure->rt = closure->ft = 0;
1319     closure->kind = ImpossibleMove;
1320     rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
1321     fFilter = closure->ftIn;
1322     if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
1323         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1324         if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
1325             closure->count = closure->captures = 0;
1326             closure->rf = closure->ff = closure->rt = closure->ft = 0;
1327             closure->kind = ImpossibleMove;
1328             GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1329         }
1330     } else
1331     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1332     if (closure->count == 0) {
1333         /* See if it's an illegal move due to check */
1334         illegal = 1;
1335         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1336         if (closure->count == 0) {
1337             /* No, it's not even that */
1338           if(!appData.testLegality && closure->pieceIn != EmptySquare) {
1339             int f, r; // if there is only a single piece of the requested type on the board, use that
1340             closure->rt = closure->rtIn, closure->ft = closure->ftIn;
1341             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1342                 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
1343             if(closure->count > 1) illegal = 0; // ambiguous
1344           }
1345           if(closure->count == 0) {
1346             if (appData.debugMode) { int i, j;
1347                 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1348                     for(j=0; j<BOARD_WIDTH; j++)
1349                         fprintf(debugFP, "%3d", (int) board[i][j]);
1350                     fprintf(debugFP, "\n");
1351                 }
1352             }
1353             return;
1354           }
1355         }
1356     }
1357
1358     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1359     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1360         if(closure->piece < BlackPawn) { // white
1361             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1362             if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
1363             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1364         } else {
1365             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1366             if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
1367             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1368         }
1369     } else
1370     if(gameInfo.variant == VariantChu) {
1371         if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
1372     } else
1373     if(gameInfo.variant == VariantShogi) {
1374         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1375         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1376             ChessSquare piece = closure->piece;
1377             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1378                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1379                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1380                    c = '+'; // allowed ICS notations
1381             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1382             else if(flags & F_WHITE_ON_MOVE) {
1383                 if( (int) piece < (int) WhiteWazir &&
1384                      (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1385                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1386                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1387                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1388                     else /* promotion optional, default is defer */
1389                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1390                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1391             } else {
1392                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1393                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1394                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1395                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1396                     else /* promotion optional, default is defer */
1397                        closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1398                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1399             }
1400         }
1401         if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1402         if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1403     } else
1404     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1405         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1406             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1407                gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
1408                 c = PieceToChar(BlackFerz);
1409             else if(gameInfo.variant == VariantGreat)
1410                 c = PieceToChar(BlackMan);
1411             else if(gameInfo.variant == VariantGrand)
1412                     closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
1413             else
1414                 c = PieceToChar(BlackQueen);
1415         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1416     } else if (c != NULLCHAR) closure->kind = IllegalMove;
1417
1418     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1419     if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1420         closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1421     if (closure->count > 1) {
1422         closure->kind = AmbiguousMove;
1423     }
1424     if (illegal) {
1425         /* Note: If more than one illegal move matches, but no legal
1426            moves, we return IllegalMove, not AmbiguousMove.  Caller
1427            can look at closure->count to detect this.
1428         */
1429         closure->kind = IllegalMove;
1430     }
1431 }
1432
1433
1434 typedef struct {
1435     /* Input */
1436     ChessSquare piece;
1437     int rf, ff, rt, ft;
1438     /* Output */
1439     ChessMove kind;
1440     int rank;
1441     int file;
1442     int either;
1443 } CoordsToAlgebraicClosure;
1444
1445 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1446                                          ChessMove kind, int rf, int ff,
1447                                          int rt, int ft, VOIDSTAR closure));
1448
1449 void
1450 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1451 {
1452     register CoordsToAlgebraicClosure *cl =
1453       (CoordsToAlgebraicClosure *) closure;
1454
1455     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1456         (board[rf][ff] == cl->piece
1457          || PieceToChar(board[rf][ff]) == '~' &&
1458             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1459                                      ) {
1460         if (rf == cl->rf) {
1461             if (ff == cl->ff) {
1462                 cl->kind = kind; /* this is the move we want */
1463             } else {
1464                 cl->file++; /* need file to rule out this move */
1465             }
1466         } else {
1467             if (ff == cl->ff) {
1468                 cl->rank++; /* need rank to rule out this move */
1469             } else {
1470                 cl->either++; /* rank or file will rule out this move */
1471             }
1472         }
1473     }
1474 }
1475
1476 /* Convert coordinates to normal algebraic notation.
1477    promoChar must be NULLCHAR or 'x' if not a promotion.
1478 */
1479 ChessMove
1480 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
1481 {
1482     ChessSquare piece;
1483     ChessMove kind;
1484     char *outp = out, c;
1485     CoordsToAlgebraicClosure cl;
1486
1487     if (rf == DROP_RANK) {
1488         if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
1489         /* Bughouse piece drop */
1490         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1491         *outp++ = '@';
1492         *outp++ = ft + AAA;
1493         if(rt+ONE <= '9')
1494            *outp++ = rt + ONE;
1495         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1496         *outp = NULLCHAR;
1497         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1498     }
1499
1500     if (promoChar == 'x') promoChar = NULLCHAR;
1501     piece = board[rf][ff];
1502     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1503
1504     switch (piece) {
1505       case WhitePawn:
1506       case BlackPawn:
1507         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1508         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1509             /* Keep short notation if move is illegal only because it
1510                leaves the player in check, but still return IllegalMove */
1511             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1512             if (kind == IllegalMove) break;
1513             kind = IllegalMove;
1514         }
1515         /* Pawn move */
1516         *outp++ = ff + AAA;
1517         if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1518             /* Non-capture; use style "e5" */
1519             if(rt+ONE <= '9')
1520                *outp++ = rt + ONE;
1521             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1522         } else {
1523             /* Capture; use style "exd5" */
1524             if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1525             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
1526             *outp++ = ft + AAA;
1527             if(rt+ONE <= '9')
1528                *outp++ = rt + ONE;
1529             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1530         }
1531         /* Use promotion suffix style "=Q" */
1532         *outp = NULLCHAR;
1533         if (promoChar != NULLCHAR) {
1534             if(IS_SHOGI(gameInfo.variant)) {
1535                 /* [HGM] ... but not in Shogi! */
1536                 *outp++ = promoChar == '=' ? '=' : '+';
1537             } else {
1538                 *outp++ = '=';
1539                 *outp++ = ToUpper(promoChar);
1540             }
1541             *outp = NULLCHAR;
1542         }
1543         return kind;
1544
1545
1546       case WhiteKing:
1547       case BlackKing:
1548         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1549         /* Code added by Tord:  FRC castling. */
1550         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1551            (piece == BlackKing && board[rt][ft] == BlackRook)) {
1552           if(ft > ff)
1553             safeStrCpy(out, "O-O", MOVE_LEN);
1554           else
1555             safeStrCpy(out, "O-O-O", MOVE_LEN);
1556           return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1557         }
1558         /* End of code added by Tord */
1559         /* Test for castling or ICS wild castling */
1560         /* Use style "O-O" (oh-oh) for PGN compatibility */
1561         else if (rf == rt &&
1562             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1563             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
1564             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1565              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1566             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1567               snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1568             else
1569               snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1570
1571             /* This notation is always unambiguous, unless there are
1572                kings on both the d and e files, with "wild castling"
1573                possible for the king on the d file and normal castling
1574                possible for the other.  ICS rules for wild 9
1575                effectively make castling illegal for either king in
1576                this situation.  So I am not going to worry about it;
1577                I'll just generate an ambiguous O-O in this case.
1578             */
1579             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1580         }
1581
1582         /* else fall through */
1583       default:
1584         /* Piece move */
1585         cl.rf = rf;
1586         cl.ff = ff;
1587         cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1588         cl.ft = fFilter = ft;
1589         cl.piece = piece;
1590         cl.kind = IllegalMove;
1591         cl.rank = cl.file = cl.either = 0;
1592         c = PieceToChar(piece) ;
1593         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
1594
1595         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1596             /* Generate pretty moves for moving into check, but
1597                still return IllegalMove.
1598             */
1599             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
1600             if (cl.kind == IllegalMove) break;
1601             cl.kind = IllegalMove;
1602         }
1603
1604         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1605            else "Ngf3" or "Ngxf7",
1606            else "N1f3" or "N5xf7",
1607            else "Ng1f3" or "Ng5xf7".
1608         */
1609         if( c == '~' || c == '+') {
1610            /* [HGM] print nonexistent piece as its demoted version */
1611            piece = (ChessSquare) (DEMOTED piece);
1612         }
1613         if(c=='+') *outp++ = c;
1614         *outp++ = ToUpper(PieceToChar(piece));
1615
1616         if (cl.file || (cl.either && !cl.rank)) {
1617             *outp++ = ff + AAA;
1618         }
1619         if (cl.rank) {
1620             if(rf+ONE <= '9')
1621                 *outp++ = rf + ONE;
1622             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1623         }
1624
1625         if(board[rt][ft] != EmptySquare)
1626           *outp++ = 'x';
1627
1628         *outp++ = ft + AAA;
1629         if(rt+ONE <= '9')
1630            *outp++ = rt + ONE;
1631         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1632         if (IS_SHOGI(gameInfo.variant)) {
1633             /* [HGM] in Shogi non-pawns can promote */
1634             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
1635         }
1636         else if (gameInfo.variant != VariantSuper && promoChar &&
1637                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
1638             *outp++ = '=';
1639             *outp++ = ToUpper(promoChar);
1640         }
1641         else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
1642             *outp++ = '/';
1643             *outp++ = ToUpper(promoChar);
1644         }
1645         *outp = NULLCHAR;
1646         return cl.kind;
1647
1648       case EmptySquare:
1649         /* Moving a nonexistent piece */
1650         break;
1651     }
1652
1653     /* Not a legal move, even ignoring check.
1654        If there was a piece on the from square,
1655        use style "Ng1g3" or "Ng1xe8";
1656        if there was a pawn or nothing (!),
1657        use style "g1g3" or "g1xe8".  Use "x"
1658        if a piece was on the to square, even
1659        a piece of the same color.
1660     */
1661     outp = out;
1662     c = 0;
1663     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1664         int r, f;
1665       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
1666                 c += (board[r][f] == piece); // count on-board pieces of given type
1667         *outp++ = ToUpper(PieceToChar(piece));
1668     }
1669   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
1670     *outp++ = ff + AAA;
1671     if(rf+ONE <= '9')
1672        *outp++ = rf + ONE;
1673     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1674   }
1675     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1676     *outp++ = ft + AAA;
1677     if(rt+ONE <= '9')
1678        *outp++ = rt + ONE;
1679     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1680     /* Use promotion suffix style "=Q" */
1681     if (promoChar != NULLCHAR && promoChar != 'x') {
1682         *outp++ = '=';
1683         *outp++ = ToUpper(promoChar);
1684     }
1685     *outp = NULLCHAR;
1686
1687     return IllegalMove;
1688 }
1689
1690 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1691
1692 typedef struct {
1693     /* Input */
1694     int rf, ff, rt, ft;
1695     /* Output */
1696     int recaptures;
1697 } ChaseClosure;
1698
1699 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1700
1701 int preyStackPointer, chaseStackPointer;
1702
1703 struct {
1704 unsigned char rf, ff, rt, ft;
1705 } chaseStack[100];
1706
1707 struct {
1708 unsigned char rank, file;
1709 } preyStack[100];
1710
1711
1712
1713
1714 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1715
1716 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1717                                 int rf, int ff, int rt, int ft,
1718                                 VOIDSTAR closure));
1719
1720 void
1721 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1722 {   // For adding captures that can lead to chase indictment to the chaseStack
1723     if(board[rt][ft] == EmptySquare) return;                               // non-capture
1724     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1725     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1726     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
1727     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1728     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1729     chaseStack[chaseStackPointer].rf = rf;
1730     chaseStack[chaseStackPointer].ff = ff;
1731     chaseStack[chaseStackPointer].rt = rt;
1732     chaseStack[chaseStackPointer].ft = ft;
1733     chaseStackPointer++;
1734 }
1735
1736 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1737                                 int rf, int ff, int rt, int ft,
1738                                 VOIDSTAR closure));
1739
1740 void
1741 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1742 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1743     int i;
1744     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1745
1746     if(board[rt][ft] == EmptySquare) return; // no capture
1747     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1748         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
1749     }
1750     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1751     for(i=0; i<chaseStackPointer; i++) {
1752         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1753            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
1754             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1755             chaseStack[i] = chaseStack[--chaseStackPointer];
1756             break;
1757         }
1758     }
1759 }
1760
1761 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1762                                 int rf, int ff, int rt, int ft,
1763                                 VOIDSTAR closure));
1764
1765 void
1766 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1767 {   // for determining if a piece (given through the closure) is protected
1768     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1769
1770     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
1771     if(appData.debugMode && board[rt][ft] != EmptySquare)
1772         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1773 }
1774
1775 extern char moveList[MAX_MOVES][MOVE_LEN];
1776
1777 int
1778 PerpetualChase (int first, int last)
1779 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1780     int i, j, k, tail;
1781     ChaseClosure cl;
1782     ChessSquare captured;
1783
1784     preyStackPointer = 0;        // clear stack of chased pieces
1785     for(i=first; i<last; i+=2) { // for all positions with same side to move
1786         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1787         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
1788         // determine all captures possible after the move, and put them on chaseStack
1789         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
1790         if(appData.debugMode) { int n;
1791             for(n=0; n<chaseStackPointer; n++)
1792                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1793                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1794             fprintf(debugFP, ": all capts\n");
1795         }
1796         // determine all captures possible before the move, and delete them from chaseStack
1797         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1798         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1799         cl.rt = moveList[i][3]-ONE;
1800         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1801         CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
1802         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
1803         xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
1804         if(appData.debugMode) { int n;
1805             for(n=0; n<chaseStackPointer; n++)
1806                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1807                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1808             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1809         }
1810         // chaseSack now contains all captures made possible by the move
1811         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1812             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1813             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1814
1815             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1816             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
1817
1818             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1819                 continue; // C or H attack on R is always chase; leave on chaseStack
1820
1821             if(attacker == victim) {
1822                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
1823                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1824                         // we can capture back with equal piece, so this is no chase but a sacrifice
1825                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1826                         j--; /* ! */ continue;
1827                 }
1828
1829             }
1830
1831             // the attack is on a lower piece, or on a pinned or blocked equal one
1832             CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
1833             CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
1834             // test if the victim is protected by a true protector. First make the capture.
1835             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1836             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1837             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1838             // Then test if the opponent can recapture
1839             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
1840             cl.rt = chaseStack[j].rt;
1841             cl.ft = chaseStack[j].ft;
1842             if(appData.debugMode) {
1843                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1844             }
1845             xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
1846             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
1847             xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
1848             // unmake the capture
1849             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1850             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1851             // if a recapture was found, piece is protected, and we are not chasing it.
1852             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1853                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1854                 j--; /* ! */
1855             }
1856         }
1857         // chaseStack now contains all moves that chased
1858         if(appData.debugMode) { int n;
1859             for(n=0; n<chaseStackPointer; n++)
1860                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1861                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1862             fprintf(debugFP, ": chases\n");
1863         }
1864         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1865             for(j=0; j<chaseStackPointer; j++) {
1866                 preyStack[j].rank = chaseStack[j].rt;
1867                 preyStack[j].file = chaseStack[j].ft;
1868             }
1869             preyStackPointer = chaseStackPointer;
1870         }
1871         tail = 0;
1872         for(j=0; j<chaseStackPointer; j++) {
1873             for(k=0; k<preyStackPointer; k++) {
1874                 // search the victim of each chase move on the preyStack (first occurrence)
1875                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1876                     if(k < tail) break; // piece was already identified as still being chased
1877                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1878                     preyStack[tail] = preyStack[k];                // by swapping
1879                     preyStack[k] = preyStack[preyStackPointer];
1880                     tail++;
1881                     break;
1882                 }
1883             }
1884         }
1885         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1886         if(appData.debugMode) { int n;
1887             for(n=0; n<preyStackPointer; n++)
1888                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1889             fprintf(debugFP, "always chased upto ply %d\n", i);
1890         }
1891         // now adjust the location of the chased pieces according to opponent move
1892         for(j=0; j<preyStackPointer; j++) {
1893             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1894                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1895                 preyStack[j].rank = moveList[i+1][3]-ONE;
1896                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1897                 break;
1898             }
1899         }
1900     }
1901     return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
1902                                 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the
1903 }