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