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