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 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);
74 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
76 char *pieceDesc[EmptySquare];
77 char *defaultDesc[EmptySquare] = {
78 "fmWfceFifmnD", "N", "B", "R", "Q",
79 "F", "A", "BN", "RN", "W", "K",
80 "mRcpR", "N0", "BW", "RF", "gQ",
81 "", "", "QN", "", "N", "",
83 "", "", "", "", "", "",
85 "", "", "", "", "", "K"
89 WhitePiece (ChessSquare piece)
91 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
95 BlackPiece (ChessSquare piece)
97 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
102 SameColor (ChessSquare piece1, ChessSquare piece2)
104 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
105 (int) piece1 < (int) BlackPawn &&
106 (int) piece2 >= (int) WhitePawn &&
107 (int) piece2 < (int) BlackPawn)
108 || ((int) piece1 >= (int) BlackPawn &&
109 (int) piece1 < (int) EmptySquare &&
110 (int) piece2 >= (int) BlackPawn &&
111 (int) piece2 < (int) EmptySquare);
114 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
117 char pieceToChar[] = {
118 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
119 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
120 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
121 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
123 char pieceNickName[EmptySquare];
126 PieceToChar (ChessSquare p)
128 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
129 return pieceToChar[(int) p];
133 PieceToNumber (ChessSquare p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
136 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
138 while(start++ != p) if(pieceToChar[start-1] != '.' && pieceToChar[start-1] != '+') i++;
146 if(c == '.') return EmptySquare;
147 for(i=0; i< (int) EmptySquare; i++)
148 if(pieceNickName[i] == c) return (ChessSquare) i;
149 for(i=0; i< (int) EmptySquare; i++)
150 if(pieceToChar[i] == c) return (ChessSquare) i;
155 CopyBoard (Board to, Board from)
159 for (i = 0; i < BOARD_HEIGHT; i++)
160 for (j = 0; j < BOARD_WIDTH; j++)
161 to[i][j] = from[i][j];
162 for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
163 to[VIRGIN][j] = from[VIRGIN][j],
164 to[CASTLING][j] = from[CASTLING][j];
165 to[HOLDINGS_SET] = 0; // flag used in ICS play
169 CompareBoards (Board board1, Board board2)
173 for (i = 0; i < BOARD_HEIGHT; i++)
174 for (j = 0; j < BOARD_WIDTH; j++) {
175 if (board1[i][j] != board2[i][j])
181 char defaultName[] = "PNBRQ......................................K" // white
182 "pnbrq......................................k"; // black
183 char shogiName[] = "PNBRLS...G.++++++..........................K" // white
184 "pnbrls...g.++++++..........................k"; // black
185 char xqName[] = "PH.R.AE..K.C................................" // white
186 "ph.r.ae..k.c................................"; // black
189 CollectPieceDescriptors ()
190 { // make a line of piece descriptions for use in the PGN Piece tag:
191 // dump all engine defined pieces, and pieces with non-standard names,
192 // but suppress black pieces that are the same as their white counterpart
194 static char buf[MSG_SIZ];
195 char *m, c, d, *pieceName = defaultName;
198 if(!pieceDefs) return "";
199 if(gameInfo.variant == VariantChu) return ""; // for now don't do this for Chu Shogi
200 if(gameInfo.variant == VariantShogi) pieceName = shogiName;
201 if(gameInfo.variant == VariantXiangqi) pieceName = xqName;
202 for(p=WhitePawn; p<EmptySquare; p++) {
203 if((c = pieceToChar[p]) == '.' || c == '~') continue; // does not participate
204 m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED p] : c);
205 if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == toupper(c)
206 && (c != '+' || pieceToChar[DEMOTED BLACK_TO_WHITE p] == d)) { // black member of normal pair
207 char *wm = pieceDesc[BLACK_TO_WHITE p];
208 if(!m && !wm || m && wm && !strcmp(wm, m)) continue; // moves as a white piece
209 } else // white or unpaired black
210 if((p < BlackPawn || CharToPiece(toupper(d)) != EmptySquare) && // white or lone black
211 !pieceDesc[p] /*&& pieceName[p] == c*/) continue; // orthodox piece known by its usual name
212 // TODO: listing pieces because of unusual name can only be done if we have accurate Betza of all defaults
213 if(!m) m = defaultDesc[p];
215 snprintf(buf+len, MSG_SIZ-len, "%s%s%c:%s", len ? ";" : "", c == '+' ? "+" : "", d, m);
220 // [HGM] gen: configurable move generation from Betza notation sent by engine.
221 // Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill-
222 // square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1
223 // if the move has 1 or 2 legs. Only the marking of squares makes use of this info, by only marking
224 // target squares of leg 1 (rejecting null move). A dummy move with MoveType 'FirstLeg' to the relay square
225 // is generated, so a cyan marker can be put there, and other functions can ignore such a move. When the
226 // user selects this square, it becomes the kill-square. Once a kill-square is set, only 2-leg moves are
227 // generated that use that square as relay, plus 1-leg moves, so the 1-leg move that goes to the kill-square
228 // can be marked during 2nd-leg entry to terminate the move there. For judging the pseudo-legality of the
229 // 2nd leg, the from-square has to be considered empty, although the moving piece is still on it.
233 // alphabet "abcdefghijklmnopqrstuvwxyz"
234 char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N";
235 char xStep[] = "2110.130.102.10.00....0..2";
236 char yStep[] = "2132.133.313.20.11....1..3";
237 char dirType[] = "01000104000200000260050000";
238 char upgrade[] = "AFCD.BGH.JQL.NO.KW....R..Z";
239 char rotate[] = "DRCA.WHG.JKL.NO.QB....F..Z";
241 // alphabet "a b c d e f g h i j k l m n o p q r s t u v w x y z "
242 int dirs1[] = { 0,0x3C,0,0,0,0xC3,0,0, 0,0,0,0xF0,0,0,0,0,0,0x0F,0 ,0,0,0 ,0,0,0,0 };
243 int dirs2[] = { 0,0x18,0,0,0,0x81,0,0xFF,0,0,0,0x60,0,0,0,0,0,0x06,0x66,0,0,0x99,0,0,0,0 };
244 int dirs3[] = { 0,0x38,0,0,0,0x83,0,0xFF,0,0,0,0xE0,0,0,0,0,0,0x0E,0xEE,0,0,0xBB,0,0,0,0 };
245 int dirs4[] = { 0,0x10,0,0,0,0x01,0,0xFF,0,0,0,0x40,0,0,0,0,0,0x04,0x44,0,0,0x11,0,0,0,0 };
247 int rot[][4] = { // rotation matrices for each direction
259 OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR cl)
265 MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, char *desc, MoveCallback cb, VOIDSTAR cl)
267 char buf[80], *p = desc, *atom = NULL;
268 int mine, his, dir, bit, occup, i, ep, promoRank = -1;
269 ChessMove promo= NormalMove; ChessSquare pc = board[r][f];
270 if(pc == DarkSquare) return; // this is not a piece, but a 'hole' in the board
271 if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
272 if(pc == WhitePawn || pc == WhiteLance) promo = WhitePromotion, promoRank = BOARD_HEIGHT-1; else
273 if(pc == BlackPawn || pc == BlackLance) promo = BlackPromotion, promoRank = 0;
274 while(*p) { // more moves to go
275 int expo = 1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0;
277 if(*p == 'i') initial = 1, desc = ++p;
278 while(islower(*p)) p++; // skip prefixes
279 if(!isupper(*p)) return; // syntax error: no atom
280 dx = xStep[*p-'A'] - '0';// step vector of atom
281 dy = yStep[*p-'A'] - '0';
282 dirSet = 0; // build direction set based on atom symmetry
283 switch(symmetry[*p-'A']) {
284 case 'B': expo = 0; // bishop, slide
285 case 'F': all = 0xAA; // diagonal atom (degenerate 4-fold)
286 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
287 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
288 int b = dirs1[*desc-'a']; // use wide version
289 if( islower(desc[1]) &&
290 ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
291 b = dirs1[*desc-'a'] & dirs1[desc[1]-'a']; // intersect wide & perp wide
296 dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
298 case 'R': expo = 0; // rook, slide
299 case 'W': all = 0x55; // orthogonal atom (non-deg 4-fold)
300 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
301 while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
302 dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
303 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
305 case 'N': all = 0xFF; // oblique atom (degenerate 8-fold)
306 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
307 if(*desc == 'h') { // chiral direction sets 'hr' and 'hl'
308 dirSet = (desc[1] == 'r' ? 0x55 : 0xAA); desc += 2;
310 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
311 int b = dirs2[*desc-'a']; // when alone, use narrow version
312 if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
313 else if(*desc == desc[1] || islower(desc[1]) && i < '4'
314 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
315 b = dirs1[*desc-'a'] & dirs2[desc[1]-'a']; // intersect wide & perp narrow
320 if(!dirSet) dirSet = 0xFF;
322 case 'Q': expo = 0; // queen, slide
323 case 'K': all = 0xFF; // non-deg (pseudo) 8-fold
325 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
326 int b = dirs4[*desc-'a']; // when alone, use narrow version
327 if(desc[1] == *desc) desc++; // doubling forces alone
328 else if(islower(desc[1]) && i < '4'
329 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
330 b = dirs3[*desc-'a'] & dirs3[desc[1]-'a']; // intersect wide & perp wide
335 if(!dirSet) dirSet = (tx < 0 ? 0xFF // default is all directions, but in continuation leg
336 : all == 0xFF ? 0xEF : 0x45); // omits backward, and for 4-fold atoms also diags
337 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
338 ds2 = dirSet & 0xAA; // extract diagonal directions
339 if(dirSet &= 0x55) // start with orthogonal moves, if present
340 retry = 1, dx = 0; // and schedule the diagonal moves for later
341 else dx = dy, dirSet = ds2; // if no orthogonal directions, do diagonal immediately
342 break; // should not have direction indicators
343 default: return; // syntax error: invalid atom
345 if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
346 mode = 0; // build mode mask
347 if(*desc == 'm') mode |= 4, desc++; // move to empty
348 if(*desc == 'c') mode |= his, desc++; // capture foe
349 if(*desc == 'd') mode |= mine, desc++; // destroy (capture friend)
350 if(*desc == 'e') mode |= 8, desc++; // e.p. capture last mover
351 if(*desc == 't') mode |= 16, desc++; // exclude enemies as hop platform ('test')
352 if(*desc == 'p') mode |= 32, desc++; // hop over occupied
353 if(*desc == 'g') mode |= 64, desc++; // hop and toggle range
354 if(*desc == 'o') mode |= 128, desc++; // wrap around cylinder board
355 if(*desc == 'y') mode |= 512, desc++; // toggle range on empty square
356 if(*desc == 'n') jump = 0, desc++; // non-jumping
357 while(*desc == 'j') jump++, desc++; // must jump (on B,R,Q: skip first square)
358 if(*desc == 'a') cont = ++desc; // move again after doing what preceded it
359 if(isdigit(*++p)) expo = atoi(p++); // read exponent
360 if(expo > 9) p++; // allow double-digit
361 desc = p; // this is start of next move
362 if(initial && (board[r][f] != initialPosition[r][f] ||
363 r == 0 && board[TOUCHED_W] & 1<<f ||
364 r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f ) ) continue;
365 if(expo > 1 && dx == 0 && dy == 0) { // castling indicated by O + number
366 mode |= 1024; dy = 1;
369 if(!(mode & 15)) mode |= his + 4; // no mode spec, use default = mc
371 strncpy(buf, cont, 80); cont = buf; // copy next leg(s), so we can modify
372 atom = buf; while(islower(*atom)) atom++; // skip to atom
373 if(mode & 32) mode ^= 256 + 32; // in non-final legs 'p' means 'pass through'
374 if(mode & 64 + 512) {
375 mode |= 256; // and 'g' too, but converts leaper <-> slider
376 if(mode & 512) mode ^= 0x304; // and 'y' is m-like 'g'
377 *atom = upgrade[*atom-'A']; // replace atom, BRQ <-> FWK
378 atom[1] = atom[2] = '\0'; // make sure any old range is stripped off
379 if(expo == 1) atom[1] = '0'; // turn other leapers into riders
381 if(!(mode & 0x30F)) mode |= 4; // and default of this leg = m
383 if(dy == 1) skip = jump - 1, jump = 1; // on W & F atoms 'j' = skip first square
385 for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
386 int i = expo, j = skip, hop = mode, vx, vy, loop = 0;
387 if(!(bit & dirSet)) continue; // does not move in this direction
388 if(dy != 1 || mode & 1024) j = 0; //
389 vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector
390 vy = dx*rot[dir][2] + dy*rot[dir][3];
391 if(tx < 0) x = f, y = r; // start square
392 else x = tx, y = ty; // from previous to-square if continuation
394 x += vx; y += vy; // step to next square
395 if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
396 if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT, loop++; else break; }
397 if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT, loop++; else break; }
398 if(board[y][x] == DarkSquare) break; // black squares are supposed to be off board
399 if(j) { j--; continue; } // skip irrespective of occupation
400 if(!jump && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
401 if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
402 if(x == f && y == r && !loop) occup = 4; else // start square counts as empty (if not around cylinder!)
403 if(board[y][x] < BlackPawn) occup = 0x101; else
404 if(board[y][x] < EmptySquare) occup = 0x102; else
406 if(cont) { // non-final leg
407 if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode
408 if(occup & mode) { // valid intermediate square, do continuation
409 char origAtom = *atom;
410 if(!(bit & all)) *atom = rotate[*atom - 'A']; // orth-diag interconversion to make direction valid
411 if(occup & mode & 0x104) // no side effects, merge legs to one move
412 MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
413 if(occup & mode & 3 && (killX < 0 || killX == x && killY == y)) { // destructive first leg
415 MovesFromString(board, flags, f, r, x, y, dir, cont, &OK, &cnt); // count possible continuations
416 if(cnt) { // and if there are
417 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); // then generate their first leg
419 MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
423 *atom = origAtom; // undo any interconversion
425 if(occup != 4) break; // occupied squares always terminate the leg
428 if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper
430 if(mode & 8 && occup == 4 && board[EP_FILE] == x && (y == (ep & 127) || y - vy == ep - 128)) { // to e.p. square (or 2nd e.p. square)
431 cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
433 if(mode & 1024) { // castling
434 i = 2; // kludge to elongate move indefinitely
435 if(occup == 4) continue; // skip empty squares
436 if((x == BOARD_LEFT + skip || x > BOARD_LEFT + skip && vx < 0 && board[y][x-1-skip] == DarkSquare)
437 && board[y][x] == initialPosition[y][x]) { // reached initial corner piece
438 if(pc != WhiteKing && pc != BlackKing) { // non-royal castling (to be entered as two-leg move via 'Rook')
439 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX < f)
440 legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f - expo, cl), legNr >>= 1;
442 cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
444 if((x == BOARD_RGHT-1-skip || x < BOARD_RGHT-1-skip && vx > 0 && board[y][x+1+skip] == DarkSquare)
445 && board[y][x] == initialPosition[y][x]) {
446 if(pc != WhiteKing && pc != BlackKing) {
447 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX > f)
448 legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f + expo, cl), legNr >>= 1;
450 cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
454 if(mode & 16 && (board[y][x] == WhiteKing || board[y][x] == BlackKing)) break; // tame piece, cannot capture royal
455 if(occup & mode) cb(board, flags, y == promoRank ? promo : NormalMove, r, f, y, x, cl); // allowed, generate
456 if(occup != 4) break; // not valid transit square
459 dx = dy; dirSet = ds2; // prepare for diagonal moves of K,Q
460 } while(retry-- && ds2); // and start doing them
461 if(tx >= 0) break; // don't do other atoms in continuation legs
465 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
468 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
473 if (rt >= BOARD_HEIGHT) break;
474 if (SameColor(board[rf][ff], board[rt][ft])) break;
475 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
476 if (board[rt][ft] != EmptySquare) break;
481 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
487 if (SameColor(board[rf][ff], board[rt][ft])) break;
488 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
489 if (board[rt][ft] != EmptySquare) break;
494 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
496 SlideForward(board, flags, rf, ff, callback, closure);
497 SlideBackward(board, flags, rf, ff, callback, closure);
501 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
503 int i, s, rt = rf, ft;
504 for(s = -1; s <= 1; s+= 2) {
507 if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
508 if (SameColor(board[rf][ff], board[rt][ft])) break;
509 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
510 if (board[rt][ft] != EmptySquare) break;
516 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
519 for(s = -1; s <= 1; s+= 2) {
523 if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
524 if (SameColor(board[rf][ff], board[rt][ft])) break;
525 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
526 if (board[rt][ft] != EmptySquare) break;
532 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
535 for(s = -1; s <= 1; s+= 2) {
539 if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
540 if (SameColor(board[rf][ff], board[rt][ft])) break;
541 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
542 if (board[rt][ft] != EmptySquare) break;
548 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
550 SlideVertical(board, flags, rf, ff, callback, closure);
551 SlideSideways(board, flags, rf, ff, callback, closure);
555 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
557 SlideDiagForward(board, flags, rf, ff, callback, closure);
558 SlideDiagBackward(board, flags, rf, ff, callback, closure);
562 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
563 { // Lion-like move of Horned Falcon and Souring Eagle
564 int ft = ff + dx, rt = rf + dy;
565 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
567 if (!SameColor(board[rf][ff], board[rt][ft]))
568 callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
571 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
573 if (!SameColor(board[rf][ff], board[rt][ft]))
574 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
575 if (!SameColor(board[rf][ff], board[rf+dy][ff+dx]))
576 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
581 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
583 int ft = ff, rt = rf + 1;
584 if (rt >= BOARD_HEIGHT) return;
585 if (SameColor(board[rf][ff], board[rt][ft])) return;
586 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
590 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
592 int ft = ff, rt = rf - 1;
594 if (SameColor(board[rf][ff], board[rt][ft])) return;
595 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
599 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
603 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
604 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
606 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
607 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
611 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
614 if (rt >= BOARD_HEIGHT) return;
616 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
617 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
619 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
620 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
624 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
629 if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
630 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
632 if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
633 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
637 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
639 StepForward(board, flags, rf, ff, callback, closure);
640 StepBackward(board, flags, rf, ff, callback, closure);
644 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
646 StepDiagForward(board, flags, rf, ff, callback, closure);
647 StepDiagBackward(board, flags, rf, ff, callback, closure);
651 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
653 StepVertical(board, flags, rf, ff, callback, closure);
654 StepSideways(board, flags, rf, ff, callback, closure);
658 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
661 for (i = -1; i <= 1; i += 2)
662 for (j = -1; j <= 1; j += 2)
663 for (s = 1; s <= 2; s++) {
666 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
667 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
668 && !SameColor(board[rf][ff], board[rt][ft]))
669 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
673 /* Call callback once for each pseudo-legal move in the given
674 position, except castling moves. A move is pseudo-legal if it is
675 legal, or if it would be legal except that it leaves the king in
676 check. In the arguments, epfile is EP_NONE if the previous move
677 was not a double pawn push, or the file 0..7 if it was, or
678 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
679 Promotion moves generated are to Queen only.
682 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
683 // speed: only do moves with this piece type
686 int i, j, d, s, fs, rs, rt, ft, m;
687 int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
688 int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
690 for (rf = 0; rf < BOARD_HEIGHT; rf++)
691 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
694 if(board[rf][ff] == EmptySquare) continue;
695 if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
696 m = 0; piece = board[rf][ff];
697 if(PieceToChar(piece) == '~')
698 piece = (ChessSquare) ( DEMOTED piece );
699 if(filter != EmptySquare && piece != filter) continue;
700 if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
701 MovesFromString(board, flags, ff, rf, -1, -1, 0, pieceDesc[piece], callback, closure);
704 if(IS_SHOGI(gameInfo.variant))
705 piece = (ChessSquare) ( SHOGI piece );
707 switch ((int)piece) {
708 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
710 /* can't happen ([HGM] except for faries...) */
714 if(gameInfo.variant == VariantXiangqi) {
715 /* [HGM] capture and move straight ahead in Xiangqi */
716 if (rf < BOARD_HEIGHT-1 &&
717 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
718 callback(board, flags, NormalMove,
719 rf, ff, rf + 1, ff, closure);
721 /* and move sideways when across the river */
722 for (s = -1; s <= 1; s += 2) {
723 if (rf >= BOARD_HEIGHT>>1 &&
724 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
725 !WhitePiece(board[rf][ff+s]) ) {
726 callback(board, flags, NormalMove,
727 rf, ff, rf, ff+s, closure);
732 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
733 callback(board, flags,
734 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
735 rf, ff, rf + 1, ff, closure);
737 if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
738 gameInfo.variant != VariantShatranj && /* [HGM] */
739 gameInfo.variant != VariantCourier && /* [HGM] */
740 board[rf+2][ff] == EmptySquare ) {
741 callback(board, flags, NormalMove,
742 rf, ff, rf+2, ff, closure);
744 for (s = -1; s <= 1; s += 2) {
745 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
746 ((flags & F_KRIEGSPIEL_CAPTURE) ||
747 BlackPiece(board[rf + 1][ff + s]))) {
748 callback(board, flags,
749 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
750 rf, ff, rf + 1, ff + s, closure);
752 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
753 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
754 (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
755 board[rf][ff + s] == BlackPawn &&
756 board[rf+1][ff + s] == EmptySquare) {
757 callback(board, flags, WhiteCapturesEnPassant,
758 rf, ff, rf+1, ff + s, closure);
765 if(gameInfo.variant == VariantXiangqi) {
766 /* [HGM] capture straight ahead in Xiangqi */
767 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
768 callback(board, flags, NormalMove,
769 rf, ff, rf - 1, ff, closure);
771 /* and move sideways when across the river */
772 for (s = -1; s <= 1; s += 2) {
773 if (rf < BOARD_HEIGHT>>1 &&
774 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
775 !BlackPiece(board[rf][ff+s]) ) {
776 callback(board, flags, NormalMove,
777 rf, ff, rf, ff+s, closure);
782 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
783 callback(board, flags,
784 rf <= promoRank ? BlackPromotion : NormalMove,
785 rf, ff, rf - 1, ff, closure);
787 if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
788 gameInfo.variant != VariantShatranj && /* [HGM] */
789 gameInfo.variant != VariantCourier && /* [HGM] */
790 board[rf-2][ff] == EmptySquare) {
791 callback(board, flags, NormalMove,
792 rf, ff, rf-2, ff, closure);
794 for (s = -1; s <= 1; s += 2) {
795 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
796 ((flags & F_KRIEGSPIEL_CAPTURE) ||
797 WhitePiece(board[rf - 1][ff + s]))) {
798 callback(board, flags,
799 rf <= promoRank ? BlackPromotion : NormalMove,
800 rf, ff, rf - 1, ff + s, closure);
802 if (rf < BOARD_HEIGHT>>1) {
803 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
804 (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
805 board[rf][ff + s] == WhitePawn &&
806 board[rf-1][ff + s] == EmptySquare) {
807 callback(board, flags, BlackCapturesEnPassant,
808 rf, ff, rf-1, ff + s, closure);
818 for (i = -1; i <= 1; i += 2)
819 for (j = -1; j <= 1; j += 2)
820 for (s = 1; s <= 2; s++) {
823 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
824 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
825 && !SameColor(board[rf][ff], board[rt][ft]))
826 callback(board, flags, NormalMove,
827 rf, ff, rt, ft, closure);
831 case SHOGI WhiteKnight:
832 for (s = -1; s <= 1; s += 2) {
833 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
834 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
835 callback(board, flags, NormalMove,
836 rf, ff, rf + 2, ff + s, closure);
841 case SHOGI BlackKnight:
842 for (s = -1; s <= 1; s += 2) {
843 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
844 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
845 callback(board, flags, NormalMove,
846 rf, ff, rf - 2, ff + s, closure);
853 for (d = 0; d <= 1; d++)
854 for (s = -1; s <= 1; s += 2) {
857 rt = rf + (i * s) * d;
858 ft = ff + (i * s) * (1 - d);
859 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
860 if (m == 0 && board[rt][ft] == EmptySquare)
861 callback(board, flags, NormalMove,
862 rf, ff, rt, ft, closure);
863 if (m == 1 && board[rt][ft] != EmptySquare &&
864 !SameColor(board[rf][ff], board[rt][ft]) )
865 callback(board, flags, NormalMove,
866 rf, ff, rt, ft, closure);
867 if (board[rt][ft] != EmptySquare && m++) break;
872 /* Gold General (and all its promoted versions) . First do the */
873 /* diagonal forward steps, then proceed as normal Wazir */
874 case SHOGI (PROMOTED WhitePawn):
875 if(gameInfo.variant == VariantShogi) goto WhiteGold;
876 case SHOGI (PROMOTED BlackPawn):
877 if(gameInfo.variant == VariantShogi) goto BlackGold;
878 SlideVertical(board, flags, rf, ff, callback, closure);
881 case SHOGI (PROMOTED WhiteKnight):
882 if(gameInfo.variant == VariantShogi) goto WhiteGold;
883 case SHOGI BlackDrunk:
884 case SHOGI BlackAlfil:
885 Ferz(board, flags, rf, ff, callback, closure);
886 StepSideways(board, flags, rf, ff, callback, closure);
887 StepBackward(board, flags, rf, ff, callback, closure);
890 case SHOGI (PROMOTED BlackKnight):
891 if(gameInfo.variant == VariantShogi) goto BlackGold;
892 case SHOGI WhiteDrunk:
893 case SHOGI WhiteAlfil:
894 Ferz(board, flags, rf, ff, callback, closure);
895 StepSideways(board, flags, rf, ff, callback, closure);
896 StepForward(board, flags, rf, ff, callback, closure);
900 case SHOGI WhiteStag:
901 case SHOGI BlackStag:
902 if(gameInfo.variant == VariantShogi) goto BlackGold;
903 SlideVertical(board, flags, rf, ff, callback, closure);
904 Ferz(board, flags, rf, ff, callback, closure);
905 StepSideways(board, flags, rf, ff, callback, closure);
908 case SHOGI (PROMOTED WhiteQueen):
909 case SHOGI WhiteTokin:
910 case SHOGI WhiteWazir:
912 StepDiagForward(board, flags, rf, ff, callback, closure);
913 Wazir(board, flags, rf, ff, callback, closure);
916 case SHOGI (PROMOTED BlackQueen):
917 case SHOGI BlackTokin:
918 case SHOGI BlackWazir:
920 StepDiagBackward(board, flags, rf, ff, callback, closure);
921 Wazir(board, flags, rf, ff, callback, closure);
926 Wazir(board, flags, rf, ff, callback, closure);
929 case SHOGI WhiteMarshall:
930 case SHOGI BlackMarshall:
931 Ferz(board, flags, rf, ff, callback, closure);
932 for (d = 0; d <= 1; d++)
933 for (s = -2; s <= 2; s += 4) {
935 ft = ff + s * (1 - d);
936 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
937 if (!SameColor(board[rf][ff], board[rt][ft]) )
938 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
942 case SHOGI WhiteAngel:
943 case SHOGI BlackAngel:
944 Wazir(board, flags, rf, ff, callback, closure);
948 /* [HGM] support Shatranj pieces */
949 for (rs = -1; rs <= 1; rs += 2)
950 for (fs = -1; fs <= 1; fs += 2) {
953 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
954 && ( gameInfo.variant != VariantXiangqi ||
955 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
957 && !SameColor(board[rf][ff], board[rt][ft]))
958 callback(board, flags, NormalMove,
959 rf, ff, rt, ft, closure);
960 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
961 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
962 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
964 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
965 && !SameColor(board[rf][ff], board[rt][ft]))
966 callback(board, flags, NormalMove,
967 rf, ff, rt, ft, closure);
969 if(gameInfo.variant == VariantSpartan)
970 for(fs = -1; fs <= 1; fs += 2) {
972 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
973 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
977 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
980 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
981 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
982 for (s = -2; s <= 2; s += 4) {
984 ft = ff + s * (1 - d);
985 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
986 if (SameColor(board[rf][ff], board[rt][ft])) continue;
987 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
990 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
991 case SHOGI WhiteCardinal:
992 case SHOGI BlackCardinal:
993 case SHOGI WhitePCardinal:
994 case SHOGI BlackPCardinal:
996 Bishop(board, flags, rf, ff, callback, closure);
997 Wazir(board, flags, rf, ff, callback, closure);
1000 /* Capablanca Archbishop continues as Knight */
1003 Knight(board, flags, rf, ff, callback, closure);
1005 /* Shogi Bishops are ordinary Bishops */
1006 case SHOGI WhiteBishop:
1007 case SHOGI BlackBishop:
1008 case SHOGI WhitePBishop:
1009 case SHOGI BlackPBishop:
1012 Bishop(board, flags, rf, ff, callback, closure);
1015 /* Shogi Lance is unlike anything, and asymmetric at that */
1016 case SHOGI WhiteQueen:
1017 if(gameInfo.variant == VariantChu) goto doQueen;
1021 if (rt >= BOARD_HEIGHT) break;
1022 if (SameColor(board[rf][ff], board[rt][ft])) break;
1023 callback(board, flags, NormalMove,
1024 rf, ff, rt, ft, closure);
1025 if (board[rt][ft] != EmptySquare) break;
1029 case SHOGI BlackQueen:
1030 if(gameInfo.variant == VariantChu) goto doQueen;
1035 if (SameColor(board[rf][ff], board[rt][ft])) break;
1036 callback(board, flags, NormalMove,
1037 rf, ff, rt, ft, closure);
1038 if (board[rt][ft] != EmptySquare) break;
1042 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
1045 if(gameInfo.variant == VariantChuChess) goto DragonKing;
1046 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1047 for (s = -2; s <= 2; s += 4) {
1049 ft = ff + s * (1 - d);
1050 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1051 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
1052 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1053 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1055 if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
1056 Wazir(board, flags, rf, ff, callback, closure);
1058 Rook(board, flags, rf, ff, callback, closure);
1061 /* Shogi Dragon King has to continue as Ferz after Rook moves */
1062 case SHOGI WhiteDragon:
1063 case SHOGI BlackDragon:
1064 case SHOGI WhitePDragon:
1065 case SHOGI BlackPDragon:
1067 Rook(board, flags, rf, ff, callback, closure);
1068 Ferz(board, flags, rf, ff, callback, closure);
1072 /* Capablanca Chancellor sets flag to continue as Knight */
1075 Rook(board, flags, rf, ff, callback, closure);
1076 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
1077 Ferz(board, flags, rf, ff, callback, closure);
1079 Knight(board, flags, rf, ff, callback, closure);
1082 /* Shogi Rooks are ordinary Rooks */
1083 case SHOGI WhiteRook:
1084 case SHOGI BlackRook:
1085 case SHOGI WhitePRook:
1086 case SHOGI BlackPRook:
1089 Rook(board, flags, rf, ff, callback, closure);
1094 case SHOGI WhiteMother:
1095 case SHOGI BlackMother:
1097 Rook(board, flags, rf, ff, callback, closure);
1098 Bishop(board, flags, rf, ff, callback, closure);
1101 case SHOGI WhitePawn:
1102 StepForward(board, flags, rf, ff, callback, closure);
1105 case SHOGI BlackPawn:
1106 StepBackward(board, flags, rf, ff, callback, closure);
1110 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1111 case SHOGI WhiteFerz:
1112 Ferz(board, flags, rf, ff, callback, closure);
1113 StepForward(board, flags, rf, ff, callback, closure);
1117 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1118 case SHOGI BlackFerz:
1119 StepBackward(board, flags, rf, ff, callback, closure);
1123 /* [HGM] support Shatranj pieces */
1124 Ferz(board, flags, rf, ff, callback, closure);
1129 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
1132 case SHOGI WhiteMonarch:
1133 case SHOGI BlackMonarch:
1134 case SHOGI WhiteKing:
1135 case SHOGI BlackKing:
1138 Ferz(board, flags, rf, ff, callback, closure);
1139 Wazir(board, flags, rf, ff, callback, closure);
1142 case WhiteNightrider:
1143 case BlackNightrider:
1144 for (i = -1; i <= 1; i += 2)
1145 for (j = -1; j <= 1; j += 2)
1146 for (s = 1; s <= 2; s++) { int k;
1149 ft = ff + k*j*(3-s);
1150 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
1151 if (SameColor(board[rf][ff], board[rt][ft])) break;
1152 callback(board, flags, NormalMove,
1153 rf, ff, rt, ft, closure);
1154 if (board[rt][ft] != EmptySquare) break;
1160 Bishop(board, flags, rf, ff, callback, closure);
1161 Rook(board, flags, rf, ff, callback, closure);
1162 Knight(board, flags, rf, ff, callback, closure);
1165 // Use Lance as Berolina / Spartan Pawn.
1167 if(gameInfo.variant == VariantSuper) goto Amazon;
1168 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1169 callback(board, flags,
1170 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1171 rf, ff, rf + 1, ff, closure);
1172 for (s = -1; s <= 1; s += 2) {
1173 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1174 callback(board, flags,
1175 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1176 rf, ff, rf + 1, ff + s, closure);
1177 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1178 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1183 if(gameInfo.variant == VariantSuper) goto Amazon;
1184 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1185 callback(board, flags,
1186 rf <= promoRank ? BlackPromotion : NormalMove,
1187 rf, ff, rf - 1, ff, closure);
1188 for (s = -1; s <= 1; s += 2) {
1189 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1190 callback(board, flags,
1191 rf <= promoRank ? BlackPromotion : NormalMove,
1192 rf, ff, rf - 1, ff + s, closure);
1193 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1194 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1198 case SHOGI WhiteNothing:
1199 case SHOGI BlackNothing:
1200 case SHOGI WhiteLion:
1201 case SHOGI BlackLion:
1204 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1205 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1206 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1207 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
1208 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
1209 rf, ff, rt, ft, closure);
1214 case SHOGI WhiteFalcon:
1215 case SHOGI BlackFalcon:
1216 case SHOGI WhitePDagger:
1217 case SHOGI BlackPDagger:
1218 SlideSideways(board, flags, rf, ff, callback, closure);
1219 StepVertical(board, flags, rf, ff, callback, closure);
1222 case SHOGI WhiteCobra:
1223 case SHOGI BlackCobra:
1224 StepVertical(board, flags, rf, ff, callback, closure);
1227 case SHOGI (PROMOTED WhiteFerz):
1228 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1229 case SHOGI (PROMOTED BlackFerz):
1230 if(gameInfo.variant == VariantShogi) goto BlackGold;
1231 case SHOGI WhitePSword:
1232 case SHOGI BlackPSword:
1233 SlideVertical(board, flags, rf, ff, callback, closure);
1234 StepSideways(board, flags, rf, ff, callback, closure);
1237 case SHOGI WhiteUnicorn:
1238 case SHOGI BlackUnicorn:
1239 Ferz(board, flags, rf, ff, callback, closure);
1240 StepVertical(board, flags, rf, ff, callback, closure);
1243 case SHOGI WhiteMan:
1244 StepDiagForward(board, flags, rf, ff, callback, closure);
1245 StepVertical(board, flags, rf, ff, callback, closure);
1248 case SHOGI BlackMan:
1249 StepDiagBackward(board, flags, rf, ff, callback, closure);
1250 StepVertical(board, flags, rf, ff, callback, closure);
1253 case SHOGI WhiteHCrown:
1254 case SHOGI BlackHCrown:
1255 Bishop(board, flags, rf, ff, callback, closure);
1256 SlideSideways(board, flags, rf, ff, callback, closure);
1259 case SHOGI WhiteCrown:
1260 case SHOGI BlackCrown:
1261 Bishop(board, flags, rf, ff, callback, closure);
1262 SlideVertical(board, flags, rf, ff, callback, closure);
1265 case SHOGI WhiteHorned:
1266 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1267 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1268 if(killX >= 0) break;
1269 Bishop(board, flags, rf, ff, callback, closure);
1270 SlideSideways(board, flags, rf, ff, callback, closure);
1271 SlideBackward(board, flags, rf, ff, callback, closure);
1274 case SHOGI BlackHorned:
1275 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1276 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1277 if(killX >= 0) break;
1278 Bishop(board, flags, rf, ff, callback, closure);
1279 SlideSideways(board, flags, rf, ff, callback, closure);
1280 SlideForward(board, flags, rf, ff, callback, closure);
1283 case SHOGI WhiteEagle:
1284 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1285 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1286 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1287 if(killX >= 0) break;
1288 Rook(board, flags, rf, ff, callback, closure);
1289 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1292 case SHOGI BlackEagle:
1293 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1294 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1295 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1296 if(killX >= 0) break;
1297 Rook(board, flags, rf, ff, callback, closure);
1298 SlideDiagForward(board, flags, rf, ff, callback, closure);
1301 case SHOGI WhiteDolphin:
1302 case SHOGI BlackHorse:
1303 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1304 SlideVertical(board, flags, rf, ff, callback, closure);
1307 case SHOGI BlackDolphin:
1308 case SHOGI WhiteHorse:
1309 SlideDiagForward(board, flags, rf, ff, callback, closure);
1310 SlideVertical(board, flags, rf, ff, callback, closure);
1313 case SHOGI WhiteLance:
1314 SlideForward(board, flags, rf, ff, callback, closure);
1317 case SHOGI BlackLance:
1318 SlideBackward(board, flags, rf, ff, callback, closure);
1321 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1325 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1338 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1339 Board xqCheckers, nullBoard;
1341 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1342 int rf, int ff, int rt, int ft,
1346 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1348 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1350 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1352 if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1354 if (!(flags & F_IGNORE_CHECK) ) {
1355 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1358 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1359 kings += (board[r][f] == BlackKing);
1363 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1365 check = CheckTest(board, flags, rf, ff, rt, ft,
1366 kind == WhiteCapturesEnPassant ||
1367 kind == BlackCapturesEnPassant);
1368 if(promo) board[rf][ff] = BlackLance;
1371 if (flags & F_ATOMIC_CAPTURE) {
1372 if (board[rt][ft] != EmptySquare ||
1373 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1375 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1376 if (board[rf][ff] == king) return;
1377 for (r = rt-1; r <= rt+1; r++) {
1378 for (f = ft-1; f <= ft+1; f++) {
1379 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1380 board[r][f] == king) return;
1385 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1392 int captures; // [HGM] losers
1393 } LegalityTestClosure;
1396 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1397 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1398 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1399 moves that would destroy your own king. The CASTLE_OK flags are
1400 true if castling is not yet ruled out by a move of the king or
1401 rook. Return TRUE if the player on move is currently in check and
1402 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1404 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1407 int ff, ft, k, left, right, swap;
1408 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1409 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1410 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1415 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1416 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1417 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1419 if (inCheck) return TRUE;
1421 /* Generate castling moves */
1422 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1423 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1426 p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1427 if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1429 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1430 if ((flags & F_WHITE_ON_MOVE) &&
1431 (flags & F_WHITE_KCASTLE_OK) &&
1432 board[0][ff] == wKing &&
1433 board[0][ff + 1] == EmptySquare &&
1434 board[0][ff + 2] == EmptySquare &&
1435 board[0][BOARD_RGHT-3] == EmptySquare &&
1436 board[0][BOARD_RGHT-2] == EmptySquare &&
1437 board[0][BOARD_RGHT-1] == WhiteRook &&
1438 castlingRights[0] != NoRights && /* [HGM] check rights */
1439 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1441 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1442 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1443 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1444 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1446 callback(board, flags,
1447 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1448 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1450 if ((flags & F_WHITE_ON_MOVE) &&
1451 (flags & F_WHITE_QCASTLE_OK) &&
1452 board[0][ff] == wKing &&
1453 board[0][ff - 1] == EmptySquare &&
1454 board[0][ff - 2] == EmptySquare &&
1455 board[0][BOARD_LEFT+2] == EmptySquare &&
1456 board[0][BOARD_LEFT+1] == EmptySquare &&
1457 board[0][BOARD_LEFT+0] == WhiteRook &&
1458 castlingRights[1] != NoRights && /* [HGM] check rights */
1459 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1461 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1462 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1463 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1465 callback(board, flags,
1466 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1467 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1469 if (!(flags & F_WHITE_ON_MOVE) &&
1470 (flags & F_BLACK_KCASTLE_OK) &&
1471 board[BOARD_HEIGHT-1][ff] == bKing &&
1472 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1473 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1474 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1475 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1476 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1477 castlingRights[3] != NoRights && /* [HGM] check rights */
1478 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1480 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1481 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1482 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1483 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1485 callback(board, flags,
1486 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1487 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1489 if (!(flags & F_WHITE_ON_MOVE) &&
1490 (flags & F_BLACK_QCASTLE_OK) &&
1491 board[BOARD_HEIGHT-1][ff] == bKing &&
1492 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1493 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1494 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1495 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1496 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1497 castlingRights[4] != NoRights && /* [HGM] check rights */
1498 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1500 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1501 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1502 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1504 callback(board, flags,
1505 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1506 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1510 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1512 /* generate all potential FRC castling moves (KxR), ignoring flags */
1513 /* [HGM] test if the Rooks we find have castling rights */
1514 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1517 if ((flags & F_WHITE_ON_MOVE) != 0) {
1518 ff = castlingRights[2]; /* King file if we have any rights */
1519 if(ff != NoRights && board[0][ff] == WhiteKing) {
1520 if (appData.debugMode) {
1521 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1522 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1524 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1526 right = BOARD_RGHT-2;
1527 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1528 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1529 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1530 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1531 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1532 if(ft != NoRights && board[0][ft] == WhiteRook) {
1533 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1534 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1537 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1538 left = BOARD_LEFT+2;
1540 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1541 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1542 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1543 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1544 if(ff > BOARD_LEFT+2)
1545 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1546 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1547 if(ft != NoRights && board[0][ft] == WhiteRook) {
1548 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1549 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1553 ff = castlingRights[5]; /* King file if we have any rights */
1554 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1555 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1557 right = BOARD_RGHT-2;
1558 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1559 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1560 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1561 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1562 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1563 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1564 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1565 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1568 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1569 left = BOARD_LEFT+2;
1571 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1572 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1573 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1574 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1575 if(ff > BOARD_LEFT+2)
1576 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1577 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1578 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1579 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1580 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1597 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1598 int rf, int ff, int rt, int ft,
1603 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1605 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1607 if (rt == cl->rking && ft == cl->fking) {
1608 if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1610 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1612 if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1613 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1614 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1618 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1619 he leave himself in check? Or if rf == -1, is the player on move
1620 in check now? enPassant must be TRUE if the indicated move is an
1621 e.p. capture. The possibility of castling out of a check along the
1622 back rank is not accounted for (i.e., we still return nonzero), as
1623 this is illegal anyway. Return value is the number of times the
1624 king is in check. */
1626 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1628 CheckTestClosure cl;
1629 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1630 ChessSquare captured = EmptySquare, ep=0, trampled=0;
1631 int saveKill = killX;
1632 /* Suppress warnings on uninitialized variables */
1634 if(gameInfo.variant == VariantXiangqi)
1635 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1636 if(gameInfo.variant == VariantKnightmate)
1637 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1638 if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check
1639 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1640 if(gameInfo.variant == VariantShogi) prince -= 11; // White/BlackFalcon
1641 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1642 if(board[r][f] == k || board[r][f] == prince) {
1643 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1644 king = board[r][f]; // remember hich one we had
1651 captured = board[rf][ft];
1652 board[rf][ft] = EmptySquare;
1654 captured = board[rt][ft];
1655 if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; }
1657 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1658 board[rt][ft] = board[rf][ff];
1659 if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
1661 ep = board[EP_STATUS];
1662 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1663 ChessSquare victim = killX < 0 ? EmptySquare : trampled;
1664 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1665 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1666 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
1667 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1671 /* For compatibility with ICS wild 9, we scan the board in the
1672 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1673 and we test only whether that one is in check. */
1674 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1675 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1676 if (board[cl.rking][cl.fking] == king) {
1678 if(gameInfo.variant == VariantXiangqi) {
1679 /* [HGM] In Xiangqi opposing Kings means check as well */
1681 dir = (king >= BlackPawn) ? -1 : 1;
1682 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1683 board[i][cl.fking] == EmptySquare; i+=dir );
1684 if(i>=0 && i<BOARD_HEIGHT &&
1685 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1688 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1689 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1690 goto undo_move; /* 2-level break */
1697 if(rf != DROP_RANK) // [HGM] drop
1698 board[rf][ff] = board[rt][ft];
1700 board[rf][ft] = captured;
1701 board[rt][ft] = EmptySquare;
1703 if(saveKill >= 0) board[killY][killX = saveKill] = trampled;
1704 board[rt][ft] = captured;
1706 board[EP_STATUS] = ep;
1709 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1713 HasLion (Board board, int flags)
1715 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1717 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1718 if(board[r][f] == lion) return 1;
1723 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1724 { // [HGM] put drop legality testing in separate routine for clarity
1726 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1727 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1728 n = PieceToNumber(piece);
1729 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1730 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1731 return ImpossibleMove; // piece not available
1732 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1733 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1734 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1735 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1736 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1737 if(piece == WhitePawn || piece == BlackPawn) {
1738 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1739 for(r=1; r<BOARD_HEIGHT-1; r++)
1740 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1741 // should still test if we mate with this Pawn
1743 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1744 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1746 if( (piece == WhitePawn || piece == BlackPawn) &&
1747 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1748 return IllegalMove; /* no pawn drops on 1st/8th */
1750 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1751 if (!(flags & F_IGNORE_CHECK) &&
1752 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1753 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1756 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1757 int rf, int ff, int rt, int ft,
1761 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1763 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1765 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1766 cl->captures++; // [HGM] losers: count legal captures
1767 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1772 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1774 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1776 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1777 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1778 piece = filterPiece = board[rf][ff];
1779 if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1781 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1782 /* (perhaps we should disallow moves that obviously leave us in check?) */
1783 if((piece == WhiteFalcon || piece == BlackFalcon ||
1784 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
1785 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1789 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1790 cl.ft = fFilter = ft;
1791 cl.kind = IllegalMove;
1792 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1793 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1794 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1795 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1796 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1797 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1799 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1800 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1801 if(board[rf][ff] < BlackPawn) { // white
1802 if(rf != 0) return IllegalMove; // must be on back rank
1803 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1804 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1805 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1806 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1808 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1809 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1810 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1811 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1812 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1815 if(gameInfo.variant == VariantChu) {
1816 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1817 if(promoChar != '+')
1818 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1819 if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
1820 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1822 if(gameInfo.variant == VariantShogi) {
1823 /* [HGM] Shogi promotions. '=' means defer */
1824 if(rf != DROP_RANK && cl.kind == NormalMove) {
1825 ChessSquare piece = board[rf][ff];
1827 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1828 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1829 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1830 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1831 promoChar = '+'; // allowed ICS notations
1832 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1833 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1834 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1835 else if(flags & F_WHITE_ON_MOVE) {
1836 if( (int) piece < (int) WhiteWazir &&
1837 (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1838 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1839 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1840 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1841 else /* promotion optional, default is defer */
1842 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1843 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1845 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1846 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1847 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1848 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1849 else /* promotion optional, default is defer */
1850 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1851 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1855 if (promoChar != NULLCHAR) {
1856 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1857 ChessSquare piece = board[rf][ff];
1858 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1859 // should test if in zone, really
1860 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1862 if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1864 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1865 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1866 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1867 if(piece == EmptySquare)
1868 cl.kind = ImpossibleMove; // non-existing piece
1869 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1870 cl.kind = IllegalMove; // no two Lions
1871 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1872 if(promoChar != PieceToChar(BlackKing)) {
1873 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1874 if(piece == BlackLance) cl.kind = ImpossibleMove;
1875 } else { // promotion to King allowed only if we do not have two yet
1876 int r, f, kings = 0;
1877 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1878 if(kings == 2) cl.kind = IllegalMove;
1880 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1881 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1882 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1883 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1884 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1885 cl.kind = IllegalMove; // promotion to King usually not allowed
1887 cl.kind = IllegalMove;
1897 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1898 int rf, int ff, int rt, int ft,
1902 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1904 register MateTestClosure *cl = (MateTestClosure *) closure;
1909 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1911 MateTest (Board board, int flags)
1914 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1915 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1917 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1918 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1919 nrKing += (board[r][f] == king); // stm has king
1920 if( board[r][f] != EmptySquare ) {
1921 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1926 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1927 case VariantShatranj:
1928 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1932 if(nrKing == 0) return MT_NOKING;
1935 if(myPieces == 1) return MT_BARE;
1938 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1939 // [HGM] 3check: yet to do!
1941 return inCheck ? MT_CHECK : MT_NONE;
1943 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1944 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1945 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1946 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1947 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1948 if(board[n][holdings] != EmptySquare) {
1949 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1950 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1953 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1954 return myPieces == hisPieces ? MT_STALEMATE :
1955 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1956 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1957 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1959 return inCheck ? MT_CHECKMATE
1960 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
1961 MT_STAINMATE : MT_STALEMATE;
1966 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1967 int rf, int ff, int rt, int ft,
1971 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1973 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1974 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1976 // [HGM] wild: for wild-card pieces rt and rf are dummies
1977 if(piece == WhiteFalcon || piece == BlackFalcon ||
1978 piece == WhiteCobra || piece == BlackCobra)
1981 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1982 || PieceToChar(board[rf][ff]) == '~'
1983 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1985 (cl->rfIn == -1 || cl->rfIn == rf) &&
1986 (cl->ffIn == -1 || cl->ffIn == ff) &&
1987 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1988 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1990 if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move
1993 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1994 // [HGM] oneclick: if multiple moves, be sure we remember capture
1995 cl->piece = board[rf][ff];
1998 cl->rt = wildCard ? cl->rtIn : rt;
1999 cl->ft = wildCard ? cl->ftIn : ft;
2002 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
2007 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
2009 int illegal = 0; char c = closure->promoCharIn;
2011 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
2012 closure->count = closure->captures = 0;
2013 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2014 closure->kind = ImpossibleMove;
2015 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
2016 fFilter = closure->ftIn;
2017 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
2018 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2019 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
2020 closure->count = closure->captures = 0;
2021 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2022 closure->kind = ImpossibleMove;
2023 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2026 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2027 if (closure->count == 0) {
2028 /* See if it's an illegal move due to check */
2030 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2031 if (closure->count == 0) {
2032 /* No, it's not even that */
2033 if(!appData.testLegality && closure->pieceIn != EmptySquare) {
2034 int f, r; // if there is only a single piece of the requested type on the board, use that
2035 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2036 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2037 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2038 if(closure->count > 1) illegal = 0; // ambiguous
2040 if(closure->count == 0) {
2041 if (appData.debugMode) { int i, j;
2042 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2043 for(j=0; j<BOARD_WIDTH; j++)
2044 fprintf(debugFP, "%3d", (int) board[i][j]);
2045 fprintf(debugFP, "\n");
2051 } else if(pieceDefs && closure->count > 1) { // [HGM] gen: move is ambiguous under engine-defined rules
2052 DisambiguateClosure spare = *closure;
2053 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
2054 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2055 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
2059 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2060 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2061 if(closure->piece < BlackPawn) { // white
2062 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2063 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2064 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2065 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2066 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2068 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2069 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2070 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2071 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2072 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2075 if(gameInfo.variant == VariantChu) {
2076 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2078 if(gameInfo.variant == VariantShogi) {
2079 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2080 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2081 ChessSquare piece = closure->piece;
2082 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
2083 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2084 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2085 c = '+'; // allowed ICS notations
2086 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2087 else if(flags & F_WHITE_ON_MOVE) {
2088 if( (int) piece < (int) WhiteWazir &&
2089 (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
2090 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2091 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2092 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2093 else /* promotion optional, default is defer */
2094 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2095 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2097 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
2098 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2099 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2100 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2101 else /* promotion optional, default is defer */
2102 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2103 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2106 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2107 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2109 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2110 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2111 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2112 gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
2113 c = PieceToChar(BlackFerz);
2114 else if(gameInfo.variant == VariantGreat)
2115 c = PieceToChar(BlackMan);
2116 else if(gameInfo.variant == VariantGrand)
2117 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2119 c = PieceToChar(BlackQueen);
2120 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2121 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2122 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2123 ChessSquare p = closure->piece;
2124 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
2125 closure->kind = ImpossibleMove; // used on non-promotable piece
2126 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2127 } else if (c != NULLCHAR) closure->kind = IllegalMove;
2129 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2130 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2131 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2132 if (closure->count > 1) {
2133 closure->kind = AmbiguousMove;
2136 /* Note: If more than one illegal move matches, but no legal
2137 moves, we return IllegalMove, not AmbiguousMove. Caller
2138 can look at closure->count to detect this.
2140 closure->kind = IllegalMove;
2154 } CoordsToAlgebraicClosure;
2156 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2157 ChessMove kind, int rf, int ff,
2158 int rt, int ft, VOIDSTAR closure));
2161 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2163 register CoordsToAlgebraicClosure *cl =
2164 (CoordsToAlgebraicClosure *) closure;
2166 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2167 (board[rf][ff] == cl->piece
2168 || PieceToChar(board[rf][ff]) == '~' &&
2169 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
2173 cl->kind = kind; /* this is the move we want */
2175 cl->file++; /* need file to rule out this move */
2179 cl->rank++; /* need rank to rule out this move */
2181 cl->either++; /* rank or file will rule out this move */
2187 /* Convert coordinates to normal algebraic notation.
2188 promoChar must be NULLCHAR or 'x' if not a promotion.
2191 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2195 char *outp = out, c, capture;
2196 CoordsToAlgebraicClosure cl;
2198 if (rf == DROP_RANK) {
2199 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2200 /* Bughouse piece drop */
2201 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2206 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2208 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2211 if (promoChar == 'x') promoChar = NULLCHAR;
2212 piece = board[rf][ff];
2213 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
2218 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2219 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2220 /* Keep short notation if move is illegal only because it
2221 leaves the player in check, but still return IllegalMove */
2222 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2223 if (kind == IllegalMove) break;
2228 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2229 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2230 /* Non-capture; use style "e5" */
2233 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2235 /* Capture; use style "exd5" */
2237 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2241 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2243 /* Use promotion suffix style "=Q" */
2245 if (promoChar != NULLCHAR) {
2246 if(IS_SHOGI(gameInfo.variant)) {
2247 /* [HGM] ... but not in Shogi! */
2248 *outp++ = promoChar == '=' ? '=' : '+';
2251 *outp++ = ToUpper(promoChar);
2260 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2261 /* Code added by Tord: FRC castling. */
2262 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2263 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2265 safeStrCpy(out, "O-O", MOVE_LEN);
2267 safeStrCpy(out, "O-O-O", MOVE_LEN);
2268 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2270 /* End of code added by Tord */
2271 /* Test for castling or ICS wild castling */
2272 /* Use style "O-O" (oh-oh) for PGN compatibility */
2273 else if (rf == rt &&
2274 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2275 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2276 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2277 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2278 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2279 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2281 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2283 /* This notation is always unambiguous, unless there are
2284 kings on both the d and e files, with "wild castling"
2285 possible for the king on the d file and normal castling
2286 possible for the other. ICS rules for wild 9
2287 effectively make castling illegal for either king in
2288 this situation. So I am not going to worry about it;
2289 I'll just generate an ambiguous O-O in this case.
2291 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2294 /* else fall through */
2299 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2300 cl.ft = fFilter = ft;
2302 cl.kind = IllegalMove;
2303 cl.rank = cl.file = cl.either = 0;
2304 c = PieceToChar(piece) ;
2305 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
2307 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2308 /* Generate pretty moves for moving into check, but
2309 still return IllegalMove.
2311 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
2312 if (cl.kind == IllegalMove) break;
2313 cl.kind = IllegalMove;
2316 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2317 else "Ngf3" or "Ngxf7",
2318 else "N1f3" or "N5xf7",
2319 else "Ng1f3" or "Ng5xf7".
2321 if( c == '~' || c == '+') {
2322 /* [HGM] print nonexistent piece as its demoted version */
2323 piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
2325 if(c=='+') *outp++ = c;
2326 *outp++ = ToUpper(PieceToChar(piece));
2328 if (cl.file || (cl.either && !cl.rank)) {
2334 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2337 if(board[rt][ft] != EmptySquare)
2343 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2344 if (IS_SHOGI(gameInfo.variant)) {
2345 /* [HGM] in Shogi non-pawns can promote */
2346 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2348 else if (gameInfo.variant == VariantChuChess && promoChar ||
2349 gameInfo.variant != VariantSuper && promoChar &&
2350 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2352 *outp++ = ToUpper(promoChar);
2354 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2356 *outp++ = ToUpper(promoChar);
2362 /* Moving a nonexistent piece */
2366 /* Not a legal move, even ignoring check.
2367 If there was a piece on the from square,
2368 use style "Ng1g3" or "Ng1xe8";
2369 if there was a pawn or nothing (!),
2370 use style "g1g3" or "g1xe8". Use "x"
2371 if a piece was on the to square, even
2372 a piece of the same color.
2376 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2378 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2379 c += (board[r][f] == piece); // count on-board pieces of given type
2380 *outp++ = ToUpper(PieceToChar(piece));
2382 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2386 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2388 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2392 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2393 /* Use promotion suffix style "=Q" */
2394 if (promoChar != NULLCHAR && promoChar != 'x') {
2396 *outp++ = ToUpper(promoChar);
2403 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2412 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2414 int preyStackPointer, chaseStackPointer;
2417 unsigned char rf, ff, rt, ft;
2421 unsigned char rank, file;
2427 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2429 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2430 int rf, int ff, int rt, int ft,
2434 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2435 { // For adding captures that can lead to chase indictment to the chaseStack
2436 if(board[rt][ft] == EmptySquare) return; // non-capture
2437 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2438 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2439 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2440 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2441 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2442 chaseStack[chaseStackPointer].rf = rf;
2443 chaseStack[chaseStackPointer].ff = ff;
2444 chaseStack[chaseStackPointer].rt = rt;
2445 chaseStack[chaseStackPointer].ft = ft;
2446 chaseStackPointer++;
2449 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2450 int rf, int ff, int rt, int ft,
2454 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2455 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2457 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2459 if(board[rt][ft] == EmptySquare) return; // no capture
2460 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2461 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2463 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2464 for(i=0; i<chaseStackPointer; i++) {
2465 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2466 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2467 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2468 chaseStack[i] = chaseStack[--chaseStackPointer];
2474 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2475 int rf, int ff, int rt, int ft,
2479 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2480 { // for determining if a piece (given through the closure) is protected
2481 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2483 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2484 if(appData.debugMode && board[rt][ft] != EmptySquare)
2485 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2488 extern char moveList[MAX_MOVES][MOVE_LEN];
2491 PerpetualChase (int first, int last)
2492 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2495 ChessSquare captured;
2497 preyStackPointer = 0; // clear stack of chased pieces
2498 for(i=first; i<last; i+=2) { // for all positions with same side to move
2499 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2500 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2501 // determine all captures possible after the move, and put them on chaseStack
2502 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2503 if(appData.debugMode) { int n;
2504 for(n=0; n<chaseStackPointer; n++)
2505 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2506 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2507 fprintf(debugFP, ": all capts\n");
2509 // determine all captures possible before the move, and delete them from chaseStack
2510 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2511 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2512 cl.rt = moveList[i][3]-ONE;
2513 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2514 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2515 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2516 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2517 if(appData.debugMode) { int n;
2518 for(n=0; n<chaseStackPointer; n++)
2519 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2520 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2521 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2523 // chaseSack now contains all captures made possible by the move
2524 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2525 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2526 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2528 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2529 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2531 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2532 continue; // C or H attack on R is always chase; leave on chaseStack
2534 if(attacker == victim) {
2535 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2536 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2537 // we can capture back with equal piece, so this is no chase but a sacrifice
2538 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2539 j--; /* ! */ continue;
2544 // the attack is on a lower piece, or on a pinned or blocked equal one
2545 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2546 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2547 // test if the victim is protected by a true protector. First make the capture.
2548 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2549 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2550 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2551 // Then test if the opponent can recapture
2552 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2553 cl.rt = chaseStack[j].rt;
2554 cl.ft = chaseStack[j].ft;
2555 if(appData.debugMode) {
2556 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2558 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2559 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2560 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2561 // unmake the capture
2562 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2563 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2564 // if a recapture was found, piece is protected, and we are not chasing it.
2565 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2566 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2570 // chaseStack now contains all moves that chased
2571 if(appData.debugMode) { int n;
2572 for(n=0; n<chaseStackPointer; n++)
2573 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2574 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2575 fprintf(debugFP, ": chases\n");
2577 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2578 for(j=0; j<chaseStackPointer; j++) {
2579 preyStack[j].rank = chaseStack[j].rt;
2580 preyStack[j].file = chaseStack[j].ft;
2582 preyStackPointer = chaseStackPointer;
2585 for(j=0; j<chaseStackPointer; j++) {
2586 for(k=0; k<preyStackPointer; k++) {
2587 // search the victim of each chase move on the preyStack (first occurrence)
2588 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2589 if(k < tail) break; // piece was already identified as still being chased
2590 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2591 preyStack[tail] = preyStack[k]; // by swapping
2592 preyStack[k] = preyStack[preyStackPointer];
2598 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2599 if(appData.debugMode) { int n;
2600 for(n=0; n<preyStackPointer; n++)
2601 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2602 fprintf(debugFP, "always chased upto ply %d\n", i);
2604 // now adjust the location of the chased pieces according to opponent move
2605 for(j=0; j<preyStackPointer; j++) {
2606 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2607 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2608 preyStack[j].rank = moveList[i+1][3]-ONE;
2609 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2614 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2615 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the