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