2 * moves.c - Move generation and checking
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
10 * Enhancements Copyright 2005 Alessandro Scotti
12 * The following terms apply to Digital Equipment Corporation's copyright
14 * ------------------------------------------------------------------------
17 * Permission to use, copy, modify, and distribute this software and its
18 * documentation for any purpose and without fee is hereby granted,
19 * provided that the above copyright notice appear in all copies and that
20 * both that copyright notice and this permission notice appear in
21 * supporting documentation, and that the name of Digital not be
22 * used in advertising or publicity pertaining to distribution of the
23 * software without specific, written prior permission.
25 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
26 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
27 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
28 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
29 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
30 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
32 * ------------------------------------------------------------------------
34 * The following terms apply to the enhanced version of XBoard
35 * distributed by the Free Software Foundation:
36 * ------------------------------------------------------------------------
38 * GNU XBoard is free software: you can redistribute it and/or modify
39 * it under the terms of the GNU General Public License as published by
40 * the Free Software Foundation, either version 3 of the License, or (at
41 * your option) any later version.
43 * GNU XBoard is distributed in the hope that it will be useful, but
44 * WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
46 * General Public License for more details.
48 * You should have received a copy of the GNU General Public License
49 * along with this program. If not, see http://www.gnu.org/licenses/. *
51 *------------------------------------------------------------------------
52 ** See the file ChangeLog for a revision history. */
61 #else /* not HAVE_STRING_H */
63 #endif /* not HAVE_STRING_H */
69 int WhitePiece P((ChessSquare));
70 int BlackPiece P((ChessSquare));
71 int SameColor P((ChessSquare, ChessSquare));
72 int PosFlags(int index);
75 char *pieceDesc[EmptySquare];
76 char *defaultDesc[EmptySquare] = {
77 "fmWfceFifmnD", "N", "B", "R", "Q",
78 "F", "A", "BN", "RN", "W", "K",
79 "mRcpR", "N0", "BW", "RF", "gQ",
80 "", "", "QN", "", "N", "",
82 "", "", "", "", "", "",
84 "", "", "", "", "", "K"
88 WhitePiece (ChessSquare piece)
90 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
94 BlackPiece (ChessSquare piece)
96 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
101 SameColor (ChessSquare piece1, ChessSquare piece2)
103 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
104 (int) piece1 < (int) BlackPawn &&
105 (int) piece2 >= (int) WhitePawn &&
106 (int) piece2 < (int) BlackPawn)
107 || ((int) piece1 >= (int) BlackPawn &&
108 (int) piece1 < (int) EmptySquare &&
109 (int) piece2 >= (int) BlackPawn &&
110 (int) piece2 < (int) EmptySquare);
113 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
116 unsigned char pieceToChar[EmptySquare+1] = {
117 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
118 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
119 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
120 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
122 unsigned char pieceNickName[EmptySquare];
123 int promoPartner[EmptySquare];
126 PieceToChar (ChessSquare p)
129 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
130 c = pieceToChar[(int) p];
131 if(c & 128) c = c & 63 | 64;
136 PieceSuffix (ChessSquare p)
139 if((int)p < 0 || (int)p >= (int)EmptySquare) return 0; /* [HGM] for safety */
140 c = pieceToChar[(int) p];
141 if(c < 128) return 0;
142 return SUFFIXES[c - 128 >> 6];
146 PieceToNumber (ChessSquare p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
149 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
151 while(start++ != p) if(pieceToChar[start-1] != '.' && pieceToChar[start-1] != '+') i++;
159 if(c == '.') return EmptySquare;
160 for(i=0; i< (int) EmptySquare; i++)
161 if(pieceNickName[i] == c) return (ChessSquare) i;
162 for(i=0; i< (int) EmptySquare; i++)
163 if(pieceToChar[i] == c) return (ChessSquare) i;
168 CopyBoard (Board to, Board from)
172 for (i = 0; i < BOARD_HEIGHT; i++)
173 for (j = 0; j < BOARD_WIDTH; j++)
174 to[i][j] = from[i][j];
175 for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
176 to[VIRGIN][j] = from[VIRGIN][j],
177 to[CASTLING][j] = from[CASTLING][j];
178 to[HOLDINGS_SET] = 0; // flag used in ICS play
182 CompareBoards (Board board1, Board board2)
186 for (i = 0; i < BOARD_HEIGHT; i++)
187 for (j = 0; j < BOARD_WIDTH; j++) {
188 if (board1[i][j] != board2[i][j])
194 char defaultName[] = "PNBRQ......................................K" // white
195 "pnbrq......................................k"; // black
196 char shogiName[] = "PNBRLS...G.++++++..........................K" // white
197 "pnbrls...g.++++++..........................k"; // black
198 char xqName[] = "PH.R.AE..K.C................................" // white
199 "ph.r.ae..k.c................................"; // black
202 CollectPieceDescriptors ()
203 { // make a line of piece descriptions for use in the PGN Piece tag:
204 // dump all engine defined pieces, and pieces with non-standard names,
205 // but suppress black pieces that are the same as their white counterpart
207 static char buf[MSG_SIZ], s[2];
208 char *m, c, d, *pieceName = defaultName;
211 if(!pieceDefs) return "";
212 if(gameInfo.variant == VariantChu) return ""; // for now don't do this for Chu Shogi
213 if(gameInfo.variant == VariantShogi) pieceName = shogiName;
214 if(gameInfo.variant == VariantXiangqi) pieceName = xqName;
215 for(p=WhitePawn; p<EmptySquare; p++) {
216 if((c = pieceToChar[p]) == '.' || c == '~') continue; // does not participate
217 m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED(p)] : c);
218 if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == (c & ~32)
219 && (c != '+' || pieceToChar[DEMOTED(BLACK_TO_WHITE p)] == d)) {// black member of normal pair
220 char *wm = pieceDesc[BLACK_TO_WHITE p];
221 if(!m && !wm || m && wm && !strcmp(wm, m)) continue; // moves as a white piece
222 } else // white or unpaired black
223 if((p < BlackPawn || CharToPiece(d & ~32) != EmptySquare) && // white or lone black
224 !pieceDesc[p] /*&& pieceName[p] == c*/) continue; // orthodox piece known by its usual name
225 // TODO: listing pieces because of unusual name can only be done if we have accurate Betza of all defaults
226 if(!m) m = defaultDesc[p];
229 *s = (d > 128 ? SUFFIXES[d-128>>6] : 0); d = 64 + (d & 63);
230 snprintf(buf+len, MSG_SIZ-len, "%s%s%c%s:%s", len ? ";" : "", c == '+' ? "+" : "", d, s, m);
235 // [HGM] gen: configurable move generation from Betza notation sent by engine.
236 // Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill-
237 // square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1
238 // if the move has 1 or 2 legs. Only the marking of squares makes use of this info, by only marking
239 // target squares of leg 1 (rejecting null move). A dummy move with MoveType 'FirstLeg' to the relay square
240 // is generated, so a cyan marker can be put there, and other functions can ignore such a move. When the
241 // user selects this square, it becomes the kill-square. Once a kill-square is set, only 2-leg moves are
242 // generated that use that square as relay, plus 1-leg moves, so the 1-leg move that goes to the kill-square
243 // can be marked during 2nd-leg entry to terminate the move there. For judging the pseudo-legality of the
244 // 2nd leg, the from-square has to be considered empty, although the moving piece is still on it.
248 // alphabet "abcdefghijklmnopqrstuvwxyz"
249 char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N";
250 char xStep[] = "2110.130.102.10.00....0..2";
251 char yStep[] = "2132.133.313.20.11....1..3";
252 char dirType[] = "01000104000200000260050000";
253 char upgrade[] = "AFCD.BGH.JQL.NO.KW....R..Z";
254 char rotate[] = "DRCA.WHG.JKL.NO.QB....F..Z";
256 // 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 "
257 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 };
258 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 };
259 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 };
260 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 };
262 int rot[][4] = { // rotation matrices for each direction
274 OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR cl)
280 MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, char *desc, MoveCallback cb, VOIDSTAR cl)
282 char buf[80], *p = desc, *atom = NULL;
283 int mine, his, dir, bit, occup, i, ep, promoRank = -1;
284 ChessMove promo= NormalMove; ChessSquare pc = board[r][f];
285 if(pc == DarkSquare) return; // this is not a piece, but a 'hole' in the board
286 if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
287 if(pc == WhitePawn || pc == WhiteLance) promo = WhitePromotion, promoRank = BOARD_HEIGHT-1; else
288 if(pc == BlackPawn || pc == BlackLance) promo = BlackPromotion, promoRank = 0;
289 while(*p) { // more moves to go
290 int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0;
292 while(*p == 'i') initial++, desc = ++p;
293 while(islower(*p)) p++; // skip prefixes
294 if(!isupper(*p)) return; // syntax error: no atom
295 dx = xStep[*p-'A'] - '0';// step vector of atom
296 dy = yStep[*p-'A'] - '0';
297 dirSet = 0; // build direction set based on atom symmetry
298 switch(symmetry[*p-'A']) {
299 case 'B': expo = 0; // bishop, slide
300 case 'F': all = 0xAA; // diagonal atom (degenerate 4-fold)
301 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
302 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
303 int b = dirs1[*desc-'a']; // use wide version
304 if( islower(desc[1]) &&
305 ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
306 b = dirs1[*desc-'a'] & dirs1[desc[1]-'a']; // intersect wide & perp wide
311 dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
313 case 'R': expo = 0; // rook, slide
314 case 'W': all = 0x55; // orthogonal atom (non-deg 4-fold)
315 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
316 while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
317 dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
318 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
320 case 'N': all = 0xFF; // oblique atom (degenerate 8-fold)
321 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
322 if(*desc == 'h') { // chiral direction sets 'hr' and 'hl'
323 dirSet = (desc[1] == 'r' ? 0x55 : 0xAA); desc += 2;
325 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
326 int b = dirs2[*desc-'a']; // when alone, use narrow version
327 if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
328 else if(*desc == desc[1] || islower(desc[1]) && i < '4'
329 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
330 b = dirs1[*desc-'a'] & dirs2[desc[1]-'a']; // intersect wide & perp narrow
335 if(!dirSet) dirSet = 0xFF;
337 case 'Q': expo = 0; // queen, slide
338 case 'K': all = 0xFF; // non-deg (pseudo) 8-fold
340 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
341 int b = dirs4[*desc-'a']; // when alone, use narrow version
342 if(desc[1] == *desc) desc++; // doubling forces alone
343 else if(islower(desc[1]) && i < '4'
344 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
345 b = dirs3[*desc-'a'] & dirs3[desc[1]-'a']; // intersect wide & perp wide
350 if(!dirSet) dirSet = (tx < 0 ? 0xFF // default is all directions, but in continuation leg
351 : all == 0xFF ? 0xEF : 0x45); // omits backward, and for 4-fold atoms also diags
352 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
353 ds2 = dirSet & 0xAA; // extract diagonal directions
354 if(dirSet &= 0x55) // start with orthogonal moves, if present
355 retry = 1, dx = 0; // and schedule the diagonal moves for later
356 else dx = dy, dirSet = ds2; // if no orthogonal directions, do diagonal immediately
357 break; // should not have direction indicators
358 default: return; // syntax error: invalid atom
360 if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
361 mode = 0; // build mode mask
362 if(*desc == 'm') mode |= 4, desc++; // move to empty
363 if(*desc == 'c') mode |= his, desc++; // capture foe
364 if(*desc == 'd') mode |= mine, desc++; // destroy (capture friend)
365 if(*desc == 'e') mode |= 8, desc++; // e.p. capture last mover
366 if(*desc == 't') mode |= 16, desc++; // exclude enemies as hop platform ('test')
367 if(*desc == 'p') mode |= 32, desc++; // hop over occupied
368 if(*desc == 'g') mode |= 64, desc++; // hop and toggle range
369 if(*desc == 'o') mode |= 128, desc++; // wrap around cylinder board
370 if(*desc == 'y') mode |= 512, desc++; // toggle range on empty square
371 if(*desc == 'n') jump = 0, desc++; // non-jumping
372 while(*desc == 'j') jump++, desc++; // must jump (on B,R,Q: skip first square)
373 if(*desc == 'a') cont = ++desc; // move again after doing what preceded it
374 if(isdigit(*++p)) expo = atoi(p++); // read exponent
375 if(expo > 9) p++; // allow double-digit
376 desc = p; // this is start of next move
377 if(initial == 2) { if(board[r][f] != initialPosition[r-2*his+3][f]) continue; } else
378 if(initial && (board[r][f] != initialPosition[r][f] ||
379 r == 0 && board[TOUCHED_W] & 1<<f ||
380 r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f ) ) continue;
381 if(expo > 0 && dx == 0 && dy == 0) { // castling indicated by O + number
382 mode |= 1024; dy = 1;
384 if(expo < 0) expo = 1; // use 1 for default
386 if(!(mode & 15)) mode |= his + 4; // no mode spec, use default = mc
388 strncpy(buf, cont, 80); cont = buf; // copy next leg(s), so we can modify
389 atom = buf; while(islower(*atom)) atom++; // skip to atom
390 if(mode & 32) mode ^= 256 + 32; // in non-final legs 'p' means 'pass through'
391 if(mode & 64 + 512) {
392 mode |= 256; // and 'g' too, but converts leaper <-> slider
393 if(mode & 512) mode ^= 0x304; // and 'y' is m-like 'g'
394 *atom = upgrade[*atom-'A']; // replace atom, BRQ <-> FWK
395 atom[1] = atom[2] = '\0'; // make sure any old range is stripped off
396 if(expo == 1) atom[1] = '0'; // turn other leapers into riders
398 if(!(mode & 0x30F)) mode |= 4; // and default of this leg = m
400 if(dy == 1) skip = jump - 1, jump = 1; // on W & F atoms 'j' = skip first square
402 for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
403 int i = expo, j = skip, hop = mode, vx, vy, loop = 0;
404 if(!(bit & dirSet)) continue; // does not move in this direction
405 if(dy != 1 || mode & 1024) j = 0; //
406 vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector
407 vy = dx*rot[dir][2] + dy*rot[dir][3];
408 if(tx < 0) x = f, y = r; // start square
409 else x = tx, y = ty; // from previous to-square if continuation
411 x += vx; y += vy; // step to next square
412 if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
413 if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT, loop++; else break; }
414 if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT, loop++; else break; }
415 if(j) { j--; continue; } // skip irrespective of occupation
416 if(board[y][x] == DarkSquare) break; // black squares are supposed to be off board
417 if(!jump && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
418 if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
419 if(x == f && y == r && !loop) occup = 4; else // start square counts as empty (if not around cylinder!)
420 if(board[y][x] < BlackPawn) occup = 0x101; else
421 if(board[y][x] < EmptySquare) occup = 0x102; else
423 if(cont) { // non-final leg
424 if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode
425 if(occup & mode) { // valid intermediate square, do continuation
426 char origAtom = *atom;
427 if(!(bit & all)) *atom = rotate[*atom - 'A']; // orth-diag interconversion to make direction valid
428 if(occup & mode & 0x104) // no side effects, merge legs to one move
429 MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
430 if(occup & mode & 3 && (killX < 0 || killX == x && killY == y)) { // destructive first leg
432 MovesFromString(board, flags, f, r, x, y, dir, cont, &OK, &cnt); // count possible continuations
433 if(cnt) { // and if there are
434 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); // then generate their first leg
436 MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
440 *atom = origAtom; // undo any interconversion
442 if(occup != 4) break; // occupied squares always terminate the leg
445 if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper
447 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)
448 cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
450 if(mode & 1024) { // castling
451 i = 2; // kludge to elongate move indefinitely
452 if(occup == 4) continue; // skip empty squares
453 if((x == BOARD_LEFT + skip || x > BOARD_LEFT + skip && vx < 0 && board[y][x-1-skip] == DarkSquare)
454 && board[y][x] == initialPosition[y][x]) { // reached initial corner piece
455 if(pc != WhiteKing && pc != BlackKing || expo == 1) { // non-royal castling (to be entered as two-leg move via 'Rook')
456 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX < f)
457 legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f - expo, cl), legNr >>= 1;
459 cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
461 if((x == BOARD_RGHT-1-skip || x < BOARD_RGHT-1-skip && vx > 0 && board[y][x+1+skip] == DarkSquare)
462 && board[y][x] == initialPosition[y][x]) {
463 if(pc != WhiteKing && pc != BlackKing || expo == 1) {
464 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX > f)
465 legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f + expo, cl), legNr >>= 1;
467 cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
471 if(mode & 16 && (board[y][x] == WhiteKing || board[y][x] == BlackKing)) break; // tame piece, cannot capture royal
472 if(occup & mode) cb(board, flags, y == promoRank ? promo : NormalMove, r, f, y, x, cl); // allowed, generate
473 if(occup != 4) break; // not valid transit square
476 dx = dy; dirSet = ds2; // prepare for diagonal moves of K,Q
477 } while(retry-- && ds2); // and start doing them
478 if(tx >= 0) break; // don't do other atoms in continuation legs
482 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
485 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
490 if (rt >= BOARD_HEIGHT) break;
491 if (SameColor(board[rf][ff], board[rt][ft])) break;
492 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
493 if (board[rt][ft] != EmptySquare) break;
498 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
504 if (SameColor(board[rf][ff], board[rt][ft])) break;
505 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
506 if (board[rt][ft] != EmptySquare) break;
511 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
513 SlideForward(board, flags, rf, ff, callback, closure);
514 SlideBackward(board, flags, rf, ff, callback, closure);
518 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
520 int i, s, rt = rf, ft;
521 for(s = -1; s <= 1; s+= 2) {
524 if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
525 if (SameColor(board[rf][ff], board[rt][ft])) break;
526 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
527 if (board[rt][ft] != EmptySquare) break;
533 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
536 for(s = -1; s <= 1; s+= 2) {
540 if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
541 if (SameColor(board[rf][ff], board[rt][ft])) break;
542 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
543 if (board[rt][ft] != EmptySquare) break;
549 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
552 for(s = -1; s <= 1; s+= 2) {
556 if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
557 if (SameColor(board[rf][ff], board[rt][ft])) break;
558 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
559 if (board[rt][ft] != EmptySquare) break;
565 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
567 SlideVertical(board, flags, rf, ff, callback, closure);
568 SlideSideways(board, flags, rf, ff, callback, closure);
572 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
574 SlideDiagForward(board, flags, rf, ff, callback, closure);
575 SlideDiagBackward(board, flags, rf, ff, callback, closure);
579 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
580 { // Lion-like move of Horned Falcon and Souring Eagle
581 int ft = ff + dx, rt = rf + dy;
582 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
584 if (!SameColor(board[rf][ff], board[rt][ft]))
585 callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
588 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
590 if (!SameColor(board[rf][ff], board[rt][ft]))
591 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
592 if (!SameColor(board[rf][ff], board[rf+dy][ff+dx]))
593 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
598 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
600 int ft = ff, rt = rf + 1;
601 if (rt >= BOARD_HEIGHT) return;
602 if (SameColor(board[rf][ff], board[rt][ft])) return;
603 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
607 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
609 int ft = ff, rt = rf - 1;
611 if (SameColor(board[rf][ff], board[rt][ft])) return;
612 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
616 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
620 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
621 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
623 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
624 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
628 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
631 if (rt >= BOARD_HEIGHT) return;
633 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
634 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
636 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
637 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
641 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
646 if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
647 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
649 if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
650 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
654 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
656 StepForward(board, flags, rf, ff, callback, closure);
657 StepBackward(board, flags, rf, ff, callback, closure);
661 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
663 StepDiagForward(board, flags, rf, ff, callback, closure);
664 StepDiagBackward(board, flags, rf, ff, callback, closure);
668 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
670 StepVertical(board, flags, rf, ff, callback, closure);
671 StepSideways(board, flags, rf, ff, callback, closure);
675 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
678 for (i = -1; i <= 1; i += 2)
679 for (j = -1; j <= 1; j += 2)
680 for (s = 1; s <= 2; s++) {
683 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
684 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
685 && !SameColor(board[rf][ff], board[rt][ft]))
686 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
690 /* Call callback once for each pseudo-legal move in the given
691 position, except castling moves. A move is pseudo-legal if it is
692 legal, or if it would be legal except that it leaves the king in
693 check. In the arguments, epfile is EP_NONE if the previous move
694 was not a double pawn push, or the file 0..7 if it was, or
695 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
696 Promotion moves generated are to Queen only.
699 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
700 // speed: only do moves with this piece type
703 int i, j, d, s, fs, rs, rt, ft, m;
704 int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
705 int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
707 for (rf = 0; rf < BOARD_HEIGHT; rf++)
708 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
711 if(board[rf][ff] == EmptySquare) continue;
712 if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
713 m = 0; piece = board[rf][ff];
714 if(PieceToChar(piece) == '~')
715 piece = (ChessSquare) ( DEMOTED(piece) );
716 if(filter != EmptySquare && piece != filter) continue;
717 if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
718 MovesFromString(board, flags, ff, rf, -1, -1, 0, pieceDesc[piece], callback, closure);
721 if(IS_SHOGI(gameInfo.variant))
722 piece = (ChessSquare) ( SHOGI piece );
724 switch ((int)piece) {
725 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
727 /* can't happen ([HGM] except for faries...) */
731 if(gameInfo.variant == VariantXiangqi) {
732 /* [HGM] capture and move straight ahead in Xiangqi */
733 if (rf < BOARD_HEIGHT-1 &&
734 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
735 callback(board, flags, NormalMove,
736 rf, ff, rf + 1, ff, closure);
738 /* and move sideways when across the river */
739 for (s = -1; s <= 1; s += 2) {
740 if (rf >= BOARD_HEIGHT>>1 &&
741 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
742 !WhitePiece(board[rf][ff+s]) ) {
743 callback(board, flags, NormalMove,
744 rf, ff, rf, ff+s, closure);
749 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
750 callback(board, flags,
751 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
752 rf, ff, rf + 1, ff, closure);
754 if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
755 gameInfo.variant != VariantShatranj && /* [HGM] */
756 gameInfo.variant != VariantCourier && /* [HGM] */
757 board[rf+2][ff] == EmptySquare ) {
758 callback(board, flags, NormalMove,
759 rf, ff, rf+2, ff, closure);
761 for (s = -1; s <= 1; s += 2) {
762 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
763 ((flags & F_KRIEGSPIEL_CAPTURE) ||
764 BlackPiece(board[rf + 1][ff + s]))) {
765 callback(board, flags,
766 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
767 rf, ff, rf + 1, ff + s, closure);
769 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
770 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
771 (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
772 board[rf][ff + s] == BlackPawn &&
773 board[rf+1][ff + s] == EmptySquare) {
774 callback(board, flags, WhiteCapturesEnPassant,
775 rf, ff, rf+1, ff + s, closure);
782 if(gameInfo.variant == VariantXiangqi) {
783 /* [HGM] capture straight ahead in Xiangqi */
784 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
785 callback(board, flags, NormalMove,
786 rf, ff, rf - 1, ff, closure);
788 /* and move sideways when across the river */
789 for (s = -1; s <= 1; s += 2) {
790 if (rf < BOARD_HEIGHT>>1 &&
791 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
792 !BlackPiece(board[rf][ff+s]) ) {
793 callback(board, flags, NormalMove,
794 rf, ff, rf, ff+s, closure);
799 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
800 callback(board, flags,
801 rf <= promoRank ? BlackPromotion : NormalMove,
802 rf, ff, rf - 1, ff, closure);
804 if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
805 gameInfo.variant != VariantShatranj && /* [HGM] */
806 gameInfo.variant != VariantCourier && /* [HGM] */
807 board[rf-2][ff] == EmptySquare) {
808 callback(board, flags, NormalMove,
809 rf, ff, rf-2, ff, closure);
811 for (s = -1; s <= 1; s += 2) {
812 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
813 ((flags & F_KRIEGSPIEL_CAPTURE) ||
814 WhitePiece(board[rf - 1][ff + s]))) {
815 callback(board, flags,
816 rf <= promoRank ? BlackPromotion : NormalMove,
817 rf, ff, rf - 1, ff + s, closure);
819 if (rf < BOARD_HEIGHT>>1) {
820 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
821 (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
822 board[rf][ff + s] == WhitePawn &&
823 board[rf-1][ff + s] == EmptySquare) {
824 callback(board, flags, BlackCapturesEnPassant,
825 rf, ff, rf-1, ff + s, closure);
835 for (i = -1; i <= 1; i += 2)
836 for (j = -1; j <= 1; j += 2)
837 for (s = 1; s <= 2; s++) {
840 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
841 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
842 && !SameColor(board[rf][ff], board[rt][ft]))
843 callback(board, flags, NormalMove,
844 rf, ff, rt, ft, closure);
848 case SHOGI WhiteKnight:
849 for (s = -1; s <= 1; s += 2) {
850 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
851 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
852 callback(board, flags, NormalMove,
853 rf, ff, rf + 2, ff + s, closure);
858 case SHOGI BlackKnight:
859 for (s = -1; s <= 1; s += 2) {
860 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
861 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
862 callback(board, flags, NormalMove,
863 rf, ff, rf - 2, ff + s, closure);
870 for (d = 0; d <= 1; d++)
871 for (s = -1; s <= 1; s += 2) {
874 rt = rf + (i * s) * d;
875 ft = ff + (i * s) * (1 - d);
876 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
877 if (m == 0 && board[rt][ft] == EmptySquare)
878 callback(board, flags, NormalMove,
879 rf, ff, rt, ft, closure);
880 if (m == 1 && board[rt][ft] != EmptySquare &&
881 !SameColor(board[rf][ff], board[rt][ft]) )
882 callback(board, flags, NormalMove,
883 rf, ff, rt, ft, closure);
884 if (board[rt][ft] != EmptySquare && m++) break;
889 /* Gold General (and all its promoted versions) . First do the */
890 /* diagonal forward steps, then proceed as normal Wazir */
891 case SHOGI (PROMO WhitePawn):
892 if(gameInfo.variant == VariantShogi) goto WhiteGold;
893 case SHOGI (PROMO BlackPawn):
894 if(gameInfo.variant == VariantShogi) goto BlackGold;
895 SlideVertical(board, flags, rf, ff, callback, closure);
898 case SHOGI (PROMO WhiteKnight):
899 if(gameInfo.variant == VariantShogi) goto WhiteGold;
900 case SHOGI BlackDrunk:
901 case SHOGI BlackAlfil:
902 Ferz(board, flags, rf, ff, callback, closure);
903 StepSideways(board, flags, rf, ff, callback, closure);
904 StepBackward(board, flags, rf, ff, callback, closure);
907 case SHOGI (PROMO BlackKnight):
908 if(gameInfo.variant == VariantShogi) goto BlackGold;
909 case SHOGI WhiteDrunk:
910 case SHOGI WhiteAlfil:
911 Ferz(board, flags, rf, ff, callback, closure);
912 StepSideways(board, flags, rf, ff, callback, closure);
913 StepForward(board, flags, rf, ff, callback, closure);
919 if(gameInfo.variant == VariantShogi) goto BlackGold;
920 SlideVertical(board, flags, rf, ff, callback, closure);
921 Ferz(board, flags, rf, ff, callback, closure);
922 StepSideways(board, flags, rf, ff, callback, closure);
925 case SHOGI (PROMO WhiteQueen):
926 case SHOGI WhiteTokin:
927 case SHOGI WhiteWazir:
929 StepDiagForward(board, flags, rf, ff, callback, closure);
930 Wazir(board, flags, rf, ff, callback, closure);
933 case SHOGI (PROMO BlackQueen):
934 case SHOGI BlackTokin:
935 case SHOGI BlackWazir:
937 StepDiagBackward(board, flags, rf, ff, callback, closure);
938 Wazir(board, flags, rf, ff, callback, closure);
943 Wazir(board, flags, rf, ff, callback, closure);
946 case SHOGI WhiteMarshall:
947 case SHOGI BlackMarshall:
948 Ferz(board, flags, rf, ff, callback, closure);
949 for (d = 0; d <= 1; d++)
950 for (s = -2; s <= 2; s += 4) {
952 ft = ff + s * (1 - d);
953 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
954 if (!SameColor(board[rf][ff], board[rt][ft]) )
955 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
959 case SHOGI WhiteAngel:
960 case SHOGI BlackAngel:
961 Wazir(board, flags, rf, ff, callback, closure);
965 /* [HGM] support Shatranj pieces */
966 for (rs = -1; rs <= 1; rs += 2)
967 for (fs = -1; fs <= 1; fs += 2) {
970 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
971 && ( gameInfo.variant != VariantXiangqi ||
972 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
974 && !SameColor(board[rf][ff], board[rt][ft]))
975 callback(board, flags, NormalMove,
976 rf, ff, rt, ft, closure);
977 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
978 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
979 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
981 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
982 && !SameColor(board[rf][ff], board[rt][ft]))
983 callback(board, flags, NormalMove,
984 rf, ff, rt, ft, closure);
986 if(gameInfo.variant == VariantSpartan)
987 for(fs = -1; fs <= 1; fs += 2) {
989 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
990 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
994 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
997 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
998 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
999 for (s = -2; s <= 2; s += 4) {
1001 ft = ff + s * (1 - d);
1002 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1003 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1004 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1007 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
1008 case SHOGI WhiteCardinal:
1009 case SHOGI BlackCardinal:
1010 case SHOGI WhitePCardinal:
1011 case SHOGI BlackPCardinal:
1013 Bishop(board, flags, rf, ff, callback, closure);
1014 Wazir(board, flags, rf, ff, callback, closure);
1017 /* Capablanca Archbishop continues as Knight */
1020 Knight(board, flags, rf, ff, callback, closure);
1022 /* Shogi Bishops are ordinary Bishops */
1023 case SHOGI WhiteBishop:
1024 case SHOGI BlackBishop:
1025 case SHOGI WhitePBishop:
1026 case SHOGI BlackPBishop:
1029 Bishop(board, flags, rf, ff, callback, closure);
1032 /* Shogi Lance is unlike anything, and asymmetric at that */
1033 case SHOGI WhiteQueen:
1034 if(gameInfo.variant == VariantChu) goto doQueen;
1038 if (rt >= BOARD_HEIGHT) break;
1039 if (SameColor(board[rf][ff], board[rt][ft])) break;
1040 callback(board, flags, NormalMove,
1041 rf, ff, rt, ft, closure);
1042 if (board[rt][ft] != EmptySquare) break;
1046 case SHOGI BlackQueen:
1047 if(gameInfo.variant == VariantChu) goto doQueen;
1052 if (SameColor(board[rf][ff], board[rt][ft])) break;
1053 callback(board, flags, NormalMove,
1054 rf, ff, rt, ft, closure);
1055 if (board[rt][ft] != EmptySquare) break;
1059 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
1062 if(gameInfo.variant == VariantChuChess) goto DragonKing;
1063 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1064 for (s = -2; s <= 2; s += 4) {
1066 ft = ff + s * (1 - d);
1067 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1068 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
1069 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1070 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1072 if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
1073 Wazir(board, flags, rf, ff, callback, closure);
1075 Rook(board, flags, rf, ff, callback, closure);
1078 /* Shogi Dragon King has to continue as Ferz after Rook moves */
1079 case SHOGI WhiteDragon:
1080 case SHOGI BlackDragon:
1081 case SHOGI WhitePDragon:
1082 case SHOGI BlackPDragon:
1084 Rook(board, flags, rf, ff, callback, closure);
1085 Ferz(board, flags, rf, ff, callback, closure);
1089 /* Capablanca Chancellor sets flag to continue as Knight */
1092 Rook(board, flags, rf, ff, callback, closure);
1093 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
1094 Ferz(board, flags, rf, ff, callback, closure);
1096 Knight(board, flags, rf, ff, callback, closure);
1099 /* Shogi Rooks are ordinary Rooks */
1100 case SHOGI WhiteRook:
1101 case SHOGI BlackRook:
1102 case SHOGI WhitePRook:
1103 case SHOGI BlackPRook:
1106 Rook(board, flags, rf, ff, callback, closure);
1111 case SHOGI WhiteMother:
1112 case SHOGI BlackMother:
1114 Rook(board, flags, rf, ff, callback, closure);
1115 Bishop(board, flags, rf, ff, callback, closure);
1118 case SHOGI WhitePawn:
1119 StepForward(board, flags, rf, ff, callback, closure);
1122 case SHOGI BlackPawn:
1123 StepBackward(board, flags, rf, ff, callback, closure);
1127 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1128 case SHOGI WhiteFerz:
1129 Ferz(board, flags, rf, ff, callback, closure);
1130 StepForward(board, flags, rf, ff, callback, closure);
1134 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1135 case SHOGI BlackFerz:
1136 StepBackward(board, flags, rf, ff, callback, closure);
1140 /* [HGM] support Shatranj pieces */
1141 Ferz(board, flags, rf, ff, callback, closure);
1146 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
1149 case SHOGI WhiteMonarch:
1150 case SHOGI BlackMonarch:
1151 case SHOGI WhiteKing:
1152 case SHOGI BlackKing:
1155 Ferz(board, flags, rf, ff, callback, closure);
1156 Wazir(board, flags, rf, ff, callback, closure);
1159 case WhiteNightrider:
1160 case BlackNightrider:
1161 for (i = -1; i <= 1; i += 2)
1162 for (j = -1; j <= 1; j += 2)
1163 for (s = 1; s <= 2; s++) { int k;
1166 ft = ff + k*j*(3-s);
1167 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
1168 if (SameColor(board[rf][ff], board[rt][ft])) break;
1169 callback(board, flags, NormalMove,
1170 rf, ff, rt, ft, closure);
1171 if (board[rt][ft] != EmptySquare) break;
1177 Bishop(board, flags, rf, ff, callback, closure);
1178 Rook(board, flags, rf, ff, callback, closure);
1179 Knight(board, flags, rf, ff, callback, closure);
1182 // Use Lance as Berolina / Spartan Pawn.
1184 if(gameInfo.variant == VariantSuper) goto Amazon;
1185 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1186 callback(board, flags,
1187 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1188 rf, ff, rf + 1, ff, closure);
1189 for (s = -1; s <= 1; s += 2) {
1190 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1191 callback(board, flags,
1192 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1193 rf, ff, rf + 1, ff + s, closure);
1194 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1195 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1200 if(gameInfo.variant == VariantSuper) goto Amazon;
1201 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1202 callback(board, flags,
1203 rf <= promoRank ? BlackPromotion : NormalMove,
1204 rf, ff, rf - 1, ff, closure);
1205 for (s = -1; s <= 1; s += 2) {
1206 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1207 callback(board, flags,
1208 rf <= promoRank ? BlackPromotion : NormalMove,
1209 rf, ff, rf - 1, ff + s, closure);
1210 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1211 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1215 case SHOGI WhiteNothing:
1216 case SHOGI BlackNothing:
1217 case SHOGI WhiteLion:
1218 case SHOGI BlackLion:
1221 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1222 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1223 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1224 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
1225 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
1226 rf, ff, rt, ft, closure);
1231 case SHOGI WhiteFalcon:
1232 case SHOGI BlackFalcon:
1233 case SHOGI WhitePDagger:
1234 case SHOGI BlackPDagger:
1235 SlideSideways(board, flags, rf, ff, callback, closure);
1236 StepVertical(board, flags, rf, ff, callback, closure);
1239 case SHOGI WhiteCobra:
1240 case SHOGI BlackCobra:
1241 StepVertical(board, flags, rf, ff, callback, closure);
1244 case SHOGI (PROMO WhiteFerz):
1245 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1246 case SHOGI (PROMO BlackFerz):
1247 if(gameInfo.variant == VariantShogi) goto BlackGold;
1248 case SHOGI WhitePSword:
1249 case SHOGI BlackPSword:
1250 SlideVertical(board, flags, rf, ff, callback, closure);
1251 StepSideways(board, flags, rf, ff, callback, closure);
1254 case SHOGI WhiteUnicorn:
1255 case SHOGI BlackUnicorn:
1256 Ferz(board, flags, rf, ff, callback, closure);
1257 StepVertical(board, flags, rf, ff, callback, closure);
1260 case SHOGI WhiteMan:
1261 StepDiagForward(board, flags, rf, ff, callback, closure);
1262 StepVertical(board, flags, rf, ff, callback, closure);
1265 case SHOGI BlackMan:
1266 StepDiagBackward(board, flags, rf, ff, callback, closure);
1267 StepVertical(board, flags, rf, ff, callback, closure);
1270 case SHOGI WhiteHCrown:
1271 case SHOGI BlackHCrown:
1272 Bishop(board, flags, rf, ff, callback, closure);
1273 SlideSideways(board, flags, rf, ff, callback, closure);
1276 case SHOGI WhiteCrown:
1277 case SHOGI BlackCrown:
1278 Bishop(board, flags, rf, ff, callback, closure);
1279 SlideVertical(board, flags, rf, ff, callback, closure);
1282 case SHOGI WhiteCat:
1283 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1284 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1285 if(killX >= 0) break;
1286 Bishop(board, flags, rf, ff, callback, closure);
1287 SlideSideways(board, flags, rf, ff, callback, closure);
1288 SlideBackward(board, flags, rf, ff, callback, closure);
1291 case SHOGI BlackCat:
1292 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1293 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1294 if(killX >= 0) break;
1295 Bishop(board, flags, rf, ff, callback, closure);
1296 SlideSideways(board, flags, rf, ff, callback, closure);
1297 SlideForward(board, flags, rf, ff, callback, closure);
1300 case SHOGI WhiteDagger:
1301 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1302 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1303 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1304 if(killX >= 0) break;
1305 Rook(board, flags, rf, ff, callback, closure);
1306 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1309 case SHOGI BlackDagger:
1310 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1311 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1312 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1313 if(killX >= 0) break;
1314 Rook(board, flags, rf, ff, callback, closure);
1315 SlideDiagForward(board, flags, rf, ff, callback, closure);
1318 case SHOGI WhiteDolphin:
1319 case SHOGI BlackHorse:
1320 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1321 SlideVertical(board, flags, rf, ff, callback, closure);
1324 case SHOGI BlackDolphin:
1325 case SHOGI WhiteHorse:
1326 SlideDiagForward(board, flags, rf, ff, callback, closure);
1327 SlideVertical(board, flags, rf, ff, callback, closure);
1330 case SHOGI WhiteLance:
1331 SlideForward(board, flags, rf, ff, callback, closure);
1334 case SHOGI BlackLance:
1335 SlideBackward(board, flags, rf, ff, callback, closure);
1338 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1342 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1355 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1356 Board xqCheckers, nullBoard;
1358 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1359 int rf, int ff, int rt, int ft,
1363 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1365 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1367 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1369 if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1371 if (!(flags & F_IGNORE_CHECK) ) {
1372 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1375 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1376 kings += (board[r][f] == BlackKing);
1380 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1382 check = CheckTest(board, flags, rf, ff, rt, ft,
1383 kind == WhiteCapturesEnPassant ||
1384 kind == BlackCapturesEnPassant);
1385 if(promo) board[rf][ff] = BlackLance;
1388 if (flags & F_ATOMIC_CAPTURE) {
1389 if (board[rt][ft] != EmptySquare ||
1390 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1392 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1393 if (board[rf][ff] == king) return;
1394 for (r = rt-1; r <= rt+1; r++) {
1395 for (f = ft-1; f <= ft+1; f++) {
1396 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1397 board[r][f] == king) return;
1402 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1409 int captures; // [HGM] losers
1410 } LegalityTestClosure;
1413 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1414 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1415 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1416 moves that would destroy your own king. The CASTLE_OK flags are
1417 true if castling is not yet ruled out by a move of the king or
1418 rook. Return TRUE if the player on move is currently in check and
1419 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1421 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1424 int ff, ft, k, left, right, swap;
1425 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1426 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1427 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1432 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1433 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1434 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1436 if (inCheck) return TRUE;
1438 /* Generate castling moves */
1439 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1440 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1443 p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1444 if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1446 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1447 if ((flags & F_WHITE_ON_MOVE) &&
1448 (flags & F_WHITE_KCASTLE_OK) &&
1449 board[0][ff] == wKing &&
1450 board[0][ff + 1] == EmptySquare &&
1451 board[0][ff + 2] == EmptySquare &&
1452 board[0][BOARD_RGHT-3] == EmptySquare &&
1453 board[0][BOARD_RGHT-2] == EmptySquare &&
1454 board[0][BOARD_RGHT-1] == WhiteRook &&
1455 castlingRights[0] != NoRights && /* [HGM] check rights */
1456 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1458 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1459 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1460 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1461 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1463 callback(board, flags,
1464 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1465 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1467 if ((flags & F_WHITE_ON_MOVE) &&
1468 (flags & F_WHITE_QCASTLE_OK) &&
1469 board[0][ff] == wKing &&
1470 board[0][ff - 1] == EmptySquare &&
1471 board[0][ff - 2] == EmptySquare &&
1472 board[0][BOARD_LEFT+2] == EmptySquare &&
1473 board[0][BOARD_LEFT+1] == EmptySquare &&
1474 board[0][BOARD_LEFT+0] == WhiteRook &&
1475 castlingRights[1] != NoRights && /* [HGM] check rights */
1476 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1478 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1479 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1480 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1482 callback(board, flags,
1483 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1484 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1486 if (!(flags & F_WHITE_ON_MOVE) &&
1487 (flags & F_BLACK_KCASTLE_OK) &&
1488 board[BOARD_HEIGHT-1][ff] == bKing &&
1489 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1490 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1491 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1492 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1493 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1494 castlingRights[3] != NoRights && /* [HGM] check rights */
1495 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1497 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1498 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1499 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1500 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1502 callback(board, flags,
1503 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1504 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1506 if (!(flags & F_WHITE_ON_MOVE) &&
1507 (flags & F_BLACK_QCASTLE_OK) &&
1508 board[BOARD_HEIGHT-1][ff] == bKing &&
1509 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1510 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1511 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1512 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1513 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1514 castlingRights[4] != NoRights && /* [HGM] check rights */
1515 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1517 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1518 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1519 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1521 callback(board, flags,
1522 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1523 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1527 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1529 /* generate all potential FRC castling moves (KxR), ignoring flags */
1530 /* [HGM] test if the Rooks we find have castling rights */
1531 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1534 if ((flags & F_WHITE_ON_MOVE) != 0) {
1535 ff = castlingRights[2]; /* King file if we have any rights */
1536 if(ff != NoRights && board[0][ff] == WhiteKing) {
1537 if (appData.debugMode) {
1538 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1539 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1541 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1543 right = BOARD_RGHT-2;
1544 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1545 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1546 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1547 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1548 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1549 if(ft != NoRights && board[0][ft] == WhiteRook) {
1550 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1551 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1554 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1555 left = BOARD_LEFT+2;
1557 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1558 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1559 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1560 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1561 if(ff > BOARD_LEFT+2)
1562 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1563 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1564 if(ft != NoRights && board[0][ft] == WhiteRook) {
1565 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1566 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1570 ff = castlingRights[5]; /* King file if we have any rights */
1571 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1572 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1574 right = BOARD_RGHT-2;
1575 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1576 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1577 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1578 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1579 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1580 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1581 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1582 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1585 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1586 left = BOARD_LEFT+2;
1588 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1589 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1590 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1591 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1592 if(ff > BOARD_LEFT+2)
1593 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1594 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1595 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1596 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1597 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1614 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1615 int rf, int ff, int rt, int ft,
1620 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1622 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1624 if (rt == cl->rking && ft == cl->fking) {
1625 if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1627 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1629 if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1630 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1631 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1635 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1636 he leave himself in check? Or if rf == -1, is the player on move
1637 in check now? enPassant must be TRUE if the indicated move is an
1638 e.p. capture. The possibility of castling out of a check along the
1639 back rank is not accounted for (i.e., we still return nonzero), as
1640 this is illegal anyway. Return value is the number of times the
1641 king is in check. */
1643 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1645 CheckTestClosure cl;
1646 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1647 ChessSquare captured = EmptySquare, ep=0, trampled=0;
1648 int saveKill = killX;
1649 /* Suppress warnings on uninitialized variables */
1651 if(gameInfo.variant == VariantXiangqi)
1652 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1653 if(gameInfo.variant == VariantKnightmate)
1654 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1655 if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check
1656 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1657 if(gameInfo.variant == VariantShogi) prince -= 11; // White/BlackFalcon
1658 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1659 if(board[r][f] == k || board[r][f] == prince) {
1660 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1661 king = board[r][f]; // remember hich one we had
1668 captured = board[rf][ft];
1669 board[rf][ft] = EmptySquare;
1671 captured = board[rt][ft];
1672 if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; }
1674 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1675 board[rt][ft] = board[rf][ff];
1676 if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
1678 ep = board[EP_STATUS];
1679 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1680 ChessSquare victim = saveKill < 0 ? EmptySquare : trampled;
1681 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1682 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1683 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn // no or worthless 'bridge'
1684 || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between)
1685 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1689 /* For compatibility with ICS wild 9, we scan the board in the
1690 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1691 and we test only whether that one is in check. */
1692 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1693 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1694 if (board[cl.rking][cl.fking] == king) {
1696 if(gameInfo.variant == VariantXiangqi) {
1697 /* [HGM] In Xiangqi opposing Kings means check as well */
1699 dir = (king >= BlackPawn) ? -1 : 1;
1700 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1701 board[i][cl.fking] == EmptySquare; i+=dir );
1702 if(i>=0 && i<BOARD_HEIGHT &&
1703 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1706 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1707 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1708 goto undo_move; /* 2-level break */
1715 if(rf != DROP_RANK) // [HGM] drop
1716 board[rf][ff] = board[rt][ft];
1718 board[rf][ft] = captured;
1719 board[rt][ft] = EmptySquare;
1721 if(saveKill >= 0) board[killY][killX = saveKill] = trampled;
1722 board[rt][ft] = captured;
1724 board[EP_STATUS] = ep;
1727 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1731 HasLion (Board board, int flags)
1733 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1735 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1736 if(board[r][f] == lion) return 1;
1741 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1742 { // [HGM] put drop legality testing in separate routine for clarity
1744 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1745 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1746 n = PieceToNumber(piece);
1747 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1748 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1749 return ImpossibleMove; // piece not available
1750 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1751 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1752 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1753 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1754 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1755 if(piece == WhitePawn || piece == BlackPawn) {
1756 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1757 for(r=1; r<BOARD_HEIGHT-1; r++)
1758 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1759 // should still test if we mate with this Pawn
1761 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1762 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1764 if( (piece == WhitePawn || piece == BlackPawn) &&
1765 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1766 return IllegalMove; /* no pawn drops on 1st/8th */
1768 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1769 if (!(flags & F_IGNORE_CHECK) &&
1770 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1771 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1774 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1775 int rf, int ff, int rt, int ft,
1779 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1781 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1783 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1784 cl->captures++; // [HGM] losers: count legal captures
1785 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1790 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1792 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1794 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1795 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1796 piece = filterPiece = board[rf][ff];
1797 if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece);
1799 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1800 /* (perhaps we should disallow moves that obviously leave us in check?) */
1801 if((piece == WhiteFalcon || piece == BlackFalcon ||
1802 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
1803 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1807 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1808 cl.ft = fFilter = ft;
1809 cl.kind = IllegalMove;
1810 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1811 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1812 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1813 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1814 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1815 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1817 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1818 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1819 if(board[rf][ff] < BlackPawn) { // white
1820 if(rf != 0) return IllegalMove; // must be on back rank
1821 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1822 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1823 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1824 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1826 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1827 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1828 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1829 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1830 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1833 if(gameInfo.variant == VariantChu) {
1834 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1835 if(promoChar != '+')
1836 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1837 if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') {
1838 if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.')
1839 return ImpossibleMove;
1841 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1843 if(gameInfo.variant == VariantShogi) {
1844 /* [HGM] Shogi promotions. '=' means defer */
1845 if(rf != DROP_RANK && cl.kind == NormalMove) {
1846 ChessSquare piece = board[rf][ff];
1847 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
1849 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1850 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1851 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1852 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1853 promoChar = '+'; // allowed ICS notations
1854 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1855 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1856 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1857 else if(flags & F_WHITE_ON_MOVE) {
1858 if( (int) piece < (int) WhiteWazir &&
1859 (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) {
1860 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1861 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1862 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1863 else /* promotion optional, default is defer */
1864 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1865 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1867 if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) {
1868 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1869 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1870 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1871 else /* promotion optional, default is defer */
1872 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1873 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1877 if (promoChar != NULLCHAR) {
1878 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1879 ChessSquare piece = board[rf][ff];
1880 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1881 // should test if in zone, really
1882 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1884 if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1886 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1887 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1888 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1889 if(piece == EmptySquare)
1890 cl.kind = ImpossibleMove; // non-existing piece
1891 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1892 cl.kind = IllegalMove; // no two Lions
1893 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1894 if(promoChar != PieceToChar(BlackKing)) {
1895 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1896 if(piece == BlackLance) cl.kind = ImpossibleMove;
1897 } else { // promotion to King allowed only if we do not have two yet
1898 int r, f, kings = 0;
1899 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1900 if(kings == 2) cl.kind = IllegalMove;
1902 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1903 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1904 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1905 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1906 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1907 cl.kind = IllegalMove; // promotion to King usually not allowed
1909 cl.kind = IllegalMove;
1919 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1920 int rf, int ff, int rt, int ft,
1924 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1926 register MateTestClosure *cl = (MateTestClosure *) closure;
1931 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1933 MateTest (Board board, int flags)
1936 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1937 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1939 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1940 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1941 nrKing += (board[r][f] == king); // stm has king
1942 if( board[r][f] != EmptySquare ) {
1943 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1948 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1949 case VariantShatranj:
1950 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1954 if(nrKing == 0) return MT_NOKING;
1957 if(myPieces == 1) return MT_BARE;
1960 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1961 // [HGM] 3check: yet to do!
1963 return inCheck ? MT_CHECK : MT_NONE;
1965 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1966 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1967 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1968 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1969 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1970 if(board[n][holdings] != EmptySquare) {
1971 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1972 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1975 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1976 return myPieces == hisPieces ? MT_STALEMATE :
1977 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1978 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1979 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1981 return inCheck ? MT_CHECKMATE
1982 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
1983 MT_STAINMATE : MT_STALEMATE;
1988 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1989 int rf, int ff, int rt, int ft,
1993 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1995 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1996 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1997 extern int kifu; // in parser.c
1999 // [HGM] wild: for wild-card pieces rt and rf are dummies
2000 if(piece == WhiteFalcon || piece == BlackFalcon ||
2001 piece == WhiteCobra || piece == BlackCobra)
2004 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
2005 || PieceToChar(board[rf][ff]) == '~'
2006 && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff]))
2008 (cl->rfIn == -1 || cl->rfIn == rf) &&
2009 (cl->ffIn == -1 || cl->ffIn == ff) &&
2010 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
2011 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
2013 if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move
2015 if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required
2016 int this = 1, other = 1;
2017 if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf);
2018 if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf);
2019 if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf);
2020 if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff);
2021 if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff);
2022 if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used
2023 if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it
2024 if(!this) return; // the current move does not satisfy the requested relative position, ignore it
2028 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
2029 // [HGM] oneclick: if multiple moves, be sure we remember capture
2030 cl->piece = board[rf][ff];
2033 cl->rt = wildCard ? cl->rtIn : rt;
2034 cl->ft = wildCard ? cl->ftIn : ft;
2037 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
2042 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
2044 int illegal = 0; char c = closure->promoCharIn;
2046 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
2047 closure->count = closure->captures = 0;
2048 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2049 closure->kind = ImpossibleMove;
2050 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
2051 fFilter = closure->ftIn;
2052 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
2053 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2054 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
2055 closure->count = closure->captures = 0;
2056 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2057 closure->kind = ImpossibleMove;
2058 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2061 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2062 if (closure->count == 0) {
2063 /* See if it's an illegal move due to check */
2065 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2066 if (closure->count == 0) {
2067 /* No, it's not even that */
2068 if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) {
2069 int f, r; // if there is only a single piece of the requested type on the board, use that
2070 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2071 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2072 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2073 if(closure->count > 1) illegal = 0; // ambiguous
2075 if(closure->count == 0) {
2076 if (appData.debugMode) { int i, j;
2077 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2078 for(j=0; j<BOARD_WIDTH; j++)
2079 fprintf(debugFP, "%3d", (int) board[i][j]);
2080 fprintf(debugFP, "\n");
2086 } else if(pieceDefs && closure->count > 1) { // [HGM] gen: move is ambiguous under engine-defined rules
2087 DisambiguateClosure spare = *closure;
2088 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
2089 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2090 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
2094 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2095 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2096 if(closure->piece < BlackPawn) { // white
2097 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2098 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2099 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2100 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2101 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2103 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2104 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2105 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2106 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2107 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2110 if(gameInfo.variant == VariantChu) {
2111 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2113 if(gameInfo.variant == VariantShogi) {
2114 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2115 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2116 ChessSquare piece = closure->piece;
2117 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
2118 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
2119 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2120 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2121 c = '+'; // allowed ICS notations
2122 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2123 else if(flags & F_WHITE_ON_MOVE) {
2124 if( (int) piece < (int) WhiteWazir &&
2125 (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) {
2126 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2127 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2128 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2129 else /* promotion optional, default is defer */
2130 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2131 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2133 if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) {
2134 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2135 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2136 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2137 else /* promotion optional, default is defer */
2138 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2139 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2142 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2143 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2145 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2146 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2147 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2148 gameInfo.variant == VariantMakruk)
2149 c = PieceToChar(BlackFerz);
2150 else if(gameInfo.variant == VariantASEAN)
2151 c = PieceToChar(BlackRook);
2152 else if(gameInfo.variant == VariantGreat)
2153 c = PieceToChar(BlackMan);
2154 else if(gameInfo.variant == VariantGrand)
2155 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2157 c = PieceToChar(BlackQueen);
2158 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2159 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2160 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2161 ChessSquare p = closure->piece;
2162 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+')
2163 closure->kind = ImpossibleMove; // used on non-promotable piece
2164 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2165 } else if (c != NULLCHAR) closure->kind = IllegalMove;
2167 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2168 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2169 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2170 if (closure->count > 1) {
2171 closure->kind = AmbiguousMove;
2174 /* Note: If more than one illegal move matches, but no legal
2175 moves, we return IllegalMove, not AmbiguousMove. Caller
2176 can look at closure->count to detect this.
2178 closure->kind = IllegalMove;
2192 } CoordsToAlgebraicClosure;
2194 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2195 ChessMove kind, int rf, int ff,
2196 int rt, int ft, VOIDSTAR closure));
2199 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2201 register CoordsToAlgebraicClosure *cl =
2202 (CoordsToAlgebraicClosure *) closure;
2204 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2205 (board[rf][ff] == cl->piece
2206 || PieceToChar(board[rf][ff]) == '~' &&
2207 (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece)
2211 cl->kind = kind; /* this is the move we want */
2213 cl->file++; /* need file to rule out this move */
2217 cl->rank++; /* need rank to rule out this move */
2219 cl->either++; /* rank or file will rule out this move */
2225 /* Convert coordinates to normal algebraic notation.
2226 promoChar must be NULLCHAR or 'x' if not a promotion.
2229 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2233 char *outp = out, c, capture;
2234 CoordsToAlgebraicClosure cl;
2236 if (rf == DROP_RANK) {
2237 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2238 /* Bughouse piece drop */
2239 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2244 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2246 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2249 if (promoChar == 'x') promoChar = NULLCHAR;
2250 piece = board[rf][ff];
2251 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
2256 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2257 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2258 /* Keep short notation if move is illegal only because it
2259 leaves the player in check, but still return IllegalMove */
2260 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2261 if (kind == IllegalMove) break;
2266 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2267 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2268 /* Non-capture; use style "e5" */
2271 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2273 /* Capture; use style "exd5" */
2275 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2279 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2281 /* Use promotion suffix style "=Q" */
2283 if (promoChar != NULLCHAR) {
2284 if(IS_SHOGI(gameInfo.variant)) {
2285 /* [HGM] ... but not in Shogi! */
2286 *outp++ = promoChar == '=' ? '=' : '+';
2289 *outp++ = ToUpper(promoChar);
2298 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2299 /* Code added by Tord: FRC castling. */
2300 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2301 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2303 safeStrCpy(out, "O-O", MOVE_LEN);
2305 safeStrCpy(out, "O-O-O", MOVE_LEN);
2306 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2308 /* End of code added by Tord */
2309 /* Test for castling or ICS wild castling */
2310 /* Use style "O-O" (oh-oh) for PGN compatibility */
2311 else if (rf == rt &&
2312 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2313 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2314 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2315 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2316 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2317 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2319 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2321 /* This notation is always unambiguous, unless there are
2322 kings on both the d and e files, with "wild castling"
2323 possible for the king on the d file and normal castling
2324 possible for the other. ICS rules for wild 9
2325 effectively make castling illegal for either king in
2326 this situation. So I am not going to worry about it;
2327 I'll just generate an ambiguous O-O in this case.
2329 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2332 /* else fall through */
2337 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2338 cl.ft = fFilter = ft;
2340 cl.kind = IllegalMove;
2341 cl.rank = cl.file = cl.either = 0;
2342 c = PieceToChar(piece) ;
2343 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed
2345 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2346 /* Generate pretty moves for moving into check, but
2347 still return IllegalMove.
2349 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece)));
2350 if (cl.kind == IllegalMove) break;
2351 cl.kind = IllegalMove;
2354 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2355 else "Ngf3" or "Ngxf7",
2356 else "N1f3" or "N5xf7",
2357 else "Ng1f3" or "Ng5xf7".
2359 if( c == '~' || c == '+') {
2360 /* [HGM] print nonexistent piece as its demoted version */
2361 piece = (ChessSquare) (CHUDEMOTED(piece));
2363 if(c=='+') *outp++ = c;
2364 *outp++ = ToUpper(PieceToChar(piece));
2365 if(*outp = PieceSuffix(piece)) outp++;
2367 if (cl.file || (cl.either && !cl.rank)) {
2373 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2376 if(board[rt][ft] != EmptySquare)
2382 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2383 if (IS_SHOGI(gameInfo.variant)) {
2384 /* [HGM] in Shogi non-pawns can promote */
2385 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2387 else if (gameInfo.variant == VariantChuChess && promoChar ||
2388 gameInfo.variant != VariantSuper && promoChar &&
2389 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2391 *outp++ = ToUpper(promoChar);
2393 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2395 *outp++ = ToUpper(promoChar);
2401 /* Moving a nonexistent piece */
2405 /* Not a legal move, even ignoring check.
2406 If there was a piece on the from square,
2407 use style "Ng1g3" or "Ng1xe8";
2408 if there was a pawn or nothing (!),
2409 use style "g1g3" or "g1xe8". Use "x"
2410 if a piece was on the to square, even
2411 a piece of the same color.
2415 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2417 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2418 c += (board[r][f] == piece); // count on-board pieces of given type
2419 *outp = PieceToChar(piece);
2420 if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
2421 *outp++ = ToUpper(PieceToChar(piece));
2422 if(*outp = PieceSuffix(piece)) outp++;
2424 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2428 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2430 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2434 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2435 /* Use promotion suffix style "=Q" */
2436 if (promoChar != NULLCHAR && promoChar != 'x') {
2438 *outp++ = ToUpper(promoChar);
2445 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2454 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2456 int preyStackPointer, chaseStackPointer;
2459 unsigned char rf, ff, rt, ft;
2463 unsigned char rank, file;
2469 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2471 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2472 int rf, int ff, int rt, int ft,
2476 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2477 { // For adding captures that can lead to chase indictment to the chaseStack
2478 if(board[rt][ft] == EmptySquare) return; // non-capture
2479 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2480 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2481 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2482 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2483 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2484 chaseStack[chaseStackPointer].rf = rf;
2485 chaseStack[chaseStackPointer].ff = ff;
2486 chaseStack[chaseStackPointer].rt = rt;
2487 chaseStack[chaseStackPointer].ft = ft;
2488 chaseStackPointer++;
2491 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2492 int rf, int ff, int rt, int ft,
2496 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2497 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2499 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2501 if(board[rt][ft] == EmptySquare) return; // no capture
2502 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2503 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2505 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2506 for(i=0; i<chaseStackPointer; i++) {
2507 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2508 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2509 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2510 chaseStack[i] = chaseStack[--chaseStackPointer];
2516 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2517 int rf, int ff, int rt, int ft,
2521 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2522 { // for determining if a piece (given through the closure) is protected
2523 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2525 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2526 if(appData.debugMode && board[rt][ft] != EmptySquare)
2527 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2530 extern char moveList[MAX_MOVES][MOVE_LEN];
2533 PerpetualChase (int first, int last)
2534 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2537 ChessSquare captured;
2539 preyStackPointer = 0; // clear stack of chased pieces
2540 for(i=first; i<last; i+=2) { // for all positions with same side to move
2541 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2542 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2543 // determine all captures possible after the move, and put them on chaseStack
2544 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2545 if(appData.debugMode) { int n;
2546 for(n=0; n<chaseStackPointer; n++)
2547 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2548 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2549 fprintf(debugFP, ": all capts\n");
2551 // determine all captures possible before the move, and delete them from chaseStack
2552 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2553 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2554 cl.rt = moveList[i][3]-ONE;
2555 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2556 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2557 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2558 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2559 if(appData.debugMode) { int n;
2560 for(n=0; n<chaseStackPointer; n++)
2561 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2562 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2563 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2565 // chaseSack now contains all captures made possible by the move
2566 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2567 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2568 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2570 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2571 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2573 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2574 continue; // C or H attack on R is always chase; leave on chaseStack
2576 if(attacker == victim) {
2577 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2578 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2579 // we can capture back with equal piece, so this is no chase but a sacrifice
2580 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2581 j--; /* ! */ continue;
2586 // the attack is on a lower piece, or on a pinned or blocked equal one
2587 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2588 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2589 // test if the victim is protected by a true protector. First make the capture.
2590 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2591 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2592 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2593 // Then test if the opponent can recapture
2594 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2595 cl.rt = chaseStack[j].rt;
2596 cl.ft = chaseStack[j].ft;
2597 if(appData.debugMode) {
2598 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2600 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2601 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2602 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2603 // unmake the capture
2604 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2605 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2606 // if a recapture was found, piece is protected, and we are not chasing it.
2607 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2608 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2612 // chaseStack now contains all moves that chased
2613 if(appData.debugMode) { int n;
2614 for(n=0; n<chaseStackPointer; n++)
2615 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2616 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2617 fprintf(debugFP, ": chases\n");
2619 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2620 for(j=0; j<chaseStackPointer; j++) {
2621 preyStack[j].rank = chaseStack[j].rt;
2622 preyStack[j].file = chaseStack[j].ft;
2624 preyStackPointer = chaseStackPointer;
2627 for(j=0; j<chaseStackPointer; j++) {
2628 for(k=0; k<preyStackPointer; k++) {
2629 // search the victim of each chase move on the preyStack (first occurrence)
2630 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2631 if(k < tail) break; // piece was already identified as still being chased
2632 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2633 preyStack[tail] = preyStack[k]; // by swapping
2634 preyStack[k] = preyStack[preyStackPointer];
2640 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2641 if(appData.debugMode) { int n;
2642 for(n=0; n<preyStackPointer; n++)
2643 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2644 fprintf(debugFP, "always chased upto ply %d\n", i);
2646 // now adjust the location of the chased pieces according to opponent move
2647 for(j=0; j<preyStackPointer; j++) {
2648 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2649 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2650 preyStack[j].rank = moveList[i+1][3]-ONE;
2651 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2656 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2657 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the