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];
1826 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
1828 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1829 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1830 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1831 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1832 promoChar = '+'; // allowed ICS notations
1833 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1834 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1835 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1836 else if(flags & F_WHITE_ON_MOVE) {
1837 if( (int) piece < (int) WhiteWazir &&
1838 (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) {
1839 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1840 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1841 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1842 else /* promotion optional, default is defer */
1843 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1844 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1846 if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) {
1847 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1848 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1849 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1850 else /* promotion optional, default is defer */
1851 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1852 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1856 if (promoChar != NULLCHAR) {
1857 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1858 ChessSquare piece = board[rf][ff];
1859 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1860 // should test if in zone, really
1861 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1863 if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1865 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1866 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1867 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1868 if(piece == EmptySquare)
1869 cl.kind = ImpossibleMove; // non-existing piece
1870 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1871 cl.kind = IllegalMove; // no two Lions
1872 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1873 if(promoChar != PieceToChar(BlackKing)) {
1874 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1875 if(piece == BlackLance) cl.kind = ImpossibleMove;
1876 } else { // promotion to King allowed only if we do not have two yet
1877 int r, f, kings = 0;
1878 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1879 if(kings == 2) cl.kind = IllegalMove;
1881 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1882 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1883 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1884 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1885 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1886 cl.kind = IllegalMove; // promotion to King usually not allowed
1888 cl.kind = IllegalMove;
1898 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1899 int rf, int ff, int rt, int ft,
1903 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1905 register MateTestClosure *cl = (MateTestClosure *) closure;
1910 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1912 MateTest (Board board, int flags)
1915 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1916 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1918 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1919 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1920 nrKing += (board[r][f] == king); // stm has king
1921 if( board[r][f] != EmptySquare ) {
1922 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1927 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1928 case VariantShatranj:
1929 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1933 if(nrKing == 0) return MT_NOKING;
1936 if(myPieces == 1) return MT_BARE;
1939 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1940 // [HGM] 3check: yet to do!
1942 return inCheck ? MT_CHECK : MT_NONE;
1944 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1945 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1946 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1947 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1948 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1949 if(board[n][holdings] != EmptySquare) {
1950 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1951 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1954 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1955 return myPieces == hisPieces ? MT_STALEMATE :
1956 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1957 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1958 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1960 return inCheck ? MT_CHECKMATE
1961 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
1962 MT_STAINMATE : MT_STALEMATE;
1967 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1968 int rf, int ff, int rt, int ft,
1972 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1974 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1975 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1976 extern int kifu; // in parser.c
1978 // [HGM] wild: for wild-card pieces rt and rf are dummies
1979 if(piece == WhiteFalcon || piece == BlackFalcon ||
1980 piece == WhiteCobra || piece == BlackCobra)
1983 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1984 || PieceToChar(board[rf][ff]) == '~'
1985 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1987 (cl->rfIn == -1 || cl->rfIn == rf) &&
1988 (cl->ffIn == -1 || cl->ffIn == ff) &&
1989 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1990 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1992 if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move
1994 if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required
1995 int this = 1, other = 1;
1996 if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf);
1997 if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf);
1998 if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf);
1999 if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff);
2000 if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff);
2001 if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used
2002 if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it
2003 if(!this) return; // the current move does not satisfy the requested relative position, ignore it
2007 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
2008 // [HGM] oneclick: if multiple moves, be sure we remember capture
2009 cl->piece = board[rf][ff];
2012 cl->rt = wildCard ? cl->rtIn : rt;
2013 cl->ft = wildCard ? cl->ftIn : ft;
2016 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
2021 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
2023 int illegal = 0; char c = closure->promoCharIn;
2025 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
2026 closure->count = closure->captures = 0;
2027 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2028 closure->kind = ImpossibleMove;
2029 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
2030 fFilter = closure->ftIn;
2031 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
2032 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2033 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
2034 closure->count = closure->captures = 0;
2035 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2036 closure->kind = ImpossibleMove;
2037 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2040 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2041 if (closure->count == 0) {
2042 /* See if it's an illegal move due to check */
2044 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2045 if (closure->count == 0) {
2046 /* No, it's not even that */
2047 if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) {
2048 int f, r; // if there is only a single piece of the requested type on the board, use that
2049 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2050 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2051 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2052 if(closure->count > 1) illegal = 0; // ambiguous
2054 if(closure->count == 0) {
2055 if (appData.debugMode) { int i, j;
2056 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2057 for(j=0; j<BOARD_WIDTH; j++)
2058 fprintf(debugFP, "%3d", (int) board[i][j]);
2059 fprintf(debugFP, "\n");
2065 } else if(pieceDefs && closure->count > 1) { // [HGM] gen: move is ambiguous under engine-defined rules
2066 DisambiguateClosure spare = *closure;
2067 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
2068 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2069 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
2073 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2074 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2075 if(closure->piece < BlackPawn) { // white
2076 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2077 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2078 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2079 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2080 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2082 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2083 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2084 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2085 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2086 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2089 if(gameInfo.variant == VariantChu) {
2090 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2092 if(gameInfo.variant == VariantShogi) {
2093 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2094 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2095 ChessSquare piece = closure->piece;
2096 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
2097 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
2098 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2099 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2100 c = '+'; // allowed ICS notations
2101 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2102 else if(flags & F_WHITE_ON_MOVE) {
2103 if( (int) piece < (int) WhiteWazir &&
2104 (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) {
2105 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2106 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2107 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2108 else /* promotion optional, default is defer */
2109 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2110 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2112 if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) {
2113 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2114 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2115 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2116 else /* promotion optional, default is defer */
2117 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2118 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2121 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2122 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2124 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2125 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2126 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2127 gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
2128 c = PieceToChar(BlackFerz);
2129 else if(gameInfo.variant == VariantGreat)
2130 c = PieceToChar(BlackMan);
2131 else if(gameInfo.variant == VariantGrand)
2132 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2134 c = PieceToChar(BlackQueen);
2135 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2136 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2137 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2138 ChessSquare p = closure->piece;
2139 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
2140 closure->kind = ImpossibleMove; // used on non-promotable piece
2141 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2142 } else if (c != NULLCHAR) closure->kind = IllegalMove;
2144 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2145 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2146 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2147 if (closure->count > 1) {
2148 closure->kind = AmbiguousMove;
2151 /* Note: If more than one illegal move matches, but no legal
2152 moves, we return IllegalMove, not AmbiguousMove. Caller
2153 can look at closure->count to detect this.
2155 closure->kind = IllegalMove;
2169 } CoordsToAlgebraicClosure;
2171 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2172 ChessMove kind, int rf, int ff,
2173 int rt, int ft, VOIDSTAR closure));
2176 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2178 register CoordsToAlgebraicClosure *cl =
2179 (CoordsToAlgebraicClosure *) closure;
2181 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2182 (board[rf][ff] == cl->piece
2183 || PieceToChar(board[rf][ff]) == '~' &&
2184 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
2188 cl->kind = kind; /* this is the move we want */
2190 cl->file++; /* need file to rule out this move */
2194 cl->rank++; /* need rank to rule out this move */
2196 cl->either++; /* rank or file will rule out this move */
2202 /* Convert coordinates to normal algebraic notation.
2203 promoChar must be NULLCHAR or 'x' if not a promotion.
2206 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2210 char *outp = out, c, capture;
2211 CoordsToAlgebraicClosure cl;
2213 if (rf == DROP_RANK) {
2214 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2215 /* Bughouse piece drop */
2216 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2221 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2223 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2226 if (promoChar == 'x') promoChar = NULLCHAR;
2227 piece = board[rf][ff];
2228 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
2233 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2234 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2235 /* Keep short notation if move is illegal only because it
2236 leaves the player in check, but still return IllegalMove */
2237 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2238 if (kind == IllegalMove) break;
2243 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2244 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2245 /* Non-capture; use style "e5" */
2248 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2250 /* Capture; use style "exd5" */
2252 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2256 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2258 /* Use promotion suffix style "=Q" */
2260 if (promoChar != NULLCHAR) {
2261 if(IS_SHOGI(gameInfo.variant)) {
2262 /* [HGM] ... but not in Shogi! */
2263 *outp++ = promoChar == '=' ? '=' : '+';
2266 *outp++ = ToUpper(promoChar);
2275 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2276 /* Code added by Tord: FRC castling. */
2277 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2278 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2280 safeStrCpy(out, "O-O", MOVE_LEN);
2282 safeStrCpy(out, "O-O-O", MOVE_LEN);
2283 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2285 /* End of code added by Tord */
2286 /* Test for castling or ICS wild castling */
2287 /* Use style "O-O" (oh-oh) for PGN compatibility */
2288 else if (rf == rt &&
2289 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2290 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2291 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2292 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2293 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2294 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2296 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2298 /* This notation is always unambiguous, unless there are
2299 kings on both the d and e files, with "wild castling"
2300 possible for the king on the d file and normal castling
2301 possible for the other. ICS rules for wild 9
2302 effectively make castling illegal for either king in
2303 this situation. So I am not going to worry about it;
2304 I'll just generate an ambiguous O-O in this case.
2306 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2309 /* else fall through */
2314 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2315 cl.ft = fFilter = ft;
2317 cl.kind = IllegalMove;
2318 cl.rank = cl.file = cl.either = 0;
2319 c = PieceToChar(piece) ;
2320 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
2322 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2323 /* Generate pretty moves for moving into check, but
2324 still return IllegalMove.
2326 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
2327 if (cl.kind == IllegalMove) break;
2328 cl.kind = IllegalMove;
2331 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2332 else "Ngf3" or "Ngxf7",
2333 else "N1f3" or "N5xf7",
2334 else "Ng1f3" or "Ng5xf7".
2336 if( c == '~' || c == '+') {
2337 /* [HGM] print nonexistent piece as its demoted version */
2338 piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
2340 if(c=='+') *outp++ = c;
2341 *outp++ = ToUpper(PieceToChar(piece));
2343 if (cl.file || (cl.either && !cl.rank)) {
2349 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2352 if(board[rt][ft] != EmptySquare)
2358 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2359 if (IS_SHOGI(gameInfo.variant)) {
2360 /* [HGM] in Shogi non-pawns can promote */
2361 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2363 else if (gameInfo.variant == VariantChuChess && promoChar ||
2364 gameInfo.variant != VariantSuper && promoChar &&
2365 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2367 *outp++ = ToUpper(promoChar);
2369 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2371 *outp++ = ToUpper(promoChar);
2377 /* Moving a nonexistent piece */
2381 /* Not a legal move, even ignoring check.
2382 If there was a piece on the from square,
2383 use style "Ng1g3" or "Ng1xe8";
2384 if there was a pawn or nothing (!),
2385 use style "g1g3" or "g1xe8". Use "x"
2386 if a piece was on the to square, even
2387 a piece of the same color.
2391 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2393 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2394 c += (board[r][f] == piece); // count on-board pieces of given type
2395 *outp++ = ToUpper(PieceToChar(piece));
2397 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2401 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2403 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2407 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2408 /* Use promotion suffix style "=Q" */
2409 if (promoChar != NULLCHAR && promoChar != 'x') {
2411 *outp++ = ToUpper(promoChar);
2418 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2427 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2429 int preyStackPointer, chaseStackPointer;
2432 unsigned char rf, ff, rt, ft;
2436 unsigned char rank, file;
2442 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2444 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2445 int rf, int ff, int rt, int ft,
2449 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2450 { // For adding captures that can lead to chase indictment to the chaseStack
2451 if(board[rt][ft] == EmptySquare) return; // non-capture
2452 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2453 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2454 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2455 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2456 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2457 chaseStack[chaseStackPointer].rf = rf;
2458 chaseStack[chaseStackPointer].ff = ff;
2459 chaseStack[chaseStackPointer].rt = rt;
2460 chaseStack[chaseStackPointer].ft = ft;
2461 chaseStackPointer++;
2464 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2465 int rf, int ff, int rt, int ft,
2469 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2470 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2472 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2474 if(board[rt][ft] == EmptySquare) return; // no capture
2475 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2476 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2478 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2479 for(i=0; i<chaseStackPointer; i++) {
2480 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2481 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2482 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2483 chaseStack[i] = chaseStack[--chaseStackPointer];
2489 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2490 int rf, int ff, int rt, int ft,
2494 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2495 { // for determining if a piece (given through the closure) is protected
2496 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2498 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2499 if(appData.debugMode && board[rt][ft] != EmptySquare)
2500 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2503 extern char moveList[MAX_MOVES][MOVE_LEN];
2506 PerpetualChase (int first, int last)
2507 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2510 ChessSquare captured;
2512 preyStackPointer = 0; // clear stack of chased pieces
2513 for(i=first; i<last; i+=2) { // for all positions with same side to move
2514 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2515 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2516 // determine all captures possible after the move, and put them on chaseStack
2517 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2518 if(appData.debugMode) { int n;
2519 for(n=0; n<chaseStackPointer; n++)
2520 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2521 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2522 fprintf(debugFP, ": all capts\n");
2524 // determine all captures possible before the move, and delete them from chaseStack
2525 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2526 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2527 cl.rt = moveList[i][3]-ONE;
2528 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2529 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2530 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2531 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2532 if(appData.debugMode) { int n;
2533 for(n=0; n<chaseStackPointer; n++)
2534 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2535 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2536 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2538 // chaseSack now contains all captures made possible by the move
2539 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2540 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2541 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2543 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2544 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2546 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2547 continue; // C or H attack on R is always chase; leave on chaseStack
2549 if(attacker == victim) {
2550 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2551 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2552 // we can capture back with equal piece, so this is no chase but a sacrifice
2553 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2554 j--; /* ! */ continue;
2559 // the attack is on a lower piece, or on a pinned or blocked equal one
2560 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2561 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2562 // test if the victim is protected by a true protector. First make the capture.
2563 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2564 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2565 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2566 // Then test if the opponent can recapture
2567 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2568 cl.rt = chaseStack[j].rt;
2569 cl.ft = chaseStack[j].ft;
2570 if(appData.debugMode) {
2571 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2573 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2574 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2575 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2576 // unmake the capture
2577 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2578 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2579 // if a recapture was found, piece is protected, and we are not chasing it.
2580 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2581 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2585 // chaseStack now contains all moves that chased
2586 if(appData.debugMode) { int n;
2587 for(n=0; n<chaseStackPointer; n++)
2588 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2589 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2590 fprintf(debugFP, ": chases\n");
2592 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2593 for(j=0; j<chaseStackPointer; j++) {
2594 preyStack[j].rank = chaseStack[j].rt;
2595 preyStack[j].file = chaseStack[j].ft;
2597 preyStackPointer = chaseStackPointer;
2600 for(j=0; j<chaseStackPointer; j++) {
2601 for(k=0; k<preyStackPointer; k++) {
2602 // search the victim of each chase move on the preyStack (first occurrence)
2603 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2604 if(k < tail) break; // piece was already identified as still being chased
2605 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2606 preyStack[tail] = preyStack[k]; // by swapping
2607 preyStack[k] = preyStack[preyStackPointer];
2613 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2614 if(appData.debugMode) { int n;
2615 for(n=0; n<preyStackPointer; n++)
2616 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2617 fprintf(debugFP, "always chased upto ply %d\n", i);
2619 // now adjust the location of the chased pieces according to opponent move
2620 for(j=0; j<preyStackPointer; j++) {
2621 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2622 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2623 preyStack[j].rank = moveList[i+1][3]-ONE;
2624 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2629 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2630 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the