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