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