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