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