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