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