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