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