Put some OSX code into gtk version
[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))
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) 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) 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(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
915             if(ff > BOARD_LEFT+2)
916             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
917                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
918             if(ft != NoRights && board[0][ft] == WhiteRook)
919                 callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
920         }
921     } else {
922         ff = castlingRights[5]; /* King file if we have any rights */
923         if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
924             ft = castlingRights[3]; /* Rook file if we have H-side rights */
925             left  = ff+1;
926             right = BOARD_RGHT-2;
927             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
928             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
929                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
930             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
931                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
932             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
933                 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
934
935             ft = castlingRights[4]; /* Rook file if we have A-side rights */
936             left  = BOARD_LEFT+2;
937             right = ff-1;
938             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
939             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
940                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
941             if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
942             if(ff > BOARD_LEFT+2)
943             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
944                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
945             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
946                 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
947         }
948     }
949
950   }
951
952     return FALSE;
953 }
954
955
956 typedef struct {
957     int rking, fking;
958     int check;
959 } CheckTestClosure;
960
961
962 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
963                                  int rf, int ff, int rt, int ft,
964                                  VOIDSTAR closure));
965
966
967 void
968 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
969 {
970     register CheckTestClosure *cl = (CheckTestClosure *) closure;
971
972     if (rt == cl->rking && ft == cl->fking) {
973         if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
974         cl->check++;
975         xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
976     }
977 }
978
979
980 /* If the player on move were to move from (rf, ff) to (rt, ft), would
981    he leave himself in check?  Or if rf == -1, is the player on move
982    in check now?  enPassant must be TRUE if the indicated move is an
983    e.p. capture.  The possibility of castling out of a check along the
984    back rank is not accounted for (i.e., we still return nonzero), as
985    this is illegal anyway.  Return value is the number of times the
986    king is in check. */
987 int
988 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
989 {
990     CheckTestClosure cl;
991     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
992     ChessSquare captured = EmptySquare;
993     /*  Suppress warnings on uninitialized variables    */
994
995     if(gameInfo.variant == VariantXiangqi)
996         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
997     if(gameInfo.variant == VariantKnightmate)
998         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
999
1000     if (rt >= 0) {
1001         if (enPassant) {
1002             captured = board[rf][ft];
1003             board[rf][ft] = EmptySquare;
1004         } else {
1005             captured = board[rt][ft];
1006         }
1007         if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1008             board[rt][ft] = board[rf][ff];
1009             board[rf][ff] = EmptySquare;
1010         }
1011     }
1012
1013     /* For compatibility with ICS wild 9, we scan the board in the
1014        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1015        and we test only whether that one is in check. */
1016     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1017         for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1018           if (board[cl.rking][cl.fking] == king) {
1019               cl.check = 0;
1020               if(gameInfo.variant == VariantXiangqi) {
1021                   /* [HGM] In Xiangqi opposing Kings means check as well */
1022                   int i, dir;
1023                   dir = (king >= BlackPawn) ? -1 : 1;
1024                   for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1025                                 board[i][cl.fking] == EmptySquare; i+=dir );
1026                   if(i>=0 && i<BOARD_HEIGHT &&
1027                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1028                           cl.check++;
1029               }
1030               GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1031               if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1032                  goto undo_move;  /* 2-level break */
1033           }
1034       }
1035
1036   undo_move:
1037
1038     if (rt >= 0) {
1039         if(rf != DROP_RANK) // [HGM] drop
1040             board[rf][ff] = board[rt][ft];
1041         if (enPassant) {
1042             board[rf][ft] = captured;
1043             board[rt][ft] = EmptySquare;
1044         } else {
1045             board[rt][ft] = captured;
1046         }
1047     }
1048
1049     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1050 }
1051
1052 ChessMove
1053 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1054 {   // [HGM] put drop legality testing in separate routine for clarity
1055     int n;
1056 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1057     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1058     n = PieceToNumber(piece);
1059     if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1060         && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1061         return ImpossibleMove; // piece not available
1062     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1063         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1064            (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1065             piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1066             piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1067         if(piece == WhitePawn || piece == BlackPawn) {
1068             int r;
1069             for(r=1; r<BOARD_HEIGHT-1; r++)
1070                 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
1071             // should still test if we mate with this Pawn
1072         }
1073     } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1074         if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1075     } else {
1076         if( (piece == WhitePawn || piece == BlackPawn) &&
1077             (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1078             return IllegalMove; /* no pawn drops on 1st/8th */
1079     }
1080 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1081     if (!(flags & F_IGNORE_CHECK) &&
1082         CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1083     return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1084 }
1085
1086 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1087                                     int rf, int ff, int rt, int ft,
1088                                     VOIDSTAR closure));
1089
1090 void
1091 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1092 {
1093     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1094
1095     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1096         cl->captures++; // [HGM] losers: count legal captures
1097     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1098       cl->kind = kind;
1099 }
1100
1101 ChessMove
1102 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1103 {
1104     LegalityTestClosure cl; ChessSquare piece, filterPiece;
1105
1106     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1107     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1108     piece = filterPiece = board[rf][ff];
1109     if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1110
1111     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1112     /* (perhaps we should disallow moves that obviously leave us in check?)              */
1113     if(piece == WhiteFalcon || piece == BlackFalcon ||
1114        piece == WhiteCobra  || piece == BlackCobra)
1115         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1116
1117     cl.rf = rf;
1118     cl.ff = ff;
1119     cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1120     cl.ft = fFilter = ft;
1121     cl.kind = IllegalMove;
1122     cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1123     if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1124     GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1125     if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1126                 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1127         return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1128
1129     if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1130     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1131         if(board[rf][ff] < BlackPawn) { // white
1132             if(rf != 0) return IllegalMove; // must be on back rank
1133             if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1134             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1135         } else {
1136             if(rf != BOARD_HEIGHT-1) return IllegalMove;
1137             if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1138             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1139         }
1140     } else
1141     if(gameInfo.variant == VariantShogi) {
1142         /* [HGM] Shogi promotions. '=' means defer */
1143         if(rf != DROP_RANK && cl.kind == NormalMove) {
1144             ChessSquare piece = board[rf][ff];
1145
1146             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1147             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1148                promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1149                promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1150                   promoChar = '+'; // allowed ICS notations
1151 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1152             if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1153                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1154             else if(flags & F_WHITE_ON_MOVE) {
1155                 if( (int) piece < (int) WhiteWazir &&
1156                      (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1157                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1158                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1159                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1160                     else /* promotion optional, default is defer */
1161                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1162                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1163             } else {
1164                 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1165                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1166                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1167                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1168                     else /* promotion optional, default is defer */
1169                        cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1170                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1171             }
1172         }
1173     } else
1174     if (promoChar != NULLCHAR) {
1175         if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1176         if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1177             ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1178             if(piece == EmptySquare)
1179                 cl.kind = ImpossibleMove; // non-existing piece
1180             if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1181                 if(promoChar != PieceToChar(BlackKing)) {
1182                     if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1183                     if(piece == BlackLance) cl.kind = ImpossibleMove;
1184                 } else { // promotion to King allowed only if we do not haave two yet
1185                     int r, f, kings = 0;
1186                     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1187                     if(kings == 2) cl.kind = IllegalMove;
1188                 }
1189             } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1190                           piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1191             else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1192              cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1193             else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1194              cl.kind = IllegalMove; // promotion to King usually not allowed
1195         } else {
1196             cl.kind = IllegalMove;
1197         }
1198     }
1199     return cl.kind;
1200 }
1201
1202 typedef struct {
1203     int count;
1204 } MateTestClosure;
1205
1206 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1207                                 int rf, int ff, int rt, int ft,
1208                                 VOIDSTAR closure));
1209
1210 void
1211 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1212 {
1213     register MateTestClosure *cl = (MateTestClosure *) closure;
1214
1215     cl->count++;
1216 }
1217
1218 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1219 int
1220 MateTest (Board board, int flags)
1221 {
1222     MateTestClosure cl;
1223     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1224     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1225
1226     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1227         // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1228         nrKing += (board[r][f] == king);   // stm has king
1229         if( board[r][f] != EmptySquare ) {
1230             if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1231                  myPieces++;
1232             else hisPieces++;
1233         }
1234     }
1235     switch(gameInfo.variant) { // [HGM] losers: extinction wins
1236         case VariantShatranj:
1237                 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1238         default:
1239                 break;
1240         case VariantAtomic:
1241                 if(nrKing == 0) return MT_NOKING;
1242                 break;
1243         case VariantLosers:
1244                 if(myPieces == 1) return MT_BARE;
1245     }
1246     cl.count = 0;
1247     inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1248     // [HGM] 3check: yet to do!
1249     if (cl.count > 0) {
1250         return inCheck ? MT_CHECK : MT_NONE;
1251     } else {
1252         if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1253                                  && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1254             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1255             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1256                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1257                     if(board[n][holdings] != EmptySquare) {
1258                         int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1259                         if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1260                     }
1261         }
1262         if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1263                 return myPieces == hisPieces ? MT_STALEMATE :
1264                                         myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1265         else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1266         else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1267
1268         return inCheck ? MT_CHECKMATE
1269                        : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || gameInfo.variant == VariantShogi) ?
1270                           MT_STAINMATE : MT_STALEMATE;
1271     }
1272 }
1273
1274
1275 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1276                                     int rf, int ff, int rt, int ft,
1277                                     VOIDSTAR closure));
1278
1279 void
1280 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1281 {
1282     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1283     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1284
1285     // [HGM] wild: for wild-card pieces rt and rf are dummies
1286     if(piece == WhiteFalcon || piece == BlackFalcon ||
1287        piece == WhiteCobra  || piece == BlackCobra)
1288         wildCard = TRUE;
1289
1290     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1291          || PieceToChar(board[rf][ff]) == '~'
1292               && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1293                                                                       ) &&
1294         (cl->rfIn == -1 || cl->rfIn == rf) &&
1295         (cl->ffIn == -1 || cl->ffIn == ff) &&
1296         (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1297         (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1298
1299         cl->count++;
1300         if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1301           // [HGM] oneclick: if multiple moves, be sure we remember capture
1302           cl->piece = board[rf][ff];
1303           cl->rf = rf;
1304           cl->ff = ff;
1305           cl->rt = wildCard ? cl->rtIn : rt;
1306           cl->ft = wildCard ? cl->ftIn : ft;
1307           cl->kind = kind;
1308         }
1309         cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1310     }
1311 }
1312
1313 void
1314 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
1315 {
1316     int illegal = 0; char c = closure->promoCharIn;
1317
1318     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1319     closure->count = closure->captures = 0;
1320     closure->rf = closure->ff = closure->rt = closure->ft = 0;
1321     closure->kind = ImpossibleMove;
1322     rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
1323     fFilter = closure->ftIn;
1324     if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
1325         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1326         if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
1327             closure->count = closure->captures = 0;
1328             closure->rf = closure->ff = closure->rt = closure->ft = 0;
1329             closure->kind = ImpossibleMove;
1330             GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1331         }
1332     } else
1333     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1334     if (closure->count == 0) {
1335         /* See if it's an illegal move due to check */
1336         illegal = 1;
1337         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1338         if (closure->count == 0) {
1339             /* No, it's not even that */
1340           if(!appData.testLegality && closure->pieceIn != EmptySquare) {
1341             int f, r; // if there is only a single piece of the requested type on the board, use that
1342             closure->rt = closure->rtIn, closure->ft = closure->ftIn;
1343             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1344                 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
1345             if(closure->count > 1) illegal = 0; // ambiguous
1346           }
1347           if(closure->count == 0) {
1348             if (appData.debugMode) { int i, j;
1349                 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1350                     for(j=0; j<BOARD_WIDTH; j++)
1351                         fprintf(debugFP, "%3d", (int) board[i][j]);
1352                     fprintf(debugFP, "\n");
1353                 }
1354             }
1355             return;
1356           }
1357         }
1358     }
1359
1360     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1361     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1362         if(closure->piece < BlackPawn) { // white
1363             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1364             if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
1365             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1366         } else {
1367             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1368             if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
1369             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1370         }
1371     } else
1372     if(gameInfo.variant == VariantShogi) {
1373         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1374         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1375             ChessSquare piece = closure->piece;
1376             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1377                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1378                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1379                    c = '+'; // allowed ICS notations
1380             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1381             else if(flags & F_WHITE_ON_MOVE) {
1382                 if( (int) piece < (int) WhiteWazir &&
1383                      (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1384                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1385                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1386                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1387                     else /* promotion optional, default is defer */
1388                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1389                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1390             } else {
1391                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1392                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1393                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1394                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1395                     else /* promotion optional, default is defer */
1396                        closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1397                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1398             }
1399         }
1400         if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1401         if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1402     } else
1403     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1404         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1405             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
1406                 c = PieceToChar(BlackFerz);
1407             else if(gameInfo.variant == VariantGreat)
1408                 c = PieceToChar(BlackMan);
1409             else if(gameInfo.variant == VariantGrand)
1410                     closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
1411             else
1412                 c = PieceToChar(BlackQueen);
1413         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1414     } else if (c != NULLCHAR) closure->kind = IllegalMove;
1415
1416     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1417     if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1418         closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1419     if (closure->count > 1) {
1420         closure->kind = AmbiguousMove;
1421     }
1422     if (illegal) {
1423         /* Note: If more than one illegal move matches, but no legal
1424            moves, we return IllegalMove, not AmbiguousMove.  Caller
1425            can look at closure->count to detect this.
1426         */
1427         closure->kind = IllegalMove;
1428     }
1429 }
1430
1431
1432 typedef struct {
1433     /* Input */
1434     ChessSquare piece;
1435     int rf, ff, rt, ft;
1436     /* Output */
1437     ChessMove kind;
1438     int rank;
1439     int file;
1440     int either;
1441 } CoordsToAlgebraicClosure;
1442
1443 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1444                                          ChessMove kind, int rf, int ff,
1445                                          int rt, int ft, VOIDSTAR closure));
1446
1447 void
1448 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1449 {
1450     register CoordsToAlgebraicClosure *cl =
1451       (CoordsToAlgebraicClosure *) closure;
1452
1453     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1454         (board[rf][ff] == cl->piece
1455          || PieceToChar(board[rf][ff]) == '~' &&
1456             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1457                                      ) {
1458         if (rf == cl->rf) {
1459             if (ff == cl->ff) {
1460                 cl->kind = kind; /* this is the move we want */
1461             } else {
1462                 cl->file++; /* need file to rule out this move */
1463             }
1464         } else {
1465             if (ff == cl->ff) {
1466                 cl->rank++; /* need rank to rule out this move */
1467             } else {
1468                 cl->either++; /* rank or file will rule out this move */
1469             }
1470         }
1471     }
1472 }
1473
1474 /* Convert coordinates to normal algebraic notation.
1475    promoChar must be NULLCHAR or 'x' if not a promotion.
1476 */
1477 ChessMove
1478 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
1479 {
1480     ChessSquare piece;
1481     ChessMove kind;
1482     char *outp = out, c;
1483     CoordsToAlgebraicClosure cl;
1484
1485     if (rf == DROP_RANK) {
1486         if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
1487         /* Bughouse piece drop */
1488         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1489         *outp++ = '@';
1490         *outp++ = ft + AAA;
1491         if(rt+ONE <= '9')
1492            *outp++ = rt + ONE;
1493         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1494         *outp = NULLCHAR;
1495         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1496     }
1497
1498     if (promoChar == 'x') promoChar = NULLCHAR;
1499     piece = board[rf][ff];
1500     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1501
1502     switch (piece) {
1503       case WhitePawn:
1504       case BlackPawn:
1505         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1506         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1507             /* Keep short notation if move is illegal only because it
1508                leaves the player in check, but still return IllegalMove */
1509             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1510             if (kind == IllegalMove) break;
1511             kind = IllegalMove;
1512         }
1513         /* Pawn move */
1514         *outp++ = ff + AAA;
1515         if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1516             /* Non-capture; use style "e5" */
1517             if(rt+ONE <= '9')
1518                *outp++ = rt + ONE;
1519             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1520         } else {
1521             /* Capture; use style "exd5" */
1522             if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1523             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
1524             *outp++ = ft + AAA;
1525             if(rt+ONE <= '9')
1526                *outp++ = rt + ONE;
1527             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1528         }
1529         /* Use promotion suffix style "=Q" */
1530         *outp = NULLCHAR;
1531         if (promoChar != NULLCHAR) {
1532             if(gameInfo.variant == VariantShogi) {
1533                 /* [HGM] ... but not in Shogi! */
1534                 *outp++ = promoChar == '=' ? '=' : '+';
1535             } else {
1536                 *outp++ = '=';
1537                 *outp++ = ToUpper(promoChar);
1538             }
1539             *outp = NULLCHAR;
1540         }
1541         return kind;
1542
1543
1544       case WhiteKing:
1545       case BlackKing:
1546         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1547         /* Code added by Tord:  FRC castling. */
1548         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1549            (piece == BlackKing && board[rt][ft] == BlackRook)) {
1550           if(ft > ff)
1551             safeStrCpy(out, "O-O", MOVE_LEN);
1552           else
1553             safeStrCpy(out, "O-O-O", MOVE_LEN);
1554           return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1555         }
1556         /* End of code added by Tord */
1557         /* Test for castling or ICS wild castling */
1558         /* Use style "O-O" (oh-oh) for PGN compatibility */
1559         else if (rf == rt &&
1560             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1561             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
1562             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1563              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1564             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1565               snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1566             else
1567               snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1568
1569             /* This notation is always unambiguous, unless there are
1570                kings on both the d and e files, with "wild castling"
1571                possible for the king on the d file and normal castling
1572                possible for the other.  ICS rules for wild 9
1573                effectively make castling illegal for either king in
1574                this situation.  So I am not going to worry about it;
1575                I'll just generate an ambiguous O-O in this case.
1576             */
1577             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1578         }
1579
1580         /* else fall through */
1581       default:
1582         /* Piece move */
1583         cl.rf = rf;
1584         cl.ff = ff;
1585         cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1586         cl.ft = fFilter = ft;
1587         cl.piece = piece;
1588         cl.kind = IllegalMove;
1589         cl.rank = cl.file = cl.either = 0;
1590         c = PieceToChar(piece) ;
1591         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
1592
1593         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1594             /* Generate pretty moves for moving into check, but
1595                still return IllegalMove.
1596             */
1597             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
1598             if (cl.kind == IllegalMove) break;
1599             cl.kind = IllegalMove;
1600         }
1601
1602         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1603            else "Ngf3" or "Ngxf7",
1604            else "N1f3" or "N5xf7",
1605            else "Ng1f3" or "Ng5xf7".
1606         */
1607         if( c == '~' || c == '+') {
1608            /* [HGM] print nonexistent piece as its demoted version */
1609            piece = (ChessSquare) (DEMOTED piece);
1610         }
1611         if(c=='+') *outp++ = c;
1612         *outp++ = ToUpper(PieceToChar(piece));
1613
1614         if (cl.file || (cl.either && !cl.rank)) {
1615             *outp++ = ff + AAA;
1616         }
1617         if (cl.rank) {
1618             if(rf+ONE <= '9')
1619                 *outp++ = rf + ONE;
1620             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1621         }
1622
1623         if(board[rt][ft] != EmptySquare)
1624           *outp++ = 'x';
1625
1626         *outp++ = ft + AAA;
1627         if(rt+ONE <= '9')
1628            *outp++ = rt + ONE;
1629         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1630         if (gameInfo.variant == VariantShogi) {
1631             /* [HGM] in Shogi non-pawns can promote */
1632             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
1633         }
1634         else if (gameInfo.variant != VariantSuper && promoChar &&
1635                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
1636             *outp++ = '=';
1637             *outp++ = ToUpper(promoChar);
1638         }
1639         else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
1640             *outp++ = '/';
1641             *outp++ = ToUpper(promoChar);
1642         }
1643         *outp = NULLCHAR;
1644         return cl.kind;
1645
1646       case EmptySquare:
1647         /* Moving a nonexistent piece */
1648         break;
1649     }
1650
1651     /* Not a legal move, even ignoring check.
1652        If there was a piece on the from square,
1653        use style "Ng1g3" or "Ng1xe8";
1654        if there was a pawn or nothing (!),
1655        use style "g1g3" or "g1xe8".  Use "x"
1656        if a piece was on the to square, even
1657        a piece of the same color.
1658     */
1659     outp = out;
1660     c = 0;
1661     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1662         int r, f;
1663       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
1664                 c += (board[r][f] == piece); // count on-board pieces of given type
1665         *outp++ = ToUpper(PieceToChar(piece));
1666     }
1667   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
1668     *outp++ = ff + AAA;
1669     if(rf+ONE <= '9')
1670        *outp++ = rf + ONE;
1671     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1672   }
1673     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1674     *outp++ = ft + AAA;
1675     if(rt+ONE <= '9')
1676        *outp++ = rt + ONE;
1677     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1678     /* Use promotion suffix style "=Q" */
1679     if (promoChar != NULLCHAR && promoChar != 'x') {
1680         *outp++ = '=';
1681         *outp++ = ToUpper(promoChar);
1682     }
1683     *outp = NULLCHAR;
1684
1685     return IllegalMove;
1686 }
1687
1688 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1689
1690 typedef struct {
1691     /* Input */
1692     int rf, ff, rt, ft;
1693     /* Output */
1694     int recaptures;
1695 } ChaseClosure;
1696
1697 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1698
1699 int preyStackPointer, chaseStackPointer;
1700
1701 struct {
1702 unsigned char rf, ff, rt, ft;
1703 } chaseStack[100];
1704
1705 struct {
1706 unsigned char rank, file;
1707 } preyStack[100];
1708
1709
1710
1711
1712 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1713
1714 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1715                                 int rf, int ff, int rt, int ft,
1716                                 VOIDSTAR closure));
1717
1718 void
1719 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1720 {   // For adding captures that can lead to chase indictment to the chaseStack
1721     if(board[rt][ft] == EmptySquare) return;                               // non-capture
1722     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1723     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1724     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
1725     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1726     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1727     chaseStack[chaseStackPointer].rf = rf;
1728     chaseStack[chaseStackPointer].ff = ff;
1729     chaseStack[chaseStackPointer].rt = rt;
1730     chaseStack[chaseStackPointer].ft = ft;
1731     chaseStackPointer++;
1732 }
1733
1734 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1735                                 int rf, int ff, int rt, int ft,
1736                                 VOIDSTAR closure));
1737
1738 void
1739 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1740 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1741     int i;
1742     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1743
1744     if(board[rt][ft] == EmptySquare) return; // no capture
1745     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1746         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
1747     }
1748     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1749     for(i=0; i<chaseStackPointer; i++) {
1750         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1751            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
1752             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1753             chaseStack[i] = chaseStack[--chaseStackPointer];
1754             break;
1755         }
1756     }
1757 }
1758
1759 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1760                                 int rf, int ff, int rt, int ft,
1761                                 VOIDSTAR closure));
1762
1763 void
1764 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1765 {   // for determining if a piece (given through the closure) is protected
1766     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1767
1768     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
1769     if(appData.debugMode && board[rt][ft] != EmptySquare)
1770         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1771 }
1772
1773 extern char moveList[MAX_MOVES][MOVE_LEN];
1774
1775 int
1776 PerpetualChase (int first, int last)
1777 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1778     int i, j, k, tail;
1779     ChaseClosure cl;
1780     ChessSquare captured;
1781
1782     preyStackPointer = 0;        // clear stack of chased pieces
1783     for(i=first; i<last; i+=2) { // for all positions with same side to move
1784         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1785         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
1786         // determine all captures possible after the move, and put them on chaseStack
1787         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
1788         if(appData.debugMode) { int n;
1789             for(n=0; n<chaseStackPointer; n++)
1790                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1791                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1792             fprintf(debugFP, ": all capts\n");
1793         }
1794         // determine all captures possible before the move, and delete them from chaseStack
1795         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1796         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1797         cl.rt = moveList[i][3]-ONE;
1798         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1799         CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
1800         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
1801         xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
1802         if(appData.debugMode) { int n;
1803             for(n=0; n<chaseStackPointer; n++)
1804                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1805                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1806             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1807         }
1808         // chaseSack now contains all captures made possible by the move
1809         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1810             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1811             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1812
1813             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1814             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
1815
1816             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1817                 continue; // C or H attack on R is always chase; leave on chaseStack
1818
1819             if(attacker == victim) {
1820                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
1821                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1822                         // we can capture back with equal piece, so this is no chase but a sacrifice
1823                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1824                         j--; /* ! */ continue;
1825                 }
1826
1827             }
1828
1829             // the attack is on a lower piece, or on a pinned or blocked equal one
1830             CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
1831             CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
1832             // test if the victim is protected by a true protector. First make the capture.
1833             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1834             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1835             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1836             // Then test if the opponent can recapture
1837             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
1838             cl.rt = chaseStack[j].rt;
1839             cl.ft = chaseStack[j].ft;
1840             if(appData.debugMode) {
1841                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1842             }
1843             xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
1844             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
1845             xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
1846             // unmake the capture
1847             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1848             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1849             // if a recapture was found, piece is protected, and we are not chasing it.
1850             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1851                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1852                 j--; /* ! */
1853             }
1854         }
1855         // chaseStack now contains all moves that chased
1856         if(appData.debugMode) { int n;
1857             for(n=0; n<chaseStackPointer; n++)
1858                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1859                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1860             fprintf(debugFP, ": chases\n");
1861         }
1862         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1863             for(j=0; j<chaseStackPointer; j++) {
1864                 preyStack[j].rank = chaseStack[j].rt;
1865                 preyStack[j].file = chaseStack[j].ft;
1866             }
1867             preyStackPointer = chaseStackPointer;
1868         }
1869         tail = 0;
1870         for(j=0; j<chaseStackPointer; j++) {
1871             for(k=0; k<preyStackPointer; k++) {
1872                 // search the victim of each chase move on the preyStack (first occurrence)
1873                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1874                     if(k < tail) break; // piece was already identified as still being chased
1875                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1876                     preyStack[tail] = preyStack[k];                // by swapping
1877                     preyStack[k] = preyStack[preyStackPointer];
1878                     tail++;
1879                     break;
1880                 }
1881             }
1882         }
1883         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1884         if(appData.debugMode) { int n;
1885             for(n=0; n<preyStackPointer; n++)
1886                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1887             fprintf(debugFP, "always chased upto ply %d\n", i);
1888         }
1889         // now adjust the location of the chased pieces according to opponent move
1890         for(j=0; j<preyStackPointer; j++) {
1891             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1892                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1893                 preyStack[j].rank = moveList[i+1][3]-ONE;
1894                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1895                 break;
1896             }
1897         }
1898     }
1899     return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
1900                                 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the
1901 }