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