Fix multi-leg promotions
[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 || gameInfo.variant == VariantSpartan) 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) continue;
1115                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
1116                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1117                   }
1118                 Rook(board, flags, rf, ff, callback, closure);
1119               break;
1120
1121             /* Shogi Dragon King has to continue as Ferz after Rook moves */
1122             case SHOGI WhiteDragon:
1123             case SHOGI BlackDragon:
1124             case SHOGI WhitePDragon:
1125             case SHOGI BlackPDragon:
1126             DragonKing:
1127                 Rook(board, flags, rf, ff, callback, closure);
1128                 Ferz(board, flags, rf, ff, callback, closure);
1129                 break;
1130               m++;
1131
1132             /* Capablanca Chancellor sets flag to continue as Knight      */
1133             case WhiteMarshall:
1134             case BlackMarshall:
1135                 Rook(board, flags, rf, ff, callback, closure);
1136                 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
1137                     Ferz(board, flags, rf, ff, callback, closure);
1138                 else
1139                     Knight(board, flags, rf, ff, callback, closure);
1140                 break;
1141
1142             case WhiteTower:
1143             case BlackTower:
1144                 for (d = 0; d <= 1; d++) // Dababba moves
1145                   for (s = -2; s <= 2; s += 4) {
1146                       rt = rf + s * d;
1147                       ft = ff + s * (1 - d);
1148                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1149                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
1150                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1151                   }
1152                 Wazir(board, flags, rf, ff, callback, closure);
1153                 break;
1154
1155             /* Shogi Rooks are ordinary Rooks */
1156             case SHOGI WhiteRook:
1157             case SHOGI BlackRook:
1158             case SHOGI WhitePRook:
1159             case SHOGI BlackPRook:
1160             case WhiteRook:
1161             case BlackRook:
1162                 Rook(board, flags, rf, ff, callback, closure);
1163                 break;
1164
1165             case WhiteQueen:
1166             case BlackQueen:
1167             case SHOGI WhiteMother:
1168             case SHOGI BlackMother:
1169             doQueen:
1170                 Rook(board, flags, rf, ff, callback, closure);
1171                 Bishop(board, flags, rf, ff, callback, closure);
1172                 break;
1173
1174            case SHOGI WhitePawn:
1175                 StepForward(board, flags, rf, ff, callback, closure);
1176                 break;
1177
1178             case SHOGI BlackPawn:
1179                 StepBackward(board, flags, rf, ff, callback, closure);
1180                 break;
1181
1182             case WhiteMan:
1183                 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1184             case SHOGI WhiteFerz:
1185                 Ferz(board, flags, rf, ff, callback, closure);
1186                 StepForward(board, flags, rf, ff, callback, closure);
1187                 break;
1188
1189             case BlackMan:
1190                 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1191             case SHOGI BlackFerz:
1192                 StepBackward(board, flags, rf, ff, callback, closure);
1193
1194             case WhiteFerz:
1195             case BlackFerz:
1196                 if(gameInfo.variant == VariantXiangqi && ff != BOARD_WIDTH>>1) {
1197                     int rt = (piece == BlackFerz ? BOARD_HEIGHT-2 : 1);
1198                     int ft = BOARD_WIDTH>>1;
1199                     if(!SameColor(board[rf][ff], board[rt][ft]))
1200                         callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1201                 } else
1202                 /* [HGM] support Shatranj pieces */
1203                 Ferz(board, flags, rf, ff, callback, closure);
1204                 break;
1205
1206             case WhiteSilver:
1207             case BlackSilver:
1208                 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
1209
1210             commoner:
1211             case SHOGI WhiteMonarch:
1212             case SHOGI BlackMonarch:
1213             case SHOGI WhiteKing:
1214             case SHOGI BlackKing:
1215             case WhiteKing:
1216             case BlackKing:
1217                 Ferz(board, flags, rf, ff, callback, closure);
1218                 Wazir(board, flags, rf, ff, callback, closure);
1219                 break;
1220
1221             case WhiteNightrider:
1222             case BlackNightrider:
1223               for (i = -1; i <= 1; i += 2)
1224                 for (j = -1; j <= 1; j += 2)
1225                   for (s = 1; s <= 2; s++) {  int k;
1226                     for(k=1;; k++) {
1227                       rt = rf + k*i*s;
1228                       ft = ff + k*j*(3-s);
1229                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
1230                       if (SameColor(board[rf][ff], board[rt][ft])) break;
1231                       callback(board, flags, NormalMove,
1232                                rf, ff, rt, ft, closure);
1233                       if (board[rt][ft] != EmptySquare) break;
1234                     }
1235                   }
1236               break;
1237
1238             Amazon:
1239                 Bishop(board, flags, rf, ff, callback, closure);
1240                 Rook(board, flags, rf, ff, callback, closure);
1241                 Knight(board, flags, rf, ff, callback, closure);
1242                 break;
1243
1244             // Use Lance as Berolina / Spartan Pawn.
1245             case WhiteLance:
1246               if(gameInfo.variant == VariantSuper) goto Amazon;
1247               if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1248                   callback(board, flags,
1249                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1250                            rf, ff, rf + 1, ff, closure);
1251               for (s = -1; s <= 1; s += 2) {
1252                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1253                       callback(board, flags,
1254                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1255                                rf, ff, rf + 1, ff + s, closure);
1256                   if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1257                       callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1258               }
1259               break;
1260
1261             case BlackLance:
1262               if(gameInfo.variant == VariantSuper) goto Amazon;
1263               if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1264                   callback(board, flags,
1265                            rf <= promoRank ? BlackPromotion : NormalMove,
1266                            rf, ff, rf - 1, ff, closure);
1267               for (s = -1; s <= 1; s += 2) {
1268                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1269                       callback(board, flags,
1270                                rf <= promoRank ? BlackPromotion : NormalMove,
1271                                rf, ff, rf - 1, ff + s, closure);
1272                   if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1273                       callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1274               }
1275             break;
1276
1277             case SHOGI WhiteNothing:
1278             case SHOGI BlackNothing:
1279             case SHOGI WhiteLion:
1280             case SHOGI BlackLion:
1281             case WhiteLion:
1282             case BlackLion:
1283               for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1284                 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1285                 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1286                 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
1287                 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare && !i ? FirstLeg : NormalMove,
1288                          rf, ff, rt, ft, closure);
1289                 legNr -= 2*i;
1290               }
1291               break;
1292
1293             case SHOGI WhiteDagger:
1294             case SHOGI BlackDagger:
1295             case SHOGI WhitePDagger:
1296             case SHOGI BlackPDagger:
1297                 SlideSideways(board, flags, rf, ff, callback, closure);
1298                 StepVertical(board, flags, rf, ff, callback, closure);
1299                 break;
1300
1301             case SHOGI WhiteCobra:
1302             case SHOGI BlackCobra:
1303                 StepVertical(board, flags, rf, ff, callback, closure);
1304                 break;
1305
1306             case SHOGI (PROMO WhiteFerz):
1307                 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1308             case SHOGI (PROMO BlackFerz):
1309                 if(gameInfo.variant == VariantShogi) goto BlackGold;
1310             case SHOGI WhiteSword:
1311             case SHOGI BlackSword:
1312             case SHOGI WhitePSword:
1313             case SHOGI BlackPSword:
1314                 SlideVertical(board, flags, rf, ff, callback, closure);
1315                 StepSideways(board, flags, rf, ff, callback, closure);
1316                 break;
1317
1318             case SHOGI WhiteCat:
1319             case SHOGI BlackCat:
1320                 Ferz(board, flags, rf, ff, callback, closure);
1321                 StepVertical(board, flags, rf, ff, callback, closure);
1322                 break;
1323
1324             case SHOGI WhiteCopper:
1325                 StepDiagForward(board, flags, rf, ff, callback, closure);
1326                 StepVertical(board, flags, rf, ff, callback, closure);
1327                 break;
1328
1329             case SHOGI BlackCopper:
1330                 StepDiagBackward(board, flags, rf, ff, callback, closure);
1331                 StepVertical(board, flags, rf, ff, callback, closure);
1332                 break;
1333
1334             case SHOGI WhiteHCrown:
1335             case SHOGI BlackHCrown:
1336                 Bishop(board, flags, rf, ff, callback, closure);
1337                 SlideSideways(board, flags, rf, ff, callback, closure);
1338                 break;
1339
1340             case SHOGI WhiteCrown:
1341             case SHOGI BlackCrown:
1342                 Bishop(board, flags, rf, ff, callback, closure);
1343                 SlideVertical(board, flags, rf, ff, callback, closure);
1344                 break;
1345
1346             case SHOGI WhiteUnicorn:
1347                 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1348                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1349                 if(killX >= 0) break;
1350                 Bishop(board, flags, rf, ff, callback, closure);
1351                 SlideSideways(board, flags, rf, ff, callback, closure);
1352                 SlideBackward(board, flags, rf, ff, callback, closure);
1353                 break;
1354
1355             case SHOGI BlackUnicorn:
1356                 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1357                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1358                 if(killX >= 0) break;
1359                 Bishop(board, flags, rf, ff, callback, closure);
1360                 SlideSideways(board, flags, rf, ff, callback, closure);
1361                 SlideForward(board, flags, rf, ff, callback, closure);
1362                 break;
1363
1364             case SHOGI WhiteFalcon:
1365                 Sting(board, flags, rf, ff, 1,  1, callback, closure);
1366                 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1367                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1368                 if(killX >= 0) break;
1369                 Rook(board, flags, rf, ff, callback, closure);
1370                 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1371                 break;
1372
1373             case SHOGI BlackFalcon:
1374                 Sting(board, flags, rf, ff, -1,  1, callback, closure);
1375                 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1376                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1377                 if(killX >= 0) break;
1378                 Rook(board, flags, rf, ff, callback, closure);
1379                 SlideDiagForward(board, flags, rf, ff, callback, closure);
1380                 break;
1381
1382             case SHOGI WhiteDolphin:
1383             case SHOGI BlackHorse:
1384                 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1385                 SlideVertical(board, flags, rf, ff, callback, closure);
1386                 break;
1387
1388             case SHOGI BlackDolphin:
1389             case SHOGI WhiteHorse:
1390                 SlideDiagForward(board, flags, rf, ff, callback, closure);
1391                 SlideVertical(board, flags, rf, ff, callback, closure);
1392                 break;
1393
1394             case SHOGI WhiteLance:
1395                 SlideForward(board, flags, rf, ff, callback, closure);
1396                 break;
1397
1398             case SHOGI BlackLance:
1399                 SlideBackward(board, flags, rf, ff, callback, closure);
1400                 break;
1401
1402             case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1403             case BlackFalcon:
1404             case WhiteCobra:
1405             case BlackCobra:
1406               callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1407               break;
1408
1409           }
1410       }
1411 }
1412
1413
1414 typedef struct {
1415     MoveCallback cb;
1416     VOIDSTAR cl;
1417 } GenLegalClosure;
1418
1419 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1420 Board xqCheckers, nullBoard;
1421
1422 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1423                                 int rf, int ff, int rt, int ft,
1424                                 VOIDSTAR closure));
1425
1426 void
1427 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1428 {
1429     register GenLegalClosure *cl = (GenLegalClosure *) closure;
1430
1431     if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1432
1433     if ((int)board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1434
1435     if (!(flags & F_IGNORE_CHECK) ) {
1436       int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1437       if(promo) {
1438             int r, f, kings=0;
1439             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT;       f++)
1440                 kings += (board[r][f] == BlackKing);
1441             if(kings >= 2)
1442               promo = 0;
1443             else
1444                 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1445         }
1446         check = CheckTest(board, flags, rf, ff, rt, ft,
1447                   kind == WhiteCapturesEnPassant ||
1448                   kind == BlackCapturesEnPassant);
1449         if(promo) board[rf][ff] = BlackLance;
1450       if(check) return;
1451     }
1452     if (flags & F_ATOMIC_CAPTURE) {
1453       if (board[rt][ft] != EmptySquare ||
1454           kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1455         int r, f;
1456         ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1457         if (board[rf][ff] == king) return;
1458         for (r = rt-1; r <= rt+1; r++) {
1459           for (f = ft-1; f <= ft+1; f++) {
1460             if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1461                 board[r][f] == king) return;
1462           }
1463         }
1464       }
1465     }
1466     cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1467 }
1468
1469
1470 typedef struct {
1471     int rf, ff, rt, ft;
1472     ChessMove kind;
1473     int captures; // [HGM] losers
1474 } LegalityTestClosure;
1475
1476
1477 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1478    F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1479    king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1480    moves that would destroy your own king.  The CASTLE_OK flags are
1481    true if castling is not yet ruled out by a move of the king or
1482    rook.  Return TRUE if the player on move is currently in check and
1483    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */
1484 int
1485 GenLegal (Board board, int  flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1486 {
1487     GenLegalClosure cl;
1488     int ff, ft, k, left, right, swap;
1489     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1490     ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1491     int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1492     char *p;
1493
1494     cl.cb = callback;
1495     cl.cl = closure;
1496     xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1497     if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1498     GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1499
1500     if (inCheck) return TRUE;
1501
1502     /* Generate castling moves */
1503     if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1504         wKing = WhiteUnicorn; bKing = BlackUnicorn;
1505     }
1506
1507     p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1508     if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1509
1510     for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1511         if ((flags & F_WHITE_ON_MOVE) &&
1512             (flags & F_WHITE_KCASTLE_OK) &&
1513             board[0][ff] == wKing &&
1514             board[0][ff + 1] == EmptySquare &&
1515             board[0][ff + 2] == EmptySquare &&
1516             board[0][BOARD_RGHT-3] == EmptySquare &&
1517             board[0][BOARD_RGHT-2] == EmptySquare &&
1518             board[0][BOARD_RGHT-1] == WhiteRook &&
1519             castlingRights[0] != NoRights && /* [HGM] check rights */
1520             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1521             (ignoreCheck ||
1522              (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1523               !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1524               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1525               !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1526
1527             callback(board, flags,
1528                      ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1529                      0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1530         }
1531         if ((flags & F_WHITE_ON_MOVE) &&
1532             (flags & F_WHITE_QCASTLE_OK) &&
1533             board[0][ff] == wKing &&
1534             board[0][ff - 1] == EmptySquare &&
1535             board[0][ff - 2] == EmptySquare &&
1536             board[0][BOARD_LEFT+2] == EmptySquare &&
1537             board[0][BOARD_LEFT+1] == EmptySquare &&
1538             board[0][BOARD_LEFT+0] == WhiteRook &&
1539             castlingRights[1] != NoRights && /* [HGM] check rights */
1540             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1541             (ignoreCheck ||
1542              (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1543               !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1544               !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1545
1546             callback(board, flags,
1547                      ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1548                      0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1549         }
1550         if (!(flags & F_WHITE_ON_MOVE) &&
1551             (flags & F_BLACK_KCASTLE_OK) &&
1552             board[BOARD_HEIGHT-1][ff] == bKing &&
1553             board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1554             board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1555             board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1556             board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1557             board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1558             castlingRights[3] != NoRights && /* [HGM] check rights */
1559             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1560             (ignoreCheck ||
1561              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1562               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1563               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1564               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1565
1566             callback(board, flags,
1567                      ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1568                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1569         }
1570         if (!(flags & F_WHITE_ON_MOVE) &&
1571             (flags & F_BLACK_QCASTLE_OK) &&
1572             board[BOARD_HEIGHT-1][ff] == bKing &&
1573             board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1574             board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1575             board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1576             board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1577             board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1578             castlingRights[4] != NoRights && /* [HGM] check rights */
1579             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1580             (ignoreCheck ||
1581              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1582               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1583               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1584
1585             callback(board, flags,
1586                      ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1587                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1588         }
1589     }
1590
1591   if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1592
1593     /* generate all potential FRC castling moves (KxR), ignoring flags */
1594     /* [HGM] test if the Rooks we find have castling rights */
1595     /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1596
1597
1598     if ((flags & F_WHITE_ON_MOVE) != 0) {
1599         ff = castlingRights[2]; /* King file if we have any rights */
1600         if(ff != NoRights && board[0][ff] == WhiteKing) {
1601     if (appData.debugMode) {
1602         fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1603                 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1604     }
1605             ft = castlingRights[0]; /* Rook file if we have H-side rights */
1606             left  = ff+1;
1607             right = BOARD_RGHT-2;
1608             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
1609             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1610                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1611             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1612                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1613             if(ft != NoRights && board[0][ft] == WhiteRook) {
1614                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1615                 if(swap)                        callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1616             }
1617
1618             ft = castlingRights[1]; /* Rook file if we have A-side rights */
1619             left  = BOARD_LEFT+2;
1620             right = ff-1;
1621             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1622             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1623                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1624             if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1625             if(ff > BOARD_LEFT+2)
1626             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1627                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1628             if(ft != NoRights && board[0][ft] == WhiteRook) {
1629                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1630                 if(swap)                        callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1631             }
1632         }
1633     } else {
1634         ff = castlingRights[5]; /* King file if we have any rights */
1635         if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1636             ft = castlingRights[3]; /* Rook file if we have H-side rights */
1637             left  = ff+1;
1638             right = BOARD_RGHT-2;
1639             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
1640             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1641                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1642             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1643                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1644             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1645                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1646                 if(swap)                        callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1647             }
1648
1649             ft = castlingRights[4]; /* Rook file if we have A-side rights */
1650             left  = BOARD_LEFT+2;
1651             right = ff-1;
1652             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1653             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1654                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1655             if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1656             if(ff > BOARD_LEFT+2)
1657             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1658                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1659             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1660                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1661                 if(swap)                        callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1662             }
1663         }
1664     }
1665
1666   }
1667
1668     return FALSE;
1669 }
1670
1671
1672 typedef struct {
1673     int rking, fking;
1674     int check;
1675 } CheckTestClosure;
1676
1677
1678 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1679                                  int rf, int ff, int rt, int ft,
1680                                  VOIDSTAR closure));
1681
1682
1683 void
1684 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1685 {
1686     register CheckTestClosure *cl = (CheckTestClosure *) closure;
1687
1688     if (rt == cl->rking && ft == cl->fking) {
1689         if((int)xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1690         cl->check++;
1691         xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1692     }
1693     if( (int)board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1694         && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1695         cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1696 }
1697
1698
1699 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1700    he leave himself in check?  Or if rf == -1, is the player on move
1701    in check now?  enPassant must be TRUE if the indicated move is an
1702    e.p. capture.  The possibility of castling out of a check along the
1703    back rank is not accounted for (i.e., we still return nonzero), as
1704    this is illegal anyway.  Return value is the number of times the
1705    king is in check. */
1706 int
1707 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1708 {
1709     CheckTestClosure cl;
1710     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1711     ChessSquare captured = EmptySquare, ep=0, trampled=0, trampled2 = 0;
1712     int saveKill = killX;
1713     /*  Suppress warnings on uninitialized variables    */
1714
1715     if(gameInfo.variant == VariantXiangqi)
1716         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1717     if(gameInfo.variant == VariantKnightmate)
1718         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1719     if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check
1720         int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1721         if(gameInfo.variant == VariantShogi) prince -= 11;                   // White/BlackFalcon
1722         for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1723             if(board[r][f] == k || board[r][f] == prince) {
1724                 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1725                 king = board[r][f]; // remember hich one we had
1726             }
1727         }
1728     }
1729
1730     if (rt >= 0) {
1731         if (enPassant) {
1732             captured = board[rf][ft];
1733             board[rf][ft] = EmptySquare;
1734         } else {
1735             captured = board[rt][ft];
1736             if(killX >= 0) {
1737                 trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; saveKill += (kill2X << 16) + (1 << 30);
1738                 if(kill2X >= 0) { trampled2 = board[kill2Y][kill2X]; board[kill2Y][kill2X] = EmptySquare; kill2X = -1; }
1739             }
1740         }
1741         if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1742             board[rt][ft] = board[rf][ff];
1743             if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
1744         }
1745         ep = board[EP_STATUS];
1746         if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1747             ChessSquare victim = saveKill < 0 ? EmptySquare : trampled;
1748             if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) &&           // capturer is Lion
1749                 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) &&           // captures from a distance
1750                 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn    // no or worthless 'bridge'
1751                                      || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between)
1752                      board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1753         }
1754     }
1755
1756     /* For compatibility with ICS wild 9, we scan the board in the
1757        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1758        and we test only whether that one is in check. */
1759     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1760         for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1761           if (board[cl.rking][cl.fking] == king) {
1762               cl.check = 0;
1763               if(gameInfo.variant == VariantXiangqi) {
1764                   /* [HGM] In Xiangqi opposing Kings means check as well */
1765                   int i, dir;
1766                   dir = (king >= BlackPawn) ? -1 : 1;
1767                   for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1768                                 board[i][cl.fking] == EmptySquare; i+=dir );
1769                   if(i>=0 && i<BOARD_HEIGHT &&
1770                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1771                           cl.check++;
1772               }
1773               GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1774               if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1775                  goto undo_move;  /* 2-level break */
1776           }
1777       }
1778
1779   undo_move:
1780
1781     if (rt >= 0) {
1782         if(rf != DROP_RANK) // [HGM] drop
1783             board[rf][ff] = board[rt][ft];
1784         if (enPassant) {
1785             board[rf][ft] = captured;
1786             board[rt][ft] = EmptySquare;
1787         } else {
1788             if(saveKill >= 0) {
1789                 if(saveKill & 1<<30) board[kill2Y][kill2X = saveKill >> 16 & 0xFFF] = trampled2;
1790                 board[killY][killX = saveKill & 0xFFF] = trampled;
1791             }
1792             board[rt][ft] = captured;
1793         }
1794         board[EP_STATUS] = ep;
1795     }
1796
1797     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1798 }
1799
1800 int
1801 HasLion (Board board, int flags)
1802 {
1803     int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1804     int r, f;
1805     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1806         if(board[r][f] == lion) return 1;
1807     return 0;
1808 }
1809
1810 ChessMove
1811 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1812 {   // [HGM] put drop legality testing in separate routine for clarity
1813     int n;
1814 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1815     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1816     n = PieceToNumber(piece);
1817     if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1818         && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1819         return ImpossibleMove; // piece not available
1820     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1821         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1822            (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1823             piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1824             piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1825         if(piece == WhitePawn || piece == BlackPawn) {
1826             int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1827             for(r=1; r<BOARD_HEIGHT-1; r++)
1828                 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1829             // should still test if we mate with this Pawn
1830         }
1831     } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1832         if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1833     } else {
1834         if( (piece == WhitePawn || piece == BlackPawn) &&
1835             (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1836             return IllegalMove; /* no pawn drops on 1st/8th */
1837     }
1838 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1839     if (!(flags & F_IGNORE_CHECK) &&
1840         CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1841     return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1842 }
1843
1844 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1845                                     int rf, int ff, int rt, int ft,
1846                                     VOIDSTAR closure));
1847
1848 void
1849 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1850 {
1851     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1852
1853     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1854         cl->captures++; // [HGM] losers: count legal captures
1855     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1856       cl->kind = kind;
1857 }
1858
1859 ChessMove
1860 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1861 {
1862     LegalityTestClosure cl; ChessSquare piece, filterPiece;
1863
1864     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1865     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1866     piece = filterPiece = board[rf][ff];
1867     if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece);
1868
1869     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1870     /* (perhaps we should disallow moves that obviously leave us in check?)              */
1871     if((piece == WhiteFalcon || piece == BlackFalcon ||
1872         piece == WhiteCobra  || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
1873         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1874
1875     cl.rf = rf;
1876     cl.ff = ff;
1877     cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1878     cl.ft = fFilter = ft;
1879     cl.kind = IllegalMove;
1880     cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1881     if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1882     GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1883     if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1884                 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1885         return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1886
1887     if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1888     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1889         if(board[rf][ff] < BlackPawn) { // white
1890             if(rf != 0) return IllegalMove; // must be on back rank
1891             if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1892             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1893             if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1894             if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1895         } else {
1896             if(rf != BOARD_HEIGHT-1) return IllegalMove;
1897             if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1898             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1899             if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1900             if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1901         }
1902     } else
1903     if(gameInfo.variant == VariantChu) {
1904         if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1905         if(promoChar != '+')
1906             return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1907         if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') {
1908             if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.')
1909             return ImpossibleMove;
1910         }
1911         return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1912     } else
1913     if(gameInfo.variant == VariantShogi) {
1914         /* [HGM] Shogi promotions. '=' means defer */
1915         if(rf != DROP_RANK && cl.kind == NormalMove) {
1916             ChessSquare piece = board[rf][ff];
1917             int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
1918
1919             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1920             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1921                promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1922                promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1923                   promoChar = '+'; // allowed ICS notations
1924 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1925             if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1926                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1927             else if(flags & F_WHITE_ON_MOVE) {
1928                 if( (int) piece < (int) WhiteWazir &&
1929                      (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) {
1930                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1931                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1932                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1933                     else /* promotion optional, default is defer */
1934                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1935                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1936             } else {
1937                 if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) {
1938                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1939                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1940                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1941                     else /* promotion optional, default is defer */
1942                        cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1943                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1944             }
1945         }
1946     } else
1947     if (promoChar != NULLCHAR) {
1948         if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1949             ChessSquare piece = board[rf][ff];
1950             if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1951             // should test if in zone, really
1952             if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1953                 return IllegalMove;
1954             if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1955         } else
1956         if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1957         if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1958             ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1959             if(piece == EmptySquare)
1960                 cl.kind = ImpossibleMove; // non-existing piece
1961             if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1962                 cl.kind = IllegalMove; // no two Lions
1963             } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1964                 if(promoChar != PieceToChar(BlackKing)) {
1965                     if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1966                     if(piece == BlackLance) cl.kind = ImpossibleMove;
1967                 } else { // promotion to King allowed only if we do not have two yet
1968                     int r, f, kings = 0;
1969                     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1970                     if(kings == 2) cl.kind = IllegalMove;
1971                 }
1972             } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1973                           piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1974             else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1975              cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1976             else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1977              cl.kind = IllegalMove; // promotion to King usually not allowed
1978         } else {
1979             cl.kind = IllegalMove;
1980         }
1981     }
1982     return cl.kind;
1983 }
1984
1985 typedef struct {
1986     int count;
1987 } MateTestClosure;
1988
1989 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1990                                 int rf, int ff, int rt, int ft,
1991                                 VOIDSTAR closure));
1992
1993 void
1994 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1995 {
1996     register MateTestClosure *cl = (MateTestClosure *) closure;
1997
1998     cl->count++;
1999 }
2000
2001 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
2002 int
2003 MateTest (Board board, int flags)
2004 {
2005     MateTestClosure cl;
2006     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
2007     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
2008
2009     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
2010         // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
2011         nrKing += (board[r][f] == king);   // stm has king
2012         if( board[r][f] != EmptySquare ) {
2013             if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
2014                  myPieces++;
2015             else hisPieces++;
2016         }
2017     }
2018     switch(gameInfo.variant) { // [HGM] losers: extinction wins
2019         case VariantShatranj:
2020                 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
2021         default:
2022                 break;
2023         case VariantAtomic:
2024                 if(nrKing == 0) return MT_NOKING;
2025                 break;
2026         case VariantLosers:
2027                 if(myPieces == 1) return MT_BARE;
2028     }
2029     cl.count = 0;
2030     inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
2031     // [HGM] 3check: yet to do!
2032     if (cl.count > 0) {
2033         return inCheck ? MT_CHECK : MT_NONE;
2034     } else {
2035         if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
2036                                  && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
2037             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
2038             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
2039                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
2040                     if(board[n][holdings] != EmptySquare) {
2041                         int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
2042                         if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
2043                     }
2044         }
2045         if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
2046                 return myPieces == hisPieces ? MT_STALEMATE :
2047                                         myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
2048         else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
2049         else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
2050
2051         return inCheck ? MT_CHECKMATE
2052                        : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
2053                           MT_STAINMATE : MT_STALEMATE;
2054     }
2055 }
2056
2057
2058 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
2059                                     int rf, int ff, int rt, int ft,
2060                                     VOIDSTAR closure));
2061
2062 void
2063 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2064 {
2065     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
2066     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
2067     extern int kifu; // in parser.c
2068
2069     // [HGM] wild: for wild-card pieces rt and rf are dummies
2070     if(piece == WhiteFalcon || piece == BlackFalcon ||
2071        piece == WhiteCobra  || piece == BlackCobra)
2072         wildCard = !pieceDefs; // no wildcards when engine defined pieces
2073
2074     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
2075          || PieceToChar(board[rf][ff]) == '~'
2076               && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff]))
2077                                                                       ) &&
2078         (cl->rfIn == -1 || cl->rfIn == rf) &&
2079         (cl->ffIn == -1 || cl->ffIn == ff) &&
2080         (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
2081         (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
2082
2083         if(cl->count && rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) return; // duplicate move
2084
2085         if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required
2086             int this = 1, other = 1;
2087             if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf);
2088             if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf);
2089             if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf);
2090             if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff);
2091             if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff);
2092             if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used
2093             if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it
2094             if(!this) return;       // the current move does not satisfy the requested relative position, ignore it
2095         }
2096
2097         cl->count++;
2098         if(cl->count == 1 || board[rt][ft] != EmptySquare) {
2099           // [HGM] oneclick: if multiple moves, be sure we remember capture
2100           cl->piece = board[rf][ff];
2101           cl->rf = rf;
2102           cl->ff = ff;
2103           cl->rt = wildCard ? cl->rtIn : rt;
2104           cl->ft = wildCard ? cl->ftIn : ft;
2105           cl->kind = kind;
2106         }
2107         cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
2108     }
2109 }
2110
2111 void
2112 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
2113 {
2114     int illegal = 0; char c = closure->promoCharIn;
2115
2116     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
2117     closure->count = closure->captures = 0;
2118     closure->rf = closure->ff = closure->rt = closure->ft = 0;
2119     closure->kind = ImpossibleMove;
2120     rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
2121     fFilter = closure->ftIn;
2122     if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
2123         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2124         if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
2125             closure->count = closure->captures = 0;
2126             closure->rf = closure->ff = closure->rt = closure->ft = 0;
2127             closure->kind = ImpossibleMove;
2128             GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2129         }
2130     } else
2131     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2132     if (closure->count == 0) {
2133         /* See if it's an illegal move due to check */
2134         illegal = 1;
2135         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2136         if (closure->count == 0) {
2137             /* No, it's not even that */
2138           if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) {
2139             int f, r; // if there is only a single piece of the requested type on the board, use that
2140             closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2141             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2142                 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2143             if(closure->count > 1) illegal = 0; // ambiguous
2144           }
2145           if(closure->count == 0) {
2146             if (appData.debugMode) { int i, j;
2147                 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2148                     for(j=0; j<BOARD_WIDTH; j++)
2149                         fprintf(debugFP, "%3d", (int) board[i][j]);
2150                     fprintf(debugFP, "\n");
2151                 }
2152             }
2153             return;
2154           }
2155         }
2156     } else if(pieceDefs && closure->count > 1 && closure->rtIn >=0) { // [HGM] gen: move is ambiguous under engine-defined rules (and not one-click)
2157         DisambiguateClosure spare = *closure;
2158         pieceDefs = FALSE; spare.count = 0;     // See if the (erroneous) built-in rules would resolve that
2159         GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2160         if(spare.count == 1) *closure = spare;  // It does, so use those in stead (game from file saved before gen patch?)
2161         pieceDefs = TRUE;
2162     }
2163
2164     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2165     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2166         if(closure->piece < BlackPawn) { // white
2167             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2168             if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2169             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2170             if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2171             if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2172         } else {
2173             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2174             if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2175             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2176             if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2177             if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2178         }
2179     } else
2180     if(gameInfo.variant == VariantChu) {
2181         if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2182     } else
2183     if(gameInfo.variant == VariantShogi) {
2184         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2185         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2186             ChessSquare piece = closure->piece;
2187             int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
2188             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
2189                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2190                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2191                    c = '+'; // allowed ICS notations
2192             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2193             else if(flags & F_WHITE_ON_MOVE) {
2194                 if( (int) piece < (int) WhiteWazir &&
2195                      (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) {
2196                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2197                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2198                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2199                     else /* promotion optional, default is defer */
2200                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2201                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2202             } else {
2203                 if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) {
2204                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2205                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2206                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2207                     else /* promotion optional, default is defer */
2208                        closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2209                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2210             }
2211         }
2212         if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2213         if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2214     } else
2215     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2216         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2217             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2218                gameInfo.variant == VariantMakruk)
2219                 c = PieceToChar(BlackFerz);
2220             else if(gameInfo.variant == VariantASEAN)
2221                 c = PieceToChar(BlackRook);
2222             else if(gameInfo.variant == VariantGreat)
2223                 c = PieceToChar(BlackMan);
2224             else if(gameInfo.variant == VariantGrand)
2225                     closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2226             else
2227                 c = PieceToChar(BlackQueen);
2228         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2229         else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2230     } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2231         ChessSquare p = closure->piece;
2232         if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+')
2233             closure->kind = ImpossibleMove; // used on non-promotable piece
2234         else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2235     } else if (c != NULLCHAR) closure->kind = IllegalMove;
2236
2237     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2238     if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2239         closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2240     if (closure->count > 1) {
2241         closure->kind = AmbiguousMove;
2242     }
2243     if (illegal) {
2244         /* Note: If more than one illegal move matches, but no legal
2245            moves, we return IllegalMove, not AmbiguousMove.  Caller
2246            can look at closure->count to detect this.
2247         */
2248         closure->kind = IllegalMove;
2249     }
2250 }
2251
2252
2253 typedef struct {
2254     /* Input */
2255     ChessSquare piece;
2256     int rf, ff, rt, ft;
2257     /* Output */
2258     ChessMove kind;
2259     int rank;
2260     int file;
2261     int either;
2262 } CoordsToAlgebraicClosure;
2263
2264 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2265                                          ChessMove kind, int rf, int ff,
2266                                          int rt, int ft, VOIDSTAR closure));
2267
2268 void
2269 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2270 {
2271     register CoordsToAlgebraicClosure *cl =
2272       (CoordsToAlgebraicClosure *) closure;
2273
2274     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2275         (board[rf][ff] == cl->piece
2276          || PieceToChar(board[rf][ff]) == '~' &&
2277             (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece)
2278                                      ) {
2279         if (rf == cl->rf) {
2280             if (ff == cl->ff) {
2281                 cl->kind = kind; /* this is the move we want */
2282             } else {
2283                 cl->file++; /* need file to rule out this move */
2284             }
2285         } else {
2286             if (ff == cl->ff) {
2287                 cl->rank++; /* need rank to rule out this move */
2288             } else {
2289                 cl->either++; /* rank or file will rule out this move */
2290             }
2291         }
2292     }
2293 }
2294
2295 /* Convert coordinates to normal algebraic notation.
2296    promoChar must be NULLCHAR or 'x' if not a promotion.
2297 */
2298 ChessMove
2299 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2300 {
2301     ChessSquare piece;
2302     ChessMove kind;
2303     char *outp = out, c, capture;
2304     CoordsToAlgebraicClosure cl;
2305
2306     if (rf == DROP_RANK) {
2307         if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2308         /* Bughouse piece drop */
2309         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2310         *outp++ = '@';
2311         *outp++ = ft + AAA;
2312         if(rt+ONE <= '9')
2313            *outp++ = rt + ONE;
2314         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2315         *outp = NULLCHAR;
2316         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2317     }
2318
2319     if (promoChar == 'x') promoChar = NULLCHAR;
2320     piece = board[rf][ff];
2321     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
2322
2323     switch (piece) {
2324       case WhitePawn:
2325       case BlackPawn:
2326         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2327         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2328             /* Keep short notation if move is illegal only because it
2329                leaves the player in check, but still return IllegalMove */
2330             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2331             if (kind == IllegalMove) break;
2332             kind = IllegalMove;
2333         }
2334         /* Pawn move */
2335         *outp++ = ff + AAA;
2336         capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2337         if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2338             /* Non-capture; use style "e5" */
2339             if(rt+ONE <= '9')
2340                *outp++ = rt + ONE;
2341             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2342         } else {
2343             /* Capture; use style "exd5" */
2344             if(capture)
2345             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
2346             *outp++ = ft + AAA;
2347             if(rt+ONE <= '9')
2348                *outp++ = rt + ONE;
2349             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2350         }
2351         /* Use promotion suffix style "=Q" */
2352         *outp = NULLCHAR;
2353         if (promoChar != NULLCHAR) {
2354             if(IS_SHOGI(gameInfo.variant)) {
2355                 /* [HGM] ... but not in Shogi! */
2356                 *outp++ = promoChar == '=' ? '=' : '+';
2357             } else {
2358                 *outp++ = '=';
2359                 *outp++ = ToUpper(promoChar);
2360             }
2361             *outp = NULLCHAR;
2362         }
2363         return kind;
2364
2365
2366       case WhiteKing:
2367       case BlackKing:
2368         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2369         /* Code added by Tord:  FRC castling. */
2370         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2371            (piece == BlackKing && board[rt][ft] == BlackRook)) {
2372           if(ft > ff)
2373             safeStrCpy(out, "O-O", MOVE_LEN);
2374           else
2375             safeStrCpy(out, "O-O-O", MOVE_LEN);
2376           return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2377         }
2378         /* End of code added by Tord */
2379         /* Test for castling or ICS wild castling */
2380         /* Use style "O-O" (oh-oh) for PGN compatibility */
2381         else if (rf == rt &&
2382             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2383             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
2384             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2385              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2386             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2387               snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2388             else
2389               snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2390
2391             /* This notation is always unambiguous, unless there are
2392                kings on both the d and e files, with "wild castling"
2393                possible for the king on the d file and normal castling
2394                possible for the other.  ICS rules for wild 9
2395                effectively make castling illegal for either king in
2396                this situation.  So I am not going to worry about it;
2397                I'll just generate an ambiguous O-O in this case.
2398             */
2399             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2400         }
2401
2402         /* else fall through */
2403       default:
2404         /* Piece move */
2405         cl.rf = rf;
2406         cl.ff = ff;
2407         cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2408         cl.ft = fFilter = ft;
2409         cl.piece = piece;
2410         cl.kind = IllegalMove;
2411         cl.rank = cl.file = cl.either = 0;
2412         c = PieceToChar(piece) ;
2413         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed
2414
2415         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2416             /* Generate pretty moves for moving into check, but
2417                still return IllegalMove.
2418             */
2419             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece)));
2420             if (cl.kind == IllegalMove) break;
2421             cl.kind = IllegalMove;
2422         }
2423
2424         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2425            else "Ngf3" or "Ngxf7",
2426            else "N1f3" or "N5xf7",
2427            else "Ng1f3" or "Ng5xf7".
2428         */
2429         if( c == '~' || c == '+') {
2430            /* [HGM] print nonexistent piece as its demoted version */
2431            piece = (ChessSquare) (CHUDEMOTED(piece));
2432         }
2433         if(c=='+') *outp++ = c;
2434         *outp++ = ToUpper(PieceToChar(piece));
2435         if(*outp = PieceSuffix(piece)) outp++;
2436
2437         if (cl.file || (cl.either && !cl.rank)) {
2438             *outp++ = ff + AAA;
2439         }
2440         if (cl.rank) {
2441             if(rf+ONE <= '9')
2442                 *outp++ = rf + ONE;
2443             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2444         }
2445
2446         if(board[rt][ft] != EmptySquare)
2447           *outp++ = 'x';
2448
2449         *outp++ = ft + AAA;
2450         if(rt+ONE <= '9')
2451            *outp++ = rt + ONE;
2452         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2453         if (IS_SHOGI(gameInfo.variant)) {
2454             /* [HGM] in Shogi non-pawns can promote */
2455             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2456         }
2457         else if (gameInfo.variant == VariantChuChess && promoChar ||
2458                  gameInfo.variant != VariantSuper && promoChar &&
2459                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2460             *outp++ = '=';
2461             *outp++ = ToUpper(promoChar);
2462         }
2463         else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2464             *outp++ = '/';
2465             *outp++ = ToUpper(promoChar);
2466         }
2467         *outp = NULLCHAR;
2468         return cl.kind;
2469
2470       case EmptySquare:
2471         /* Moving a nonexistent piece */
2472         break;
2473     }
2474
2475     /* Not a legal move, even ignoring check.
2476        If there was a piece on the from square,
2477        use style "Ng1g3" or "Ng1xe8";
2478        if there was a pawn or nothing (!),
2479        use style "g1g3" or "g1xe8".  Use "x"
2480        if a piece was on the to square, even
2481        a piece of the same color.
2482     */
2483     outp = out;
2484     c = 0;
2485     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2486         int r, f;
2487       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2488                 c += (board[r][f] == piece); // count on-board pieces of given type
2489         *outp = PieceToChar(piece);
2490         if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
2491         *outp++ = ToUpper(PieceToChar(piece));
2492         if(*outp = PieceSuffix(piece)) outp++;
2493     }
2494   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2495     *outp++ = ff + AAA;
2496     if(rf+ONE <= '9')
2497        *outp++ = rf + ONE;
2498     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2499   }
2500     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2501     *outp++ = ft + AAA;
2502     if(rt+ONE <= '9')
2503        *outp++ = rt + ONE;
2504     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2505     /* Use promotion suffix style "=Q" */
2506     if (promoChar != NULLCHAR && promoChar != 'x') {
2507         *outp++ = '=';
2508         *outp++ = ToUpper(promoChar);
2509     }
2510     *outp = NULLCHAR;
2511
2512     return IllegalMove;
2513 }
2514
2515 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2516
2517 typedef struct {
2518     /* Input */
2519     int rf, ff, rt, ft;
2520     /* Output */
2521     int recaptures;
2522 } ChaseClosure;
2523
2524 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2525
2526 int preyStackPointer, chaseStackPointer;
2527
2528 struct {
2529 unsigned char rf, ff, rt, ft;
2530 } chaseStack[100];
2531
2532 struct {
2533 unsigned char rank, file;
2534 } preyStack[100];
2535
2536
2537
2538
2539 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2540
2541 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2542                                 int rf, int ff, int rt, int ft,
2543                                 VOIDSTAR closure));
2544
2545 void
2546 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2547 {   // For adding captures that can lead to chase indictment to the chaseStack
2548     if(board[rt][ft] == EmptySquare) return;                               // non-capture
2549     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
2550     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
2551     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
2552     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2553     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2554     chaseStack[chaseStackPointer].rf = rf;
2555     chaseStack[chaseStackPointer].ff = ff;
2556     chaseStack[chaseStackPointer].rt = rt;
2557     chaseStack[chaseStackPointer].ft = ft;
2558     chaseStackPointer++;
2559 }
2560
2561 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2562                                 int rf, int ff, int rt, int ft,
2563                                 VOIDSTAR closure));
2564
2565 void
2566 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2567 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2568     int i;
2569     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2570
2571     if(board[rt][ft] == EmptySquare) return; // no capture
2572     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2573         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
2574     }
2575     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2576     for(i=0; i<chaseStackPointer; i++) {
2577         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2578            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
2579             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2580             chaseStack[i] = chaseStack[--chaseStackPointer];
2581             break;
2582         }
2583     }
2584 }
2585
2586 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2587                                 int rf, int ff, int rt, int ft,
2588                                 VOIDSTAR closure));
2589
2590 void
2591 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2592 {   // for determining if a piece (given through the closure) is protected
2593     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2594
2595     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
2596     if(appData.debugMode && board[rt][ft] != EmptySquare)
2597         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2598 }
2599
2600 extern char moveList[MAX_MOVES][MOVE_LEN];
2601
2602 int
2603 PerpetualChase (int first, int last)
2604 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2605     int i, j, k, tail;
2606     ChaseClosure cl;
2607     ChessSquare captured;
2608
2609     preyStackPointer = 0;        // clear stack of chased pieces
2610     for(i=first; i<last; i+=2) { // for all positions with same side to move
2611         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2612         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
2613         // determine all captures possible after the move, and put them on chaseStack
2614         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2615         if(appData.debugMode) { int n;
2616             for(n=0; n<chaseStackPointer; n++)
2617                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2618                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2619             fprintf(debugFP, ": all capts\n");
2620         }
2621         // determine all captures possible before the move, and delete them from chaseStack
2622         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2623         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2624         cl.rt = moveList[i][3]-ONE;
2625         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2626         CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2627         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2628         xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2629         if(appData.debugMode) { int n;
2630             for(n=0; n<chaseStackPointer; n++)
2631                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2632                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2633             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2634         }
2635         // chaseSack now contains all captures made possible by the move
2636         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2637             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2638             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2639
2640             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2641             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
2642
2643             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2644                 continue; // C or H attack on R is always chase; leave on chaseStack
2645
2646             if(attacker == victim) {
2647                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2648                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2649                         // we can capture back with equal piece, so this is no chase but a sacrifice
2650                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2651                         j--; /* ! */ continue;
2652                 }
2653
2654             }
2655
2656             // the attack is on a lower piece, or on a pinned or blocked equal one
2657             CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2658             CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2659             // test if the victim is protected by a true protector. First make the capture.
2660             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2661             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2662             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2663             // Then test if the opponent can recapture
2664             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
2665             cl.rt = chaseStack[j].rt;
2666             cl.ft = chaseStack[j].ft;
2667             if(appData.debugMode) {
2668                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2669             }
2670             xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2671             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2672             xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2673             // unmake the capture
2674             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2675             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2676             // if a recapture was found, piece is protected, and we are not chasing it.
2677             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2678                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2679                 j--; /* ! */
2680             }
2681         }
2682         // chaseStack now contains all moves that chased
2683         if(appData.debugMode) { int n;
2684             for(n=0; n<chaseStackPointer; n++)
2685                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2686                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2687             fprintf(debugFP, ": chases\n");
2688         }
2689         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2690             for(j=0; j<chaseStackPointer; j++) {
2691                 preyStack[j].rank = chaseStack[j].rt;
2692                 preyStack[j].file = chaseStack[j].ft;
2693             }
2694             preyStackPointer = chaseStackPointer;
2695         }
2696         tail = 0;
2697         for(j=0; j<chaseStackPointer; j++) {
2698             for(k=0; k<preyStackPointer; k++) {
2699                 // search the victim of each chase move on the preyStack (first occurrence)
2700                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2701                     if(k < tail) break; // piece was already identified as still being chased
2702                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2703                     preyStack[tail] = preyStack[k];                // by swapping
2704                     preyStack[k] = preyStack[preyStackPointer];
2705                     tail++;
2706                     break;
2707                 }
2708             }
2709         }
2710         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2711         if(appData.debugMode) { int n;
2712             for(n=0; n<preyStackPointer; n++)
2713                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2714             fprintf(debugFP, "always chased upto ply %d\n", i);
2715         }
2716         // now adjust the location of the chased pieces according to opponent move
2717         for(j=0; j<preyStackPointer; j++) {
2718             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2719                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2720                 preyStack[j].rank = moveList[i+1][3]-ONE;
2721                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2722                 break;
2723             }
2724         }
2725     }
2726     return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2727                                 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the
2728 }