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