Implement (inaccessible) dark squares
[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(gameInfo.variant == VariantShogi) {
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 == VariantShogi) {
1371         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1372         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1373             ChessSquare piece = closure->piece;
1374             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1375                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1376                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1377                    c = '+'; // allowed ICS notations
1378             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1379             else if(flags & F_WHITE_ON_MOVE) {
1380                 if( (int) piece < (int) WhiteWazir &&
1381                      (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1382                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1383                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1384                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1385                     else /* promotion optional, default is defer */
1386                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1387                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1388             } else {
1389                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1390                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1391                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1392                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1393                     else /* promotion optional, default is defer */
1394                        closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1395                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1396             }
1397         }
1398         if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1399         if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1400     } else
1401     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1402         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1403             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1404                gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
1405                 c = PieceToChar(BlackFerz);
1406             else if(gameInfo.variant == VariantGreat)
1407                 c = PieceToChar(BlackMan);
1408             else if(gameInfo.variant == VariantGrand)
1409                     closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
1410             else
1411                 c = PieceToChar(BlackQueen);
1412         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1413     } else if (c != NULLCHAR) closure->kind = IllegalMove;
1414
1415     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1416     if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1417         closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1418     if (closure->count > 1) {
1419         closure->kind = AmbiguousMove;
1420     }
1421     if (illegal) {
1422         /* Note: If more than one illegal move matches, but no legal
1423            moves, we return IllegalMove, not AmbiguousMove.  Caller
1424            can look at closure->count to detect this.
1425         */
1426         closure->kind = IllegalMove;
1427     }
1428 }
1429
1430
1431 typedef struct {
1432     /* Input */
1433     ChessSquare piece;
1434     int rf, ff, rt, ft;
1435     /* Output */
1436     ChessMove kind;
1437     int rank;
1438     int file;
1439     int either;
1440 } CoordsToAlgebraicClosure;
1441
1442 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1443                                          ChessMove kind, int rf, int ff,
1444                                          int rt, int ft, VOIDSTAR closure));
1445
1446 void
1447 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1448 {
1449     register CoordsToAlgebraicClosure *cl =
1450       (CoordsToAlgebraicClosure *) closure;
1451
1452     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1453         (board[rf][ff] == cl->piece
1454          || PieceToChar(board[rf][ff]) == '~' &&
1455             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1456                                      ) {
1457         if (rf == cl->rf) {
1458             if (ff == cl->ff) {
1459                 cl->kind = kind; /* this is the move we want */
1460             } else {
1461                 cl->file++; /* need file to rule out this move */
1462             }
1463         } else {
1464             if (ff == cl->ff) {
1465                 cl->rank++; /* need rank to rule out this move */
1466             } else {
1467                 cl->either++; /* rank or file will rule out this move */
1468             }
1469         }
1470     }
1471 }
1472
1473 /* Convert coordinates to normal algebraic notation.
1474    promoChar must be NULLCHAR or 'x' if not a promotion.
1475 */
1476 ChessMove
1477 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
1478 {
1479     ChessSquare piece;
1480     ChessMove kind;
1481     char *outp = out, c;
1482     CoordsToAlgebraicClosure cl;
1483
1484     if (rf == DROP_RANK) {
1485         if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
1486         /* Bughouse piece drop */
1487         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1488         *outp++ = '@';
1489         *outp++ = ft + AAA;
1490         if(rt+ONE <= '9')
1491            *outp++ = rt + ONE;
1492         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1493         *outp = NULLCHAR;
1494         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1495     }
1496
1497     if (promoChar == 'x') promoChar = NULLCHAR;
1498     piece = board[rf][ff];
1499     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1500
1501     switch (piece) {
1502       case WhitePawn:
1503       case BlackPawn:
1504         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1505         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1506             /* Keep short notation if move is illegal only because it
1507                leaves the player in check, but still return IllegalMove */
1508             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1509             if (kind == IllegalMove) break;
1510             kind = IllegalMove;
1511         }
1512         /* Pawn move */
1513         *outp++ = ff + AAA;
1514         if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1515             /* Non-capture; use style "e5" */
1516             if(rt+ONE <= '9')
1517                *outp++ = rt + ONE;
1518             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1519         } else {
1520             /* Capture; use style "exd5" */
1521             if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1522             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
1523             *outp++ = ft + AAA;
1524             if(rt+ONE <= '9')
1525                *outp++ = rt + ONE;
1526             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1527         }
1528         /* Use promotion suffix style "=Q" */
1529         *outp = NULLCHAR;
1530         if (promoChar != NULLCHAR) {
1531             if(gameInfo.variant == VariantShogi) {
1532                 /* [HGM] ... but not in Shogi! */
1533                 *outp++ = promoChar == '=' ? '=' : '+';
1534             } else {
1535                 *outp++ = '=';
1536                 *outp++ = ToUpper(promoChar);
1537             }
1538             *outp = NULLCHAR;
1539         }
1540         return kind;
1541
1542
1543       case WhiteKing:
1544       case BlackKing:
1545         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1546         /* Code added by Tord:  FRC castling. */
1547         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1548            (piece == BlackKing && board[rt][ft] == BlackRook)) {
1549           if(ft > ff)
1550             safeStrCpy(out, "O-O", MOVE_LEN);
1551           else
1552             safeStrCpy(out, "O-O-O", MOVE_LEN);
1553           return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1554         }
1555         /* End of code added by Tord */
1556         /* Test for castling or ICS wild castling */
1557         /* Use style "O-O" (oh-oh) for PGN compatibility */
1558         else if (rf == rt &&
1559             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1560             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
1561             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1562              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1563             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1564               snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1565             else
1566               snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1567
1568             /* This notation is always unambiguous, unless there are
1569                kings on both the d and e files, with "wild castling"
1570                possible for the king on the d file and normal castling
1571                possible for the other.  ICS rules for wild 9
1572                effectively make castling illegal for either king in
1573                this situation.  So I am not going to worry about it;
1574                I'll just generate an ambiguous O-O in this case.
1575             */
1576             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1577         }
1578
1579         /* else fall through */
1580       default:
1581         /* Piece move */
1582         cl.rf = rf;
1583         cl.ff = ff;
1584         cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1585         cl.ft = fFilter = ft;
1586         cl.piece = piece;
1587         cl.kind = IllegalMove;
1588         cl.rank = cl.file = cl.either = 0;
1589         c = PieceToChar(piece) ;
1590         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
1591
1592         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1593             /* Generate pretty moves for moving into check, but
1594                still return IllegalMove.
1595             */
1596             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
1597             if (cl.kind == IllegalMove) break;
1598             cl.kind = IllegalMove;
1599         }
1600
1601         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1602            else "Ngf3" or "Ngxf7",
1603            else "N1f3" or "N5xf7",
1604            else "Ng1f3" or "Ng5xf7".
1605         */
1606         if( c == '~' || c == '+') {
1607            /* [HGM] print nonexistent piece as its demoted version */
1608            piece = (ChessSquare) (DEMOTED piece);
1609         }
1610         if(c=='+') *outp++ = c;
1611         *outp++ = ToUpper(PieceToChar(piece));
1612
1613         if (cl.file || (cl.either && !cl.rank)) {
1614             *outp++ = ff + AAA;
1615         }
1616         if (cl.rank) {
1617             if(rf+ONE <= '9')
1618                 *outp++ = rf + ONE;
1619             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1620         }
1621
1622         if(board[rt][ft] != EmptySquare)
1623           *outp++ = 'x';
1624
1625         *outp++ = ft + AAA;
1626         if(rt+ONE <= '9')
1627            *outp++ = rt + ONE;
1628         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1629         if (gameInfo.variant == VariantShogi) {
1630             /* [HGM] in Shogi non-pawns can promote */
1631             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
1632         }
1633         else if (gameInfo.variant != VariantSuper && promoChar &&
1634                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
1635             *outp++ = '=';
1636             *outp++ = ToUpper(promoChar);
1637         }
1638         else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
1639             *outp++ = '/';
1640             *outp++ = ToUpper(promoChar);
1641         }
1642         *outp = NULLCHAR;
1643         return cl.kind;
1644
1645       case EmptySquare:
1646         /* Moving a nonexistent piece */
1647         break;
1648     }
1649
1650     /* Not a legal move, even ignoring check.
1651        If there was a piece on the from square,
1652        use style "Ng1g3" or "Ng1xe8";
1653        if there was a pawn or nothing (!),
1654        use style "g1g3" or "g1xe8".  Use "x"
1655        if a piece was on the to square, even
1656        a piece of the same color.
1657     */
1658     outp = out;
1659     c = 0;
1660     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1661         int r, f;
1662       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
1663                 c += (board[r][f] == piece); // count on-board pieces of given type
1664         *outp++ = ToUpper(PieceToChar(piece));
1665     }
1666   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
1667     *outp++ = ff + AAA;
1668     if(rf+ONE <= '9')
1669        *outp++ = rf + ONE;
1670     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1671   }
1672     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1673     *outp++ = ft + AAA;
1674     if(rt+ONE <= '9')
1675        *outp++ = rt + ONE;
1676     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1677     /* Use promotion suffix style "=Q" */
1678     if (promoChar != NULLCHAR && promoChar != 'x') {
1679         *outp++ = '=';
1680         *outp++ = ToUpper(promoChar);
1681     }
1682     *outp = NULLCHAR;
1683
1684     return IllegalMove;
1685 }
1686
1687 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1688
1689 typedef struct {
1690     /* Input */
1691     int rf, ff, rt, ft;
1692     /* Output */
1693     int recaptures;
1694 } ChaseClosure;
1695
1696 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1697
1698 int preyStackPointer, chaseStackPointer;
1699
1700 struct {
1701 unsigned char rf, ff, rt, ft;
1702 } chaseStack[100];
1703
1704 struct {
1705 unsigned char rank, file;
1706 } preyStack[100];
1707
1708
1709
1710
1711 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1712
1713 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1714                                 int rf, int ff, int rt, int ft,
1715                                 VOIDSTAR closure));
1716
1717 void
1718 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1719 {   // For adding captures that can lead to chase indictment to the chaseStack
1720     if(board[rt][ft] == EmptySquare) return;                               // non-capture
1721     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1722     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1723     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
1724     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1725     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1726     chaseStack[chaseStackPointer].rf = rf;
1727     chaseStack[chaseStackPointer].ff = ff;
1728     chaseStack[chaseStackPointer].rt = rt;
1729     chaseStack[chaseStackPointer].ft = ft;
1730     chaseStackPointer++;
1731 }
1732
1733 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1734                                 int rf, int ff, int rt, int ft,
1735                                 VOIDSTAR closure));
1736
1737 void
1738 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1739 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1740     int i;
1741     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1742
1743     if(board[rt][ft] == EmptySquare) return; // no capture
1744     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1745         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
1746     }
1747     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1748     for(i=0; i<chaseStackPointer; i++) {
1749         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1750            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
1751             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1752             chaseStack[i] = chaseStack[--chaseStackPointer];
1753             break;
1754         }
1755     }
1756 }
1757
1758 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1759                                 int rf, int ff, int rt, int ft,
1760                                 VOIDSTAR closure));
1761
1762 void
1763 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1764 {   // for determining if a piece (given through the closure) is protected
1765     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1766
1767     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
1768     if(appData.debugMode && board[rt][ft] != EmptySquare)
1769         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1770 }
1771
1772 extern char moveList[MAX_MOVES][MOVE_LEN];
1773
1774 int
1775 PerpetualChase (int first, int last)
1776 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1777     int i, j, k, tail;
1778     ChaseClosure cl;
1779     ChessSquare captured;
1780
1781     preyStackPointer = 0;        // clear stack of chased pieces
1782     for(i=first; i<last; i+=2) { // for all positions with same side to move
1783         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1784         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
1785         // determine all captures possible after the move, and put them on chaseStack
1786         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
1787         if(appData.debugMode) { int n;
1788             for(n=0; n<chaseStackPointer; n++)
1789                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1790                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1791             fprintf(debugFP, ": all capts\n");
1792         }
1793         // determine all captures possible before the move, and delete them from chaseStack
1794         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1795         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1796         cl.rt = moveList[i][3]-ONE;
1797         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1798         CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
1799         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
1800         xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
1801         if(appData.debugMode) { int n;
1802             for(n=0; n<chaseStackPointer; n++)
1803                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1804                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1805             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1806         }
1807         // chaseSack now contains all captures made possible by the move
1808         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1809             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1810             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1811
1812             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1813             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
1814
1815             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1816                 continue; // C or H attack on R is always chase; leave on chaseStack
1817
1818             if(attacker == victim) {
1819                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
1820                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1821                         // we can capture back with equal piece, so this is no chase but a sacrifice
1822                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1823                         j--; /* ! */ continue;
1824                 }
1825
1826             }
1827
1828             // the attack is on a lower piece, or on a pinned or blocked equal one
1829             CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
1830             CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
1831             // test if the victim is protected by a true protector. First make the capture.
1832             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1833             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1834             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1835             // Then test if the opponent can recapture
1836             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
1837             cl.rt = chaseStack[j].rt;
1838             cl.ft = chaseStack[j].ft;
1839             if(appData.debugMode) {
1840                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1841             }
1842             xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
1843             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
1844             xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
1845             // unmake the capture
1846             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1847             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1848             // if a recapture was found, piece is protected, and we are not chasing it.
1849             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1850                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1851                 j--; /* ! */
1852             }
1853         }
1854         // chaseStack now contains all moves that chased
1855         if(appData.debugMode) { int n;
1856             for(n=0; n<chaseStackPointer; n++)
1857                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1858                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1859             fprintf(debugFP, ": chases\n");
1860         }
1861         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1862             for(j=0; j<chaseStackPointer; j++) {
1863                 preyStack[j].rank = chaseStack[j].rt;
1864                 preyStack[j].file = chaseStack[j].ft;
1865             }
1866             preyStackPointer = chaseStackPointer;
1867         }
1868         tail = 0;
1869         for(j=0; j<chaseStackPointer; j++) {
1870             for(k=0; k<preyStackPointer; k++) {
1871                 // search the victim of each chase move on the preyStack (first occurrence)
1872                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1873                     if(k < tail) break; // piece was already identified as still being chased
1874                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1875                     preyStack[tail] = preyStack[k];                // by swapping
1876                     preyStack[k] = preyStack[preyStackPointer];
1877                     tail++;
1878                     break;
1879                 }
1880             }
1881         }
1882         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1883         if(appData.debugMode) { int n;
1884             for(n=0; n<preyStackPointer; n++)
1885                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1886             fprintf(debugFP, "always chased upto ply %d\n", i);
1887         }
1888         // now adjust the location of the chased pieces according to opponent move
1889         for(j=0; j<preyStackPointer; j++) {
1890             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1891                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1892                 preyStack[j].rank = moveList[i+1][3]-ONE;
1893                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1894                 break;
1895             }
1896         }
1897     }
1898     return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
1899                                 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the
1900 }