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