Fix moves of Spartan Captain
[xboard.git] / moves.c
1 /*
2  * moves.c - Move generation and checking
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
9  *
10  * Enhancements Copyright 2005 Alessandro Scotti
11  *
12  * The following terms apply to Digital Equipment Corporation's copyright
13  * interest in XBoard:
14  * ------------------------------------------------------------------------
15  * All Rights Reserved
16  *
17  * Permission to use, copy, modify, and distribute this software and its
18  * documentation for any purpose and without fee is hereby granted,
19  * provided that the above copyright notice appear in all copies and that
20  * both that copyright notice and this permission notice appear in
21  * supporting documentation, and that the name of Digital not be
22  * used in advertising or publicity pertaining to distribution of the
23  * software without specific, written prior permission.
24  *
25  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
26  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
27  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
28  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
29  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
30  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31  * SOFTWARE.
32  * ------------------------------------------------------------------------
33  *
34  * The following terms apply to the enhanced version of XBoard
35  * distributed by the Free Software Foundation:
36  * ------------------------------------------------------------------------
37  *
38  * GNU XBoard is free software: you can redistribute it and/or modify
39  * it under the terms of the GNU General Public License as published by
40  * the Free Software Foundation, either version 3 of the License, or (at
41  * your option) any later version.
42  *
43  * GNU XBoard is distributed in the hope that it will be useful, but
44  * WITHOUT ANY WARRANTY; without even the implied warranty of
45  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
46  * General Public License for more details.
47  *
48  * You should have received a copy of the GNU General Public License
49  * along with this program. If not, see http://www.gnu.org/licenses/.  *
50  *
51  *------------------------------------------------------------------------
52  ** See the file ChangeLog for a revision history.  */
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #if HAVE_STRING_H
58 # include <string.h>
59 #else /* not HAVE_STRING_H */
60 # include <strings.h>
61 #endif /* not HAVE_STRING_H */
62 #include "common.h"
63 #include "backend.h"
64 #include "moves.h"
65 #include "parser.h"
66
67 int WhitePiece P((ChessSquare));
68 int BlackPiece P((ChessSquare));
69 int SameColor P((ChessSquare, ChessSquare));
70 int PosFlags(int index);
71
72 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
73 int quickFlag;
74
75 int
76 WhitePiece (ChessSquare piece)
77 {
78     return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
79 }
80
81 int
82 BlackPiece (ChessSquare piece)
83 {
84     return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
85 }
86
87 #if 0
88 int
89 SameColor (ChessSquare piece1, ChessSquare piece2)
90 {
91     return ((int) piece1 >= (int) WhitePawn &&   /* [HGM] can be > King ! */
92             (int) piece1 <  (int) BlackPawn &&
93             (int) piece2 >= (int) WhitePawn &&
94             (int) piece2 <  (int) BlackPawn)
95       ||   ((int) piece1 >= (int) BlackPawn &&
96             (int) piece1 <  (int) EmptySquare &&
97             (int) piece2 >= (int) BlackPawn &&
98             (int) piece2 <  (int) EmptySquare);
99 }
100 #else
101 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
102 #endif
103
104 char pieceToChar[] = {
105                         'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
106                         'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
107                         'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
108                         'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
109                         'x' };
110 char pieceNickName[EmptySquare];
111
112 char
113 PieceToChar (ChessSquare p)
114 {
115     if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
116     return pieceToChar[(int) p];
117 }
118
119 int
120 PieceToNumber (ChessSquare p)  /* [HGM] holdings: count piece type, ignoring non-participating piece types */
121 {
122     int i=0;
123     ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
124
125     while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
126     return i;
127 }
128
129 ChessSquare
130 CharToPiece (int c)
131 {
132      int i;
133      if(c == '.') return EmptySquare;
134      for(i=0; i< (int) EmptySquare; i++)
135           if(pieceNickName[i] == c) return (ChessSquare) i;
136      for(i=0; i< (int) EmptySquare; i++)
137           if(pieceToChar[i] == c) return (ChessSquare) i;
138      return EmptySquare;
139 }
140
141 void
142 CopyBoard (Board to, Board from)
143 {
144     int i, j;
145
146     for (i = 0; i < BOARD_HEIGHT; i++)
147       for (j = 0; j < BOARD_WIDTH; j++)
148         to[i][j] = from[i][j];
149     for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
150         to[VIRGIN][j] = from[VIRGIN][j],
151         to[CASTLING][j] = from[CASTLING][j];
152     to[HOLDINGS_SET] = 0; // flag used in ICS play
153 }
154
155 int
156 CompareBoards (Board board1, Board board2)
157 {
158     int i, j;
159
160     for (i = 0; i < BOARD_HEIGHT; i++)
161       for (j = 0; j < BOARD_WIDTH; j++) {
162           if (board1[i][j] != board2[i][j])
163             return FALSE;
164     }
165     return TRUE;
166 }
167
168 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
169
170 void
171 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
172 {
173   int i, rt, ft = ff;
174   for (i = 1;; i++) {
175       rt = rf + i;
176       if (rt >= BOARD_HEIGHT) break;
177       if (SameColor(board[rf][ff], board[rt][ft])) break;
178       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
179       if (board[rt][ft] != EmptySquare) break;
180   }
181 }
182
183 void
184 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
185 {
186   int i, rt, ft = ff;
187   for (i = 1;; i++) {
188       rt = rf - i;
189       if (rt < 0) break;
190       if (SameColor(board[rf][ff], board[rt][ft])) break;
191       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
192       if (board[rt][ft] != EmptySquare) break;
193   }
194 }
195
196 void
197 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
198 {
199   SlideForward(board, flags, rf, ff, callback, closure);
200   SlideBackward(board, flags, rf, ff, callback, closure);
201 }
202
203 void
204 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
205 {
206   int i, s, rt = rf, ft;
207   for(s = -1; s <= 1; s+= 2) {
208     for (i = 1;; i++) {
209       ft = ff + i*s;
210       if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
211       if (SameColor(board[rf][ff], board[rt][ft])) break;
212       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
213       if (board[rt][ft] != EmptySquare) break;
214     }
215   }
216 }
217
218 void
219 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
220 {
221   int i, s, rt, ft;
222   for(s = -1; s <= 1; s+= 2) {
223     for (i = 1;; i++) {
224       rt = rf + i;
225       ft = ff + i * s;
226       if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
227       if (SameColor(board[rf][ff], board[rt][ft])) break;
228       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
229       if (board[rt][ft] != EmptySquare) break;
230     }
231   }
232 }
233
234 void
235 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
236 {
237   int i, s, rt, ft;
238   for(s = -1; s <= 1; s+= 2) {
239     for (i = 1;; i++) {
240       rt = rf - i;
241       ft = ff + i * s;
242       if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
243       if (SameColor(board[rf][ff], board[rt][ft])) break;
244       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
245       if (board[rt][ft] != EmptySquare) break;
246     }
247   }
248 }
249
250 void
251 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
252 {
253   SlideVertical(board, flags, rf, ff, callback, closure);
254   SlideSideways(board, flags, rf, ff, callback, closure);
255 }
256
257 void
258 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
259 {
260   SlideDiagForward(board, flags, rf, ff, callback, closure);
261   SlideDiagBackward(board, flags, rf, ff, callback, closure);
262 }
263
264 void
265 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
266 { // Lion-like move of Horned Falcon and Souring Eagle
267   int ft = ff + dx, rt = rf + dy;
268   if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
269   if (!SameColor(board[rf][ff], board[rt][ft]))
270     callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
271   ft += dx; rt += dy;
272   if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
273   if (!SameColor(board[rf][ff], board[rt][ft]))
274     callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
275 }
276
277 void
278 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
279 {
280   int ft = ff, rt = rf + 1;
281   if (rt >= BOARD_HEIGHT) return;
282   if (SameColor(board[rf][ff], board[rt][ft])) return;
283   callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
284 }
285
286 void
287 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
288 {
289   int ft = ff, rt = rf - 1;
290   if (rt < 0) return;
291   if (SameColor(board[rf][ff], board[rt][ft])) return;
292   callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
293 }
294
295 void
296 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
297 {
298   int ft, rt = rf;
299   ft = ff + 1;
300   if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
301       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
302   ft = ff - 1;
303   if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
304       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
305 }
306
307 void
308 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
309 {
310   int ft, rt = rf + 1;
311   if (rt >= BOARD_HEIGHT) return;
312   ft = ff + 1;
313   if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
314       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
315   ft = ff - 1;
316   if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
317       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
318 }
319
320 void
321 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
322 {
323   int ft, rt = rf - 1;
324   if(rt < 0) return;
325   ft = ff + 1;
326   if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
327       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
328   ft = ff - 1;
329   if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
330       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
331 }
332
333 void
334 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
335 {
336   StepForward(board, flags, rf, ff, callback, closure);
337   StepBackward(board, flags, rf, ff, callback, closure);
338 }
339
340 void
341 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
342 {
343   StepDiagForward(board, flags, rf, ff, callback, closure);
344   StepDiagBackward(board, flags, rf, ff, callback, closure);
345 }
346
347 void
348 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
349 {
350   StepVertical(board, flags, rf, ff, callback, closure);
351   StepSideways(board, flags, rf, ff, callback, closure);
352 }
353
354 void
355 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
356 {
357     int i, j, s, rt, ft;
358     for (i = -1; i <= 1; i += 2)
359         for (j = -1; j <= 1; j += 2)
360             for (s = 1; s <= 2; s++) {
361                 rt = rf + i*s;
362                 ft = ff + j*(3-s);
363                 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
364                     && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
365                     && !SameColor(board[rf][ff], board[rt][ft]))
366                     callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
367             }
368 }
369
370 /* Call callback once for each pseudo-legal move in the given
371    position, except castling moves. A move is pseudo-legal if it is
372    legal, or if it would be legal except that it leaves the king in
373    check.  In the arguments, epfile is EP_NONE if the previous move
374    was not a double pawn push, or the file 0..7 if it was, or
375    EP_UNKNOWN if we don't know and want to allow all e.p. captures.
376    Promotion moves generated are to Queen only.
377 */
378 void
379 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
380 // speed: only do moves with this piece type
381 {
382     int rf, ff;
383     int i, j, d, s, fs, rs, rt, ft, m;
384     int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
385     int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
386
387     for (rf = 0; rf < BOARD_HEIGHT; rf++)
388       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
389           ChessSquare piece;
390
391           if(board[rf][ff] == EmptySquare) continue;
392           if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
393           m = 0; piece = board[rf][ff];
394           if(PieceToChar(piece) == '~')
395                  piece = (ChessSquare) ( DEMOTED piece );
396           if(filter != EmptySquare && piece != filter) continue;
397           if(IS_SHOGI(gameInfo.variant))
398                  piece = (ChessSquare) ( SHOGI piece );
399
400           switch ((int)piece) {
401             /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
402             default:
403               /* can't happen ([HGM] except for faries...) */
404               break;
405
406              case WhitePawn:
407               if(gameInfo.variant == VariantXiangqi) {
408                   /* [HGM] capture and move straight ahead in Xiangqi */
409                   if (rf < BOARD_HEIGHT-1 &&
410                            !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
411                            callback(board, flags, NormalMove,
412                                     rf, ff, rf + 1, ff, closure);
413                   }
414                   /* and move sideways when across the river */
415                   for (s = -1; s <= 1; s += 2) {
416                       if (rf >= BOARD_HEIGHT>>1 &&
417                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
418                           !WhitePiece(board[rf][ff+s]) ) {
419                            callback(board, flags, NormalMove,
420                                     rf, ff, rf, ff+s, closure);
421                       }
422                   }
423                   break;
424               }
425               if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
426                   callback(board, flags,
427                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
428                            rf, ff, rf + 1, ff, closure);
429               }
430               if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
431                   gameInfo.variant != VariantShatranj && /* [HGM] */
432                   gameInfo.variant != VariantCourier  && /* [HGM] */
433                   board[rf+2][ff] == EmptySquare ) {
434                       callback(board, flags, NormalMove,
435                                rf, ff, rf+2, ff, closure);
436               }
437               for (s = -1; s <= 1; s += 2) {
438                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
439                       ((flags & F_KRIEGSPIEL_CAPTURE) ||
440                        BlackPiece(board[rf + 1][ff + s]))) {
441                       callback(board, flags,
442                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
443                                rf, ff, rf + 1, ff + s, closure);
444                   }
445                   if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
446                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
447                           (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
448                           board[rf][ff + s] == BlackPawn &&
449                           board[rf+1][ff + s] == EmptySquare) {
450                           callback(board, flags, WhiteCapturesEnPassant,
451                                    rf, ff, rf+1, ff + s, closure);
452                       }
453                   }
454               }
455               break;
456
457             case BlackPawn:
458               if(gameInfo.variant == VariantXiangqi) {
459                   /* [HGM] capture straight ahead in Xiangqi */
460                   if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
461                            callback(board, flags, NormalMove,
462                                     rf, ff, rf - 1, ff, closure);
463                   }
464                   /* and move sideways when across the river */
465                   for (s = -1; s <= 1; s += 2) {
466                       if (rf < BOARD_HEIGHT>>1 &&
467                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
468                           !BlackPiece(board[rf][ff+s]) ) {
469                            callback(board, flags, NormalMove,
470                                     rf, ff, rf, ff+s, closure);
471                       }
472                   }
473                   break;
474               }
475               if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
476                   callback(board, flags,
477                            rf <= promoRank ? BlackPromotion : NormalMove,
478                            rf, ff, rf - 1, ff, closure);
479               }
480               if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
481                   gameInfo.variant != VariantShatranj && /* [HGM] */
482                   gameInfo.variant != VariantCourier  && /* [HGM] */
483                   board[rf-2][ff] == EmptySquare) {
484                   callback(board, flags, NormalMove,
485                            rf, ff, rf-2, ff, closure);
486               }
487               for (s = -1; s <= 1; s += 2) {
488                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
489                       ((flags & F_KRIEGSPIEL_CAPTURE) ||
490                        WhitePiece(board[rf - 1][ff + s]))) {
491                       callback(board, flags,
492                                rf <= promoRank ? BlackPromotion : NormalMove,
493                                rf, ff, rf - 1, ff + s, closure);
494                   }
495                   if (rf < BOARD_HEIGHT>>1) {
496                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
497                           (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
498                           board[rf][ff + s] == WhitePawn &&
499                           board[rf-1][ff + s] == EmptySquare) {
500                           callback(board, flags, BlackCapturesEnPassant,
501                                    rf, ff, rf-1, ff + s, closure);
502                       }
503                   }
504               }
505               break;
506
507             case WhiteUnicorn:
508             case BlackUnicorn:
509             case WhiteKnight:
510             case BlackKnight:
511               for (i = -1; i <= 1; i += 2)
512                 for (j = -1; j <= 1; j += 2)
513                   for (s = 1; s <= 2; s++) {
514                       rt = rf + i*s;
515                       ft = ff + j*(3-s);
516                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
517                           && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
518                           && !SameColor(board[rf][ff], board[rt][ft]))
519                       callback(board, flags, NormalMove,
520                                rf, ff, rt, ft, closure);
521                   }
522               break;
523
524             case SHOGI WhiteKnight:
525               for (s = -1; s <= 1; s += 2) {
526                   if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
527                       !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
528                       callback(board, flags, NormalMove,
529                                rf, ff, rf + 2, ff + s, closure);
530                   }
531               }
532               break;
533
534             case SHOGI BlackKnight:
535               for (s = -1; s <= 1; s += 2) {
536                   if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
537                       !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
538                       callback(board, flags, NormalMove,
539                                rf, ff, rf - 2, ff + s, closure);
540                   }
541               }
542               break;
543
544             case WhiteCannon:
545             case BlackCannon:
546               for (d = 0; d <= 1; d++)
547                 for (s = -1; s <= 1; s += 2) {
548                   m = 0;
549                   for (i = 1;; i++) {
550                       rt = rf + (i * s) * d;
551                       ft = ff + (i * s) * (1 - d);
552                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
553                       if (m == 0 && board[rt][ft] == EmptySquare)
554                                  callback(board, flags, NormalMove,
555                                           rf, ff, rt, ft, closure);
556                       if (m == 1 && board[rt][ft] != EmptySquare &&
557                           !SameColor(board[rf][ff], board[rt][ft]) )
558                                  callback(board, flags, NormalMove,
559                                           rf, ff, rt, ft, closure);
560                       if (board[rt][ft] != EmptySquare && m++) break;
561                   }
562                 }
563               break;
564
565             /* Gold General (and all its promoted versions) . First do the */
566             /* diagonal forward steps, then proceed as normal Wazir        */
567             case SHOGI (PROMOTED WhitePawn):
568                 if(gameInfo.variant == VariantShogi) goto WhiteGold;
569             case SHOGI (PROMOTED BlackPawn):
570                 if(gameInfo.variant == VariantShogi) goto BlackGold;
571                 SlideVertical(board, flags, rf, ff, callback, closure);
572                 break;
573
574             case SHOGI (PROMOTED WhiteKnight):
575                 if(gameInfo.variant == VariantShogi) goto WhiteGold;
576             case SHOGI BlackDrunk:
577             case SHOGI BlackAlfil:
578                 Ferz(board, flags, rf, ff, callback, closure);
579                 StepSideways(board, flags, rf, ff, callback, closure);
580                 StepBackward(board, flags, rf, ff, callback, closure);
581                 break;
582
583             case SHOGI (PROMOTED BlackKnight):
584                 if(gameInfo.variant == VariantShogi) goto BlackGold;
585             case SHOGI WhiteDrunk:
586             case SHOGI WhiteAlfil:
587                 Ferz(board, flags, rf, ff, callback, closure);
588                 StepSideways(board, flags, rf, ff, callback, closure);
589                 StepForward(board, flags, rf, ff, callback, closure);
590                 break;
591
592
593             case SHOGI WhiteStag:
594             case SHOGI BlackStag:
595                 if(gameInfo.variant == VariantShogi) goto BlackGold;
596                 SlideVertical(board, flags, rf, ff, callback, closure);
597                 Ferz(board, flags, rf, ff, callback, closure);
598                 StepSideways(board, flags, rf, ff, callback, closure);
599                 break;
600
601             case SHOGI (PROMOTED WhiteQueen):
602             case SHOGI WhiteTokin:
603             case SHOGI WhiteWazir:
604             WhiteGold:
605                 StepDiagForward(board, flags, rf, ff, callback, closure);
606                 Wazir(board, flags, rf, ff, callback, closure);
607                 break;
608
609             case SHOGI (PROMOTED BlackQueen):
610             case SHOGI BlackTokin:
611             case SHOGI BlackWazir:
612             BlackGold:
613                 StepDiagBackward(board, flags, rf, ff, callback, closure);
614                 Wazir(board, flags, rf, ff, callback, closure);
615                 break;
616
617             case WhiteWazir:
618             case BlackWazir:
619                 Wazir(board, flags, rf, ff, callback, closure);
620                 break;
621
622             case SHOGI WhiteMarshall:
623             case SHOGI BlackMarshall:
624                 Ferz(board, flags, rf, ff, callback, closure);
625                 for (d = 0; d <= 1; d++)
626                     for (s = -2; s <= 2; s += 4) {
627                         rt = rf + s * d;
628                         ft = ff + s * (1 - d);
629                         if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
630                         if (!SameColor(board[rf][ff], board[rt][ft]) )
631                             callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
632                     }
633                 break;
634
635             case SHOGI WhiteAngel:
636             case SHOGI BlackAngel:
637                 Wazir(board, flags, rf, ff, callback, closure);
638
639             case WhiteAlfil:
640             case BlackAlfil:
641                 /* [HGM] support Shatranj pieces */
642                 for (rs = -1; rs <= 1; rs += 2)
643                   for (fs = -1; fs <= 1; fs += 2) {
644                       rt = rf + 2 * rs;
645                       ft = ff + 2 * fs;
646                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
647                           && ( gameInfo.variant != VariantXiangqi ||
648                                board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
649
650                           && !SameColor(board[rf][ff], board[rt][ft]))
651                                callback(board, flags, NormalMove,
652                                         rf, ff, rt, ft, closure);
653                       if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
654                          gameInfo.variant == VariantChu      || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
655                       rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
656                       ft = ff + fs;
657                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
658                           && !SameColor(board[rf][ff], board[rt][ft]))
659                                callback(board, flags, NormalMove,
660                                         rf, ff, rt, ft, closure);
661                   }
662                 if(gameInfo.variant == VariantSpartan)
663                    for(fs = -1; fs <= 1; fs += 2) {
664                       ft = ff + fs;
665                       if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
666                                callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
667                    }
668                 break;
669
670             /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
671             case WhiteCardinal:
672             case BlackCardinal:
673               if(gameInfo.variant == VariantChuChess) goto DragonHorse;
674               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
675                 for (s = -2; s <= 2; s += 4) {
676                       rt = rf + s * d;
677                       ft = ff + s * (1 - d);
678                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
679                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
680                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
681                   }
682
683             /* Shogi Dragon Horse has to continue with Wazir after Bishop */
684             case SHOGI WhiteCardinal:
685             case SHOGI BlackCardinal:
686             case SHOGI WhitePCardinal:
687             case SHOGI BlackPCardinal:
688             DragonHorse:
689                 Bishop(board, flags, rf, ff, callback, closure);
690                 Wazir(board, flags, rf, ff, callback, closure);
691                 break;
692
693             /* Capablanca Archbishop continues as Knight                  */
694             case WhiteAngel:
695             case BlackAngel:
696                 Knight(board, flags, rf, ff, callback, closure);
697
698             /* Shogi Bishops are ordinary Bishops */
699             case SHOGI WhiteBishop:
700             case SHOGI BlackBishop:
701             case SHOGI WhitePBishop:
702             case SHOGI BlackPBishop:
703             case WhiteBishop:
704             case BlackBishop:
705                 Bishop(board, flags, rf, ff, callback, closure);
706                 break;
707
708             /* Shogi Lance is unlike anything, and asymmetric at that */
709             case SHOGI WhiteQueen:
710               if(gameInfo.variant == VariantChu) goto doQueen;
711               for(i = 1;; i++) {
712                       rt = rf + i;
713                       ft = ff;
714                       if (rt >= BOARD_HEIGHT) break;
715                       if (SameColor(board[rf][ff], board[rt][ft])) break;
716                       callback(board, flags, NormalMove,
717                                rf, ff, rt, ft, closure);
718                       if (board[rt][ft] != EmptySquare) break;
719               }
720               break;
721
722             case SHOGI BlackQueen:
723               if(gameInfo.variant == VariantChu) goto doQueen;
724               for(i = 1;; i++) {
725                       rt = rf - i;
726                       ft = ff;
727                       if (rt < 0) break;
728                       if (SameColor(board[rf][ff], board[rt][ft])) break;
729                       callback(board, flags, NormalMove,
730                                rf, ff, rt, ft, closure);
731                       if (board[rt][ft] != EmptySquare) break;
732               }
733               break;
734
735             /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
736             case WhiteDragon:
737             case BlackDragon:
738               if(gameInfo.variant == VariantChuChess) goto DragonKing;
739               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
740                 for (s = -2; s <= 2; s += 4) {
741                       rt = rf + s * d;
742                       ft = ff + s * (1 - d);
743                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
744                       if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
745                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
746                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
747                   }
748               if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
749                 Wazir(board, flags, rf, ff, callback, closure);
750               else
751                 Rook(board, flags, rf, ff, callback, closure);
752               break;
753
754             /* Shogi Dragon King has to continue as Ferz after Rook moves */
755             case SHOGI WhiteDragon:
756             case SHOGI BlackDragon:
757             case SHOGI WhitePDragon:
758             case SHOGI BlackPDragon:
759             DragonKing:
760                 Rook(board, flags, rf, ff, callback, closure);
761                 Ferz(board, flags, rf, ff, callback, closure);
762                 break;
763               m++;
764
765             /* Capablanca Chancellor sets flag to continue as Knight      */
766             case WhiteMarshall:
767             case BlackMarshall:
768                 Rook(board, flags, rf, ff, callback, closure);
769                 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
770                     Ferz(board, flags, rf, ff, callback, closure);
771                 else
772                     Knight(board, flags, rf, ff, callback, closure);
773                 break;
774
775             /* Shogi Rooks are ordinary Rooks */
776             case SHOGI WhiteRook:
777             case SHOGI BlackRook:
778             case SHOGI WhitePRook:
779             case SHOGI BlackPRook:
780             case WhiteRook:
781             case BlackRook:
782                 Rook(board, flags, rf, ff, callback, closure);
783                 break;
784
785             case WhiteQueen:
786             case BlackQueen:
787             case SHOGI WhiteMother:
788             case SHOGI BlackMother:
789             doQueen:
790                 Rook(board, flags, rf, ff, callback, closure);
791                 Bishop(board, flags, rf, ff, callback, closure);
792                 break;
793
794            case SHOGI WhitePawn:
795                 StepForward(board, flags, rf, ff, callback, closure);
796                 break;
797
798             case SHOGI BlackPawn:
799                 StepBackward(board, flags, rf, ff, callback, closure);
800                 break;
801
802             case WhiteMan:
803                 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
804             case SHOGI WhiteFerz:
805                 Ferz(board, flags, rf, ff, callback, closure);
806                 StepForward(board, flags, rf, ff, callback, closure);
807                 break;
808
809             case BlackMan:
810                 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
811             case SHOGI BlackFerz:
812                 StepBackward(board, flags, rf, ff, callback, closure);
813
814             case WhiteFerz:
815             case BlackFerz:
816                 /* [HGM] support Shatranj pieces */
817                 Ferz(board, flags, rf, ff, callback, closure);
818                 break;
819
820             case WhiteSilver:
821             case BlackSilver:
822                 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
823
824             commoner:
825             case SHOGI WhiteMonarch:
826             case SHOGI BlackMonarch:
827             case SHOGI WhiteKing:
828             case SHOGI BlackKing:
829             case WhiteKing:
830             case BlackKing:
831                 Ferz(board, flags, rf, ff, callback, closure);
832                 Wazir(board, flags, rf, ff, callback, closure);
833                 break;
834
835             case WhiteNightrider:
836             case BlackNightrider:
837               for (i = -1; i <= 1; i += 2)
838                 for (j = -1; j <= 1; j += 2)
839                   for (s = 1; s <= 2; s++) {  int k;
840                     for(k=1;; k++) {
841                       rt = rf + k*i*s;
842                       ft = ff + k*j*(3-s);
843                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
844                       if (SameColor(board[rf][ff], board[rt][ft])) break;
845                       callback(board, flags, NormalMove,
846                                rf, ff, rt, ft, closure);
847                       if (board[rt][ft] != EmptySquare) break;
848                     }
849                   }
850               break;
851
852             Amazon:
853                 Bishop(board, flags, rf, ff, callback, closure);
854                 Rook(board, flags, rf, ff, callback, closure);
855                 Knight(board, flags, rf, ff, callback, closure);
856                 break;
857
858             // Use Lance as Berolina / Spartan Pawn.
859             case WhiteLance:
860               if(gameInfo.variant == VariantSuper) goto Amazon;
861               if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
862                   callback(board, flags,
863                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
864                            rf, ff, rf + 1, ff, closure);
865               for (s = -1; s <= 1; s += 2) {
866                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
867                       callback(board, flags,
868                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
869                                rf, ff, rf + 1, ff + s, closure);
870                   if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
871                       callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
872               }
873               break;
874
875             case BlackLance:
876               if(gameInfo.variant == VariantSuper) goto Amazon;
877               if (rf > 0 && WhitePiece(board[rf - 1][ff]))
878                   callback(board, flags,
879                            rf <= promoRank ? BlackPromotion : NormalMove,
880                            rf, ff, rf - 1, ff, closure);
881               for (s = -1; s <= 1; s += 2) {
882                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
883                       callback(board, flags,
884                                rf <= promoRank ? BlackPromotion : NormalMove,
885                                rf, ff, rf - 1, ff + s, closure);
886                   if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
887                       callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
888               }
889             break;
890
891             case SHOGI WhiteNothing:
892             case SHOGI BlackNothing:
893             case SHOGI WhiteLion:
894             case SHOGI BlackLion:
895             case WhiteLion:
896             case BlackLion:
897               for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
898                 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
899                 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
900                 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
901                          rf, ff, rt, ft, closure);
902               }
903               break;
904
905             case SHOGI WhiteFalcon:
906             case SHOGI BlackFalcon:
907             case SHOGI WhitePDagger:
908             case SHOGI BlackPDagger:
909                 SlideSideways(board, flags, rf, ff, callback, closure);
910                 StepVertical(board, flags, rf, ff, callback, closure);
911                 break;
912
913             case SHOGI WhiteCobra:
914             case SHOGI BlackCobra:
915                 StepVertical(board, flags, rf, ff, callback, closure);
916                 break;
917
918             case SHOGI (PROMOTED WhiteFerz):
919                 if(gameInfo.variant == VariantShogi) goto WhiteGold;
920             case SHOGI (PROMOTED BlackFerz):
921                 if(gameInfo.variant == VariantShogi) goto BlackGold;
922             case SHOGI WhitePSword:
923             case SHOGI BlackPSword:
924                 SlideVertical(board, flags, rf, ff, callback, closure);
925                 StepSideways(board, flags, rf, ff, callback, closure);
926                 break;
927
928             case SHOGI WhiteUnicorn:
929             case SHOGI BlackUnicorn:
930                 Ferz(board, flags, rf, ff, callback, closure);
931                 StepVertical(board, flags, rf, ff, callback, closure);
932                 break;
933
934             case SHOGI WhiteMan:
935                 StepDiagForward(board, flags, rf, ff, callback, closure);
936                 StepVertical(board, flags, rf, ff, callback, closure);
937                 break;
938
939             case SHOGI BlackMan:
940                 StepDiagBackward(board, flags, rf, ff, callback, closure);
941                 StepVertical(board, flags, rf, ff, callback, closure);
942                 break;
943
944             case SHOGI WhiteHCrown:
945             case SHOGI BlackHCrown:
946                 Bishop(board, flags, rf, ff, callback, closure);
947                 SlideSideways(board, flags, rf, ff, callback, closure);
948                 break;
949
950             case SHOGI WhiteCrown:
951             case SHOGI BlackCrown:
952                 Bishop(board, flags, rf, ff, callback, closure);
953                 SlideVertical(board, flags, rf, ff, callback, closure);
954                 break;
955
956             case SHOGI WhiteHorned:
957                 Sting(board, flags, rf, ff, 1, 0, callback, closure);
958                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
959                 if(killX >= 0) break;
960                 Bishop(board, flags, rf, ff, callback, closure);
961                 SlideSideways(board, flags, rf, ff, callback, closure);
962                 SlideBackward(board, flags, rf, ff, callback, closure);
963                 break;
964
965             case SHOGI BlackHorned:
966                 Sting(board, flags, rf, ff, -1, 0, callback, closure);
967                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
968                 if(killX >= 0) break;
969                 Bishop(board, flags, rf, ff, callback, closure);
970                 SlideSideways(board, flags, rf, ff, callback, closure);
971                 SlideForward(board, flags, rf, ff, callback, closure);
972                 break;
973
974             case SHOGI WhiteEagle:
975                 Sting(board, flags, rf, ff, 1,  1, callback, closure);
976                 Sting(board, flags, rf, ff, 1, -1, callback, closure);
977                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
978                 if(killX >= 0) break;
979                 Rook(board, flags, rf, ff, callback, closure);
980                 SlideDiagBackward(board, flags, rf, ff, callback, closure);
981                 break;
982
983             case SHOGI BlackEagle:
984                 Sting(board, flags, rf, ff, -1,  1, callback, closure);
985                 Sting(board, flags, rf, ff, -1, -1, callback, closure);
986                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
987                 if(killX >= 0) break;
988                 Rook(board, flags, rf, ff, callback, closure);
989                 SlideDiagForward(board, flags, rf, ff, callback, closure);
990                 break;
991
992             case SHOGI WhiteDolphin:
993             case SHOGI BlackHorse:
994                 SlideDiagBackward(board, flags, rf, ff, callback, closure);
995                 SlideVertical(board, flags, rf, ff, callback, closure);
996                 break;
997
998             case SHOGI BlackDolphin:
999             case SHOGI WhiteHorse:
1000                 SlideDiagForward(board, flags, rf, ff, callback, closure);
1001                 SlideVertical(board, flags, rf, ff, callback, closure);
1002                 break;
1003
1004             case SHOGI WhiteLance:
1005                 SlideForward(board, flags, rf, ff, callback, closure);
1006                 break;
1007
1008             case SHOGI BlackLance:
1009                 SlideBackward(board, flags, rf, ff, callback, closure);
1010                 break;
1011
1012             case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1013             case BlackFalcon:
1014             case WhiteCobra:
1015             case BlackCobra:
1016               callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1017               break;
1018
1019           }
1020       }
1021 }
1022
1023
1024 typedef struct {
1025     MoveCallback cb;
1026     VOIDSTAR cl;
1027 } GenLegalClosure;
1028
1029 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1030 Board xqCheckers, nullBoard;
1031
1032 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1033                                 int rf, int ff, int rt, int ft,
1034                                 VOIDSTAR closure));
1035
1036 void
1037 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1038 {
1039     register GenLegalClosure *cl = (GenLegalClosure *) closure;
1040
1041     if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1042
1043     if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1044
1045     if (!(flags & F_IGNORE_CHECK) ) {
1046       int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1047       if(promo) {
1048             int r, f, kings=0;
1049             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT;       f++)
1050                 kings += (board[r][f] == BlackKing);
1051             if(kings >= 2)
1052               promo = 0;
1053             else
1054                 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1055         }
1056         check = CheckTest(board, flags, rf, ff, rt, ft,
1057                   kind == WhiteCapturesEnPassant ||
1058                   kind == BlackCapturesEnPassant);
1059         if(promo) board[rf][ff] = BlackLance;
1060       if(check) return;
1061     }
1062     if (flags & F_ATOMIC_CAPTURE) {
1063       if (board[rt][ft] != EmptySquare ||
1064           kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1065         int r, f;
1066         ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1067         if (board[rf][ff] == king) return;
1068         for (r = rt-1; r <= rt+1; r++) {
1069           for (f = ft-1; f <= ft+1; f++) {
1070             if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1071                 board[r][f] == king) return;
1072           }
1073         }
1074       }
1075     }
1076     cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1077 }
1078
1079
1080 typedef struct {
1081     int rf, ff, rt, ft;
1082     ChessMove kind;
1083     int captures; // [HGM] losers
1084 } LegalityTestClosure;
1085
1086
1087 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1088    F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1089    king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1090    moves that would destroy your own king.  The CASTLE_OK flags are
1091    true if castling is not yet ruled out by a move of the king or
1092    rook.  Return TRUE if the player on move is currently in check and
1093    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */
1094 int
1095 GenLegal (Board board, int  flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1096 {
1097     GenLegalClosure cl;
1098     int ff, ft, k, left, right, swap;
1099     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1100     ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1101     int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1102
1103     cl.cb = callback;
1104     cl.cl = closure;
1105     xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1106     if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1107     GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1108
1109     if (inCheck) return TRUE;
1110
1111     /* Generate castling moves */
1112     if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1113         wKing = WhiteUnicorn; bKing = BlackUnicorn;
1114     }
1115
1116     for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1117         if ((flags & F_WHITE_ON_MOVE) &&
1118             (flags & F_WHITE_KCASTLE_OK) &&
1119             board[0][ff] == wKing &&
1120             board[0][ff + 1] == EmptySquare &&
1121             board[0][ff + 2] == EmptySquare &&
1122             board[0][BOARD_RGHT-3] == EmptySquare &&
1123             board[0][BOARD_RGHT-2] == EmptySquare &&
1124             board[0][BOARD_RGHT-1] == WhiteRook &&
1125             castlingRights[0] != NoRights && /* [HGM] check rights */
1126             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1127             (ignoreCheck ||
1128              (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1129               !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1130               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1131               !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1132
1133             callback(board, flags,
1134                      ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1135                      0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1136         }
1137         if ((flags & F_WHITE_ON_MOVE) &&
1138             (flags & F_WHITE_QCASTLE_OK) &&
1139             board[0][ff] == wKing &&
1140             board[0][ff - 1] == EmptySquare &&
1141             board[0][ff - 2] == EmptySquare &&
1142             board[0][BOARD_LEFT+2] == EmptySquare &&
1143             board[0][BOARD_LEFT+1] == EmptySquare &&
1144             board[0][BOARD_LEFT+0] == WhiteRook &&
1145             castlingRights[1] != NoRights && /* [HGM] check rights */
1146             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1147             (ignoreCheck ||
1148              (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1149               !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1150               !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1151
1152             callback(board, flags,
1153                      ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1154                      0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1155         }
1156         if (!(flags & F_WHITE_ON_MOVE) &&
1157             (flags & F_BLACK_KCASTLE_OK) &&
1158             board[BOARD_HEIGHT-1][ff] == bKing &&
1159             board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1160             board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1161             board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1162             board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1163             board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1164             castlingRights[3] != NoRights && /* [HGM] check rights */
1165             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1166             (ignoreCheck ||
1167              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1168               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1169               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1170               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1171
1172             callback(board, flags,
1173                      ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1174                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1175         }
1176         if (!(flags & F_WHITE_ON_MOVE) &&
1177             (flags & F_BLACK_QCASTLE_OK) &&
1178             board[BOARD_HEIGHT-1][ff] == bKing &&
1179             board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1180             board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1181             board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1182             board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1183             board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1184             castlingRights[4] != NoRights && /* [HGM] check rights */
1185             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1186             (ignoreCheck ||
1187              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1188               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1189               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1190
1191             callback(board, flags,
1192                      ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1193                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1194         }
1195     }
1196
1197   if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1198
1199     /* generate all potential FRC castling moves (KxR), ignoring flags */
1200     /* [HGM] test if the Rooks we find have castling rights */
1201     /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1202
1203
1204     if ((flags & F_WHITE_ON_MOVE) != 0) {
1205         ff = castlingRights[2]; /* King file if we have any rights */
1206         if(ff != NoRights && board[0][ff] == WhiteKing) {
1207     if (appData.debugMode) {
1208         fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1209                 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1210     }
1211             ft = castlingRights[0]; /* Rook file if we have H-side rights */
1212             left  = ff+1;
1213             right = BOARD_RGHT-2;
1214             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
1215             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1216                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1217             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1218                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1219             if(ft != NoRights && board[0][ft] == WhiteRook) {
1220                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1221                 if(swap)                        callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1222             }
1223
1224             ft = castlingRights[1]; /* Rook file if we have A-side rights */
1225             left  = BOARD_LEFT+2;
1226             right = ff-1;
1227             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1228             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1229                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1230             if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1231             if(ff > BOARD_LEFT+2)
1232             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1233                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1234             if(ft != NoRights && board[0][ft] == WhiteRook) {
1235                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1236                 if(swap)                        callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1237             }
1238         }
1239     } else {
1240         ff = castlingRights[5]; /* King file if we have any rights */
1241         if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1242             ft = castlingRights[3]; /* Rook file if we have H-side rights */
1243             left  = ff+1;
1244             right = BOARD_RGHT-2;
1245             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
1246             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1247                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1248             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1249                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1250             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1251                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1252                 if(swap)                        callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1253             }
1254
1255             ft = castlingRights[4]; /* Rook file if we have A-side rights */
1256             left  = BOARD_LEFT+2;
1257             right = ff-1;
1258             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1259             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1260                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1261             if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1262             if(ff > BOARD_LEFT+2)
1263             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1264                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1265             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1266                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1267                 if(swap)                        callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1268             }
1269         }
1270     }
1271
1272   }
1273
1274     return FALSE;
1275 }
1276
1277
1278 typedef struct {
1279     int rking, fking;
1280     int check;
1281 } CheckTestClosure;
1282
1283
1284 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1285                                  int rf, int ff, int rt, int ft,
1286                                  VOIDSTAR closure));
1287
1288
1289 void
1290 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1291 {
1292     register CheckTestClosure *cl = (CheckTestClosure *) closure;
1293
1294     if (rt == cl->rking && ft == cl->fking) {
1295         if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1296         cl->check++;
1297         xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1298     }
1299     if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1300         && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1301         cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1302 }
1303
1304
1305 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1306    he leave himself in check?  Or if rf == -1, is the player on move
1307    in check now?  enPassant must be TRUE if the indicated move is an
1308    e.p. capture.  The possibility of castling out of a check along the
1309    back rank is not accounted for (i.e., we still return nonzero), as
1310    this is illegal anyway.  Return value is the number of times the
1311    king is in check. */
1312 int
1313 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1314 {
1315     CheckTestClosure cl;
1316     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1317     ChessSquare captured = EmptySquare, ep=0, trampled=0;
1318     /*  Suppress warnings on uninitialized variables    */
1319
1320     if(gameInfo.variant == VariantXiangqi)
1321         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1322     if(gameInfo.variant == VariantKnightmate)
1323         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1324     if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check
1325         int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1326         for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1327             if(board[r][f] == k || board[r][f] == prince) {
1328                 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1329                 king = board[r][f]; // remember hich one we had
1330             }
1331         }
1332     }
1333
1334     if (rt >= 0) {
1335         if (enPassant) {
1336             captured = board[rf][ft];
1337             board[rf][ft] = EmptySquare;
1338         } else {
1339             captured = board[rt][ft];
1340             if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
1341         }
1342         if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1343             board[rt][ft] = board[rf][ff];
1344             board[rf][ff] = EmptySquare;
1345         }
1346         ep = board[EP_STATUS];
1347         if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1348             ChessSquare victim = killX < 0 ? EmptySquare : trampled;
1349             if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) &&           // capturer is Lion
1350                 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) &&           // captures from a distance
1351                 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
1352                      board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1353         }
1354     }
1355
1356     /* For compatibility with ICS wild 9, we scan the board in the
1357        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1358        and we test only whether that one is in check. */
1359     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1360         for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1361           if (board[cl.rking][cl.fking] == king) {
1362               cl.check = 0;
1363               if(gameInfo.variant == VariantXiangqi) {
1364                   /* [HGM] In Xiangqi opposing Kings means check as well */
1365                   int i, dir;
1366                   dir = (king >= BlackPawn) ? -1 : 1;
1367                   for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1368                                 board[i][cl.fking] == EmptySquare; i+=dir );
1369                   if(i>=0 && i<BOARD_HEIGHT &&
1370                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1371                           cl.check++;
1372               }
1373               GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1374               if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1375                  goto undo_move;  /* 2-level break */
1376           }
1377       }
1378
1379   undo_move:
1380
1381     if (rt >= 0) {
1382         if(rf != DROP_RANK) // [HGM] drop
1383             board[rf][ff] = board[rt][ft];
1384         if (enPassant) {
1385             board[rf][ft] = captured;
1386             board[rt][ft] = EmptySquare;
1387         } else {
1388             if(killX >= 0) board[killY][killX] = trampled;
1389             board[rt][ft] = captured;
1390         }
1391         board[EP_STATUS] = ep;
1392     }
1393
1394     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1395 }
1396
1397 int
1398 HasLion (Board board, int flags)
1399 {
1400     int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1401     int r, f;
1402     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1403         if(board[r][f] == lion) return 1;
1404     return 0;
1405 }
1406
1407 ChessMove
1408 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1409 {   // [HGM] put drop legality testing in separate routine for clarity
1410     int n;
1411 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1412     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1413     n = PieceToNumber(piece);
1414     if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1415         && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1416         return ImpossibleMove; // piece not available
1417     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1418         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1419            (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1420             piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1421             piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1422         if(piece == WhitePawn || piece == BlackPawn) {
1423             int r;
1424             for(r=1; r<BOARD_HEIGHT-1; r++)
1425                 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
1426             // should still test if we mate with this Pawn
1427         }
1428     } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1429         if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1430     } else {
1431         if( (piece == WhitePawn || piece == BlackPawn) &&
1432             (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1433             return IllegalMove; /* no pawn drops on 1st/8th */
1434     }
1435 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1436     if (!(flags & F_IGNORE_CHECK) &&
1437         CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1438     return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1439 }
1440
1441 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1442                                     int rf, int ff, int rt, int ft,
1443                                     VOIDSTAR closure));
1444
1445 void
1446 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1447 {
1448     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1449
1450     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1451         cl->captures++; // [HGM] losers: count legal captures
1452     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1453       cl->kind = kind;
1454 }
1455
1456 ChessMove
1457 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1458 {
1459     LegalityTestClosure cl; ChessSquare piece, filterPiece;
1460
1461     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1462     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1463     piece = filterPiece = board[rf][ff];
1464     if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1465
1466     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1467     /* (perhaps we should disallow moves that obviously leave us in check?)              */
1468     if((piece == WhiteFalcon || piece == BlackFalcon ||
1469         piece == WhiteCobra  || piece == BlackCobra) && gameInfo.variant != VariantChu)
1470         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1471
1472     cl.rf = rf;
1473     cl.ff = ff;
1474     cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1475     cl.ft = fFilter = ft;
1476     cl.kind = IllegalMove;
1477     cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1478     if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1479     GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1480     if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1481                 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1482         return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1483
1484     if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1485     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1486         if(board[rf][ff] < BlackPawn) { // white
1487             if(rf != 0) return IllegalMove; // must be on back rank
1488             if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1489             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1490             if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1491             if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1492         } else {
1493             if(rf != BOARD_HEIGHT-1) return IllegalMove;
1494             if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1495             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1496             if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1497             if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1498         }
1499     } else
1500     if(gameInfo.variant == VariantChu) {
1501         if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1502         if(promoChar != '+')
1503             return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1504         if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
1505         return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1506     } else
1507     if(gameInfo.variant == VariantShogi) {
1508         /* [HGM] Shogi promotions. '=' means defer */
1509         if(rf != DROP_RANK && cl.kind == NormalMove) {
1510             ChessSquare piece = board[rf][ff];
1511
1512             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1513             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1514                promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1515                promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1516                   promoChar = '+'; // allowed ICS notations
1517 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1518             if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1519                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1520             else if(flags & F_WHITE_ON_MOVE) {
1521                 if( (int) piece < (int) WhiteWazir &&
1522                      (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1523                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1524                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1525                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1526                     else /* promotion optional, default is defer */
1527                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1528                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1529             } else {
1530                 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1531                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1532                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1533                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1534                     else /* promotion optional, default is defer */
1535                        cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1536                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1537             }
1538         }
1539     } else
1540     if (promoChar != NULLCHAR) {
1541         if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1542             ChessSquare piece = board[rf][ff];
1543             if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1544             // should test if in zone, really
1545             if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1546                 return IllegalMove;
1547             if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1548         } else
1549         if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1550         if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1551             ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1552             if(piece == EmptySquare)
1553                 cl.kind = ImpossibleMove; // non-existing piece
1554             if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1555                 cl.kind = IllegalMove; // no two Lions
1556             } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1557                 if(promoChar != PieceToChar(BlackKing)) {
1558                     if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1559                     if(piece == BlackLance) cl.kind = ImpossibleMove;
1560                 } else { // promotion to King allowed only if we do not have two yet
1561                     int r, f, kings = 0;
1562                     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1563                     if(kings == 2) cl.kind = IllegalMove;
1564                 }
1565             } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1566                           piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1567             else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1568              cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1569             else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1570              cl.kind = IllegalMove; // promotion to King usually not allowed
1571         } else {
1572             cl.kind = IllegalMove;
1573         }
1574     }
1575     return cl.kind;
1576 }
1577
1578 typedef struct {
1579     int count;
1580 } MateTestClosure;
1581
1582 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1583                                 int rf, int ff, int rt, int ft,
1584                                 VOIDSTAR closure));
1585
1586 void
1587 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1588 {
1589     register MateTestClosure *cl = (MateTestClosure *) closure;
1590
1591     cl->count++;
1592 }
1593
1594 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1595 int
1596 MateTest (Board board, int flags)
1597 {
1598     MateTestClosure cl;
1599     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1600     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1601
1602     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1603         // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1604         nrKing += (board[r][f] == king);   // stm has king
1605         if( board[r][f] != EmptySquare ) {
1606             if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1607                  myPieces++;
1608             else hisPieces++;
1609         }
1610     }
1611     switch(gameInfo.variant) { // [HGM] losers: extinction wins
1612         case VariantShatranj:
1613                 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1614         default:
1615                 break;
1616         case VariantAtomic:
1617                 if(nrKing == 0) return MT_NOKING;
1618                 break;
1619         case VariantLosers:
1620                 if(myPieces == 1) return MT_BARE;
1621     }
1622     cl.count = 0;
1623     inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1624     // [HGM] 3check: yet to do!
1625     if (cl.count > 0) {
1626         return inCheck ? MT_CHECK : MT_NONE;
1627     } else {
1628         if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1629                                  && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1630             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1631             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1632                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1633                     if(board[n][holdings] != EmptySquare) {
1634                         int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1635                         if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1636                     }
1637         }
1638         if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1639                 return myPieces == hisPieces ? MT_STALEMATE :
1640                                         myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1641         else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1642         else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1643
1644         return inCheck ? MT_CHECKMATE
1645                        : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
1646                           MT_STAINMATE : MT_STALEMATE;
1647     }
1648 }
1649
1650
1651 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1652                                     int rf, int ff, int rt, int ft,
1653                                     VOIDSTAR closure));
1654
1655 void
1656 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1657 {
1658     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1659     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1660
1661     // [HGM] wild: for wild-card pieces rt and rf are dummies
1662     if(piece == WhiteFalcon || piece == BlackFalcon ||
1663        piece == WhiteCobra  || piece == BlackCobra)
1664         wildCard = TRUE;
1665
1666     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1667          || PieceToChar(board[rf][ff]) == '~'
1668               && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1669                                                                       ) &&
1670         (cl->rfIn == -1 || cl->rfIn == rf) &&
1671         (cl->ffIn == -1 || cl->ffIn == ff) &&
1672         (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1673         (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1674
1675         cl->count++;
1676         if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1677           // [HGM] oneclick: if multiple moves, be sure we remember capture
1678           cl->piece = board[rf][ff];
1679           cl->rf = rf;
1680           cl->ff = ff;
1681           cl->rt = wildCard ? cl->rtIn : rt;
1682           cl->ft = wildCard ? cl->ftIn : ft;
1683           cl->kind = kind;
1684         }
1685         cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1686     }
1687 }
1688
1689 void
1690 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
1691 {
1692     int illegal = 0; char c = closure->promoCharIn;
1693
1694     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1695     closure->count = closure->captures = 0;
1696     closure->rf = closure->ff = closure->rt = closure->ft = 0;
1697     closure->kind = ImpossibleMove;
1698     rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
1699     fFilter = closure->ftIn;
1700     if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
1701         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1702         if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
1703             closure->count = closure->captures = 0;
1704             closure->rf = closure->ff = closure->rt = closure->ft = 0;
1705             closure->kind = ImpossibleMove;
1706             GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1707         }
1708     } else
1709     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1710     if (closure->count == 0) {
1711         /* See if it's an illegal move due to check */
1712         illegal = 1;
1713         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1714         if (closure->count == 0) {
1715             /* No, it's not even that */
1716           if(!appData.testLegality && closure->pieceIn != EmptySquare) {
1717             int f, r; // if there is only a single piece of the requested type on the board, use that
1718             closure->rt = closure->rtIn, closure->ft = closure->ftIn;
1719             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1720                 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
1721             if(closure->count > 1) illegal = 0; // ambiguous
1722           }
1723           if(closure->count == 0) {
1724             if (appData.debugMode) { int i, j;
1725                 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1726                     for(j=0; j<BOARD_WIDTH; j++)
1727                         fprintf(debugFP, "%3d", (int) board[i][j]);
1728                     fprintf(debugFP, "\n");
1729                 }
1730             }
1731             return;
1732           }
1733         }
1734     }
1735
1736     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1737     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1738         if(closure->piece < BlackPawn) { // white
1739             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1740             if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
1741             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1742             if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1743             if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1744         } else {
1745             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1746             if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
1747             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1748             if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1749             if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1750         }
1751     } else
1752     if(gameInfo.variant == VariantChu) {
1753         if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
1754     } else
1755     if(gameInfo.variant == VariantShogi) {
1756         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1757         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1758             ChessSquare piece = closure->piece;
1759             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1760                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1761                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1762                    c = '+'; // allowed ICS notations
1763             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1764             else if(flags & F_WHITE_ON_MOVE) {
1765                 if( (int) piece < (int) WhiteWazir &&
1766                      (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1767                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1768                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1769                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1770                     else /* promotion optional, default is defer */
1771                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1772                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1773             } else {
1774                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1775                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1776                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1777                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1778                     else /* promotion optional, default is defer */
1779                        closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1780                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1781             }
1782         }
1783         if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1784         if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1785     } else
1786     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1787         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1788             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1789                gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
1790                 c = PieceToChar(BlackFerz);
1791             else if(gameInfo.variant == VariantGreat)
1792                 c = PieceToChar(BlackMan);
1793             else if(gameInfo.variant == VariantGrand)
1794                     closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
1795             else
1796                 c = PieceToChar(BlackQueen);
1797         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1798         else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1799     } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
1800         ChessSquare p = closure->piece;
1801         if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
1802             closure->kind = ImpossibleMove; // used on non-promotable piece
1803         else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1804     } else if (c != NULLCHAR) closure->kind = IllegalMove;
1805
1806     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1807     if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1808         closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1809     if (closure->count > 1) {
1810         closure->kind = AmbiguousMove;
1811     }
1812     if (illegal) {
1813         /* Note: If more than one illegal move matches, but no legal
1814            moves, we return IllegalMove, not AmbiguousMove.  Caller
1815            can look at closure->count to detect this.
1816         */
1817         closure->kind = IllegalMove;
1818     }
1819 }
1820
1821
1822 typedef struct {
1823     /* Input */
1824     ChessSquare piece;
1825     int rf, ff, rt, ft;
1826     /* Output */
1827     ChessMove kind;
1828     int rank;
1829     int file;
1830     int either;
1831 } CoordsToAlgebraicClosure;
1832
1833 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1834                                          ChessMove kind, int rf, int ff,
1835                                          int rt, int ft, VOIDSTAR closure));
1836
1837 void
1838 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1839 {
1840     register CoordsToAlgebraicClosure *cl =
1841       (CoordsToAlgebraicClosure *) closure;
1842
1843     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1844         (board[rf][ff] == cl->piece
1845          || PieceToChar(board[rf][ff]) == '~' &&
1846             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1847                                      ) {
1848         if (rf == cl->rf) {
1849             if (ff == cl->ff) {
1850                 cl->kind = kind; /* this is the move we want */
1851             } else {
1852                 cl->file++; /* need file to rule out this move */
1853             }
1854         } else {
1855             if (ff == cl->ff) {
1856                 cl->rank++; /* need rank to rule out this move */
1857             } else {
1858                 cl->either++; /* rank or file will rule out this move */
1859             }
1860         }
1861     }
1862 }
1863
1864 /* Convert coordinates to normal algebraic notation.
1865    promoChar must be NULLCHAR or 'x' if not a promotion.
1866 */
1867 ChessMove
1868 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
1869 {
1870     ChessSquare piece;
1871     ChessMove kind;
1872     char *outp = out, c;
1873     CoordsToAlgebraicClosure cl;
1874
1875     if (rf == DROP_RANK) {
1876         if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
1877         /* Bughouse piece drop */
1878         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1879         *outp++ = '@';
1880         *outp++ = ft + AAA;
1881         if(rt+ONE <= '9')
1882            *outp++ = rt + ONE;
1883         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1884         *outp = NULLCHAR;
1885         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1886     }
1887
1888     if (promoChar == 'x') promoChar = NULLCHAR;
1889     piece = board[rf][ff];
1890     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1891
1892     switch (piece) {
1893       case WhitePawn:
1894       case BlackPawn:
1895         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1896         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1897             /* Keep short notation if move is illegal only because it
1898                leaves the player in check, but still return IllegalMove */
1899             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1900             if (kind == IllegalMove) break;
1901             kind = IllegalMove;
1902         }
1903         /* Pawn move */
1904         *outp++ = ff + AAA;
1905         if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1906             /* Non-capture; use style "e5" */
1907             if(rt+ONE <= '9')
1908                *outp++ = rt + ONE;
1909             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1910         } else {
1911             /* Capture; use style "exd5" */
1912             if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1913             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
1914             *outp++ = ft + AAA;
1915             if(rt+ONE <= '9')
1916                *outp++ = rt + ONE;
1917             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1918         }
1919         /* Use promotion suffix style "=Q" */
1920         *outp = NULLCHAR;
1921         if (promoChar != NULLCHAR) {
1922             if(IS_SHOGI(gameInfo.variant)) {
1923                 /* [HGM] ... but not in Shogi! */
1924                 *outp++ = promoChar == '=' ? '=' : '+';
1925             } else {
1926                 *outp++ = '=';
1927                 *outp++ = ToUpper(promoChar);
1928             }
1929             *outp = NULLCHAR;
1930         }
1931         return kind;
1932
1933
1934       case WhiteKing:
1935       case BlackKing:
1936         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1937         /* Code added by Tord:  FRC castling. */
1938         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1939            (piece == BlackKing && board[rt][ft] == BlackRook)) {
1940           if(ft > ff)
1941             safeStrCpy(out, "O-O", MOVE_LEN);
1942           else
1943             safeStrCpy(out, "O-O-O", MOVE_LEN);
1944           return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1945         }
1946         /* End of code added by Tord */
1947         /* Test for castling or ICS wild castling */
1948         /* Use style "O-O" (oh-oh) for PGN compatibility */
1949         else if (rf == rt &&
1950             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1951             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
1952             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1953              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1954             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1955               snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1956             else
1957               snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1958
1959             /* This notation is always unambiguous, unless there are
1960                kings on both the d and e files, with "wild castling"
1961                possible for the king on the d file and normal castling
1962                possible for the other.  ICS rules for wild 9
1963                effectively make castling illegal for either king in
1964                this situation.  So I am not going to worry about it;
1965                I'll just generate an ambiguous O-O in this case.
1966             */
1967             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1968         }
1969
1970         /* else fall through */
1971       default:
1972         /* Piece move */
1973         cl.rf = rf;
1974         cl.ff = ff;
1975         cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1976         cl.ft = fFilter = ft;
1977         cl.piece = piece;
1978         cl.kind = IllegalMove;
1979         cl.rank = cl.file = cl.either = 0;
1980         c = PieceToChar(piece) ;
1981         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
1982
1983         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1984             /* Generate pretty moves for moving into check, but
1985                still return IllegalMove.
1986             */
1987             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
1988             if (cl.kind == IllegalMove) break;
1989             cl.kind = IllegalMove;
1990         }
1991
1992         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1993            else "Ngf3" or "Ngxf7",
1994            else "N1f3" or "N5xf7",
1995            else "Ng1f3" or "Ng5xf7".
1996         */
1997         if( c == '~' || c == '+') {
1998            /* [HGM] print nonexistent piece as its demoted version */
1999            piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
2000         }
2001         if(c=='+') *outp++ = c;
2002         *outp++ = ToUpper(PieceToChar(piece));
2003
2004         if (cl.file || (cl.either && !cl.rank)) {
2005             *outp++ = ff + AAA;
2006         }
2007         if (cl.rank) {
2008             if(rf+ONE <= '9')
2009                 *outp++ = rf + ONE;
2010             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2011         }
2012
2013         if(board[rt][ft] != EmptySquare)
2014           *outp++ = 'x';
2015
2016         *outp++ = ft + AAA;
2017         if(rt+ONE <= '9')
2018            *outp++ = rt + ONE;
2019         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2020         if (IS_SHOGI(gameInfo.variant)) {
2021             /* [HGM] in Shogi non-pawns can promote */
2022             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2023         }
2024         else if (gameInfo.variant == VariantChuChess && promoChar ||
2025                  gameInfo.variant != VariantSuper && promoChar &&
2026                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2027             *outp++ = '=';
2028             *outp++ = ToUpper(promoChar);
2029         }
2030         else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2031             *outp++ = '/';
2032             *outp++ = ToUpper(promoChar);
2033         }
2034         *outp = NULLCHAR;
2035         return cl.kind;
2036
2037       case EmptySquare:
2038         /* Moving a nonexistent piece */
2039         break;
2040     }
2041
2042     /* Not a legal move, even ignoring check.
2043        If there was a piece on the from square,
2044        use style "Ng1g3" or "Ng1xe8";
2045        if there was a pawn or nothing (!),
2046        use style "g1g3" or "g1xe8".  Use "x"
2047        if a piece was on the to square, even
2048        a piece of the same color.
2049     */
2050     outp = out;
2051     c = 0;
2052     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2053         int r, f;
2054       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2055                 c += (board[r][f] == piece); // count on-board pieces of given type
2056         *outp++ = ToUpper(PieceToChar(piece));
2057     }
2058   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2059     *outp++ = ff + AAA;
2060     if(rf+ONE <= '9')
2061        *outp++ = rf + ONE;
2062     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2063   }
2064     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2065     *outp++ = ft + AAA;
2066     if(rt+ONE <= '9')
2067        *outp++ = rt + ONE;
2068     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2069     /* Use promotion suffix style "=Q" */
2070     if (promoChar != NULLCHAR && promoChar != 'x') {
2071         *outp++ = '=';
2072         *outp++ = ToUpper(promoChar);
2073     }
2074     *outp = NULLCHAR;
2075
2076     return IllegalMove;
2077 }
2078
2079 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2080
2081 typedef struct {
2082     /* Input */
2083     int rf, ff, rt, ft;
2084     /* Output */
2085     int recaptures;
2086 } ChaseClosure;
2087
2088 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2089
2090 int preyStackPointer, chaseStackPointer;
2091
2092 struct {
2093 unsigned char rf, ff, rt, ft;
2094 } chaseStack[100];
2095
2096 struct {
2097 unsigned char rank, file;
2098 } preyStack[100];
2099
2100
2101
2102
2103 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2104
2105 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2106                                 int rf, int ff, int rt, int ft,
2107                                 VOIDSTAR closure));
2108
2109 void
2110 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2111 {   // For adding captures that can lead to chase indictment to the chaseStack
2112     if(board[rt][ft] == EmptySquare) return;                               // non-capture
2113     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
2114     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
2115     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
2116     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2117     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2118     chaseStack[chaseStackPointer].rf = rf;
2119     chaseStack[chaseStackPointer].ff = ff;
2120     chaseStack[chaseStackPointer].rt = rt;
2121     chaseStack[chaseStackPointer].ft = ft;
2122     chaseStackPointer++;
2123 }
2124
2125 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2126                                 int rf, int ff, int rt, int ft,
2127                                 VOIDSTAR closure));
2128
2129 void
2130 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2131 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2132     int i;
2133     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2134
2135     if(board[rt][ft] == EmptySquare) return; // no capture
2136     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2137         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
2138     }
2139     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2140     for(i=0; i<chaseStackPointer; i++) {
2141         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2142            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
2143             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2144             chaseStack[i] = chaseStack[--chaseStackPointer];
2145             break;
2146         }
2147     }
2148 }
2149
2150 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2151                                 int rf, int ff, int rt, int ft,
2152                                 VOIDSTAR closure));
2153
2154 void
2155 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2156 {   // for determining if a piece (given through the closure) is protected
2157     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2158
2159     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
2160     if(appData.debugMode && board[rt][ft] != EmptySquare)
2161         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2162 }
2163
2164 extern char moveList[MAX_MOVES][MOVE_LEN];
2165
2166 int
2167 PerpetualChase (int first, int last)
2168 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2169     int i, j, k, tail;
2170     ChaseClosure cl;
2171     ChessSquare captured;
2172
2173     preyStackPointer = 0;        // clear stack of chased pieces
2174     for(i=first; i<last; i+=2) { // for all positions with same side to move
2175         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2176         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
2177         // determine all captures possible after the move, and put them on chaseStack
2178         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2179         if(appData.debugMode) { int n;
2180             for(n=0; n<chaseStackPointer; n++)
2181                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2182                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2183             fprintf(debugFP, ": all capts\n");
2184         }
2185         // determine all captures possible before the move, and delete them from chaseStack
2186         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2187         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2188         cl.rt = moveList[i][3]-ONE;
2189         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2190         CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2191         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2192         xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2193         if(appData.debugMode) { int n;
2194             for(n=0; n<chaseStackPointer; n++)
2195                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2196                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2197             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2198         }
2199         // chaseSack now contains all captures made possible by the move
2200         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2201             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2202             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2203
2204             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2205             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
2206
2207             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2208                 continue; // C or H attack on R is always chase; leave on chaseStack
2209
2210             if(attacker == victim) {
2211                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2212                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2213                         // we can capture back with equal piece, so this is no chase but a sacrifice
2214                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2215                         j--; /* ! */ continue;
2216                 }
2217
2218             }
2219
2220             // the attack is on a lower piece, or on a pinned or blocked equal one
2221             CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2222             CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2223             // test if the victim is protected by a true protector. First make the capture.
2224             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2225             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2226             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2227             // Then test if the opponent can recapture
2228             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
2229             cl.rt = chaseStack[j].rt;
2230             cl.ft = chaseStack[j].ft;
2231             if(appData.debugMode) {
2232                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2233             }
2234             xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2235             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2236             xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2237             // unmake the capture
2238             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2239             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2240             // if a recapture was found, piece is protected, and we are not chasing it.
2241             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2242                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2243                 j--; /* ! */
2244             }
2245         }
2246         // chaseStack now contains all moves that chased
2247         if(appData.debugMode) { int n;
2248             for(n=0; n<chaseStackPointer; n++)
2249                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2250                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2251             fprintf(debugFP, ": chases\n");
2252         }
2253         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2254             for(j=0; j<chaseStackPointer; j++) {
2255                 preyStack[j].rank = chaseStack[j].rt;
2256                 preyStack[j].file = chaseStack[j].ft;
2257             }
2258             preyStackPointer = chaseStackPointer;
2259         }
2260         tail = 0;
2261         for(j=0; j<chaseStackPointer; j++) {
2262             for(k=0; k<preyStackPointer; k++) {
2263                 // search the victim of each chase move on the preyStack (first occurrence)
2264                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2265                     if(k < tail) break; // piece was already identified as still being chased
2266                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2267                     preyStack[tail] = preyStack[k];                // by swapping
2268                     preyStack[k] = preyStack[preyStackPointer];
2269                     tail++;
2270                     break;
2271                 }
2272             }
2273         }
2274         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2275         if(appData.debugMode) { int n;
2276             for(n=0; n<preyStackPointer; n++)
2277                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2278             fprintf(debugFP, "always chased upto ply %d\n", i);
2279         }
2280         // now adjust the location of the chased pieces according to opponent move
2281         for(j=0; j<preyStackPointer; j++) {
2282             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2283                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2284                 preyStack[j].rank = moveList[i+1][3]-ONE;
2285                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2286                 break;
2287             }
2288         }
2289     }
2290     return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2291                                 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the
2292 }