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[] = "AKCD.QGH.JQL.NO.KK....Q..Z";
240 // 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 "
241 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 };
242 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 };
243 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 };
244 int dirs4[] = { 0,0x10,0,0,0,0x01,0,0xFF,0,0,0,0x40,0,0,0,0,0,0x04,0x44,0,0,0x11,0,0,0,0 };
246 int rot[][4] = { // rotation matrices for each direction
258 OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR cl)
264 MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, char *desc, MoveCallback cb, VOIDSTAR cl)
266 char buf[80], *p = desc;
267 int mine, his, dir, bit, occup, i;
268 if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
269 while(*p) { // more moves to go
270 int expo = 1, dx, dy, x, y, mode, dirSet, ds2, retry=0, initial=0, jump=1, skip = 0;
272 if(*p == 'i') initial = 1, desc = ++p;
273 while(islower(*p)) p++; // skip prefixes
274 if(!isupper(*p)) return; // syntax error: no atom
275 dx = xStep[*p-'A'] - '0';// step vector of atom
276 dy = yStep[*p-'A'] - '0';
277 dirSet = 0; // build direction set based on atom symmetry
278 switch(symmetry[*p-'A']) {
279 case 'B': expo = 0; // bishop, slide
280 case 'F': // diagonal atom (degenerate 4-fold)
281 if(tx < 0) { // for continuation legs relative directions are orthogonal!
282 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
283 int b = dirs1[*desc-'a']; // use wide version
284 if( islower(desc[1]) &&
285 ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
286 b = dirs1[*desc-'a'] & dirs1[desc[1]-'a']; // intersect wide & perp wide
291 dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
294 case 'R': expo = 0; // rook, slide
295 case 'W': // orthogonal atom (non-deg 4-fold)
296 while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
297 dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
298 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
300 case 'N': // oblique atom (degenerate 8-fold)
301 if(tx < 0) { // for continuation legs relative directions are non-degenerate!
302 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
303 int b = dirs2[*desc-'a']; // when alone, use narrow version
304 if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
305 else if(*desc == desc[1] || islower(desc[1]) && i < '4'
306 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
307 b = dirs1[*desc-'a'] & dirs2[desc[1]-'a']; // intersect wide & perp narrow
312 if(!dirSet) dirSet = 0xFF;
315 case 'Q': expo = 0; // queen, slide
316 case 'K': // non-deg (pseudo) 8-fold
317 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
318 int b = dirs4[*desc-'a']; // when alone, use narrow version
319 if(desc[1] == *desc) desc++; // doubling forces alone
320 else if(islower(desc[1]) && i < '4'
321 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
322 b = dirs3[*desc-'a'] & dirs3[desc[1]-'a']; // intersect wide & perp wide
327 if(!dirSet) dirSet = 0xFF;
328 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
329 ds2 = dirSet & 0xAA; // extract diagonal directions
330 if(dirSet &= 0x55) // start with orthogonal moves, if present
331 retry = 1; // and schedule the diagonal moves for later
332 else dx = 1, dirSet = ds2; // if no orthogonal directions, do diagonal immediately
333 break; // should not have direction indicators
334 default: return; // syntax error: invalid atom
336 if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
337 mode = 0; // build mode mask
338 if(*desc == 'm') mode |= 4, desc++; // move to empty
339 if(*desc == 'c') mode |= his, desc++; // capture foe
340 if(*desc == 'd') mode |= mine, desc++; // destroy (capture friend)
341 if(*desc == 'e') mode |= 8, desc++; // e.p. capture last mover
342 if(*desc == 't') mode |= 16, desc++; // exclude enemies as hop platform ('test')
343 if(*desc == 'p') mode |= 32, desc++; // hop over occupied
344 if(*desc == 'g') mode |= 64, desc++; // hop and toggle range
345 if(*desc == 'o') mode |= 128, desc++; // wrap around cylinder board
346 if(*desc == 'y') mode |= 512, desc++; // toggle range on empty square
347 if(*desc == 'n') jump = 0, desc++; // non-jumping
348 while(*desc == 'j') jump++, desc++; // must jump (on B,R,Q: skip first square)
349 if(*desc == 'a') cont = ++desc; // move again after doing what preceded it
350 if(isdigit(*++p)) expo = atoi(p++); // read exponent
351 if(expo > 9) p++; // allow double-digit
352 desc = p; // this is start of next move
353 if(initial && (board[r][f] != initialPosition[r][f] ||
354 r == 0 && board[TOUCHED_W] & 1<<f ||
355 r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f ) ) continue;
356 if(expo > 1 && dx == 0 && dy == 0) { // castling indicated by O + number
357 mode |= 1024; dy = 1;
360 if(!(mode & 15)) mode = his + 4; // no mode spec, use default = mc
362 if(mode & 32) mode ^= 256 + 32; // in non-final legs 'p' means 'pass through'
363 if(mode & 64 + 512) {
364 mode |= 256; // and 'g' too, but converts leaper <-> slider
365 if(mode & 512) mode ^= 0x304; // and 'y' is m-like 'g'
366 strncpy(buf, cont, 80); cont = buf; // copy next leg(s), so we can modify
367 while(islower(*cont)) cont++; // skip to atom
368 *cont = upgrade[*cont-'A']; // replace atom, BRQ <-> FWK
369 if(expo == 1) *++cont = '0'; // turn other leapers into riders
370 *++cont = '\0'; // make sure any old range is stripped off
371 cont = buf; // use modified string for continuation leg
373 if(!(mode & 0x30F)) mode = his + 0x104; // and default = mcp
375 if(dy == 1) skip = jump - 1, jump = 1; // on W & F atoms 'j' = skip first square
377 for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
378 int i = expo, j = skip, hop = mode, vx, vy;
379 if(!(bit & dirSet)) continue; // does not move in this direction
380 if(dy != 1) j = 0; //
381 vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector
382 vy = dx*rot[dir][2] + dy*rot[dir][3];
383 if(tx < 0) x = f, y = r; // start square
384 else x = tx, y = ty; // from previous to-square if continuation
386 x += vx; y += vy; // step to next square
387 if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
388 if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT; else break; }
389 if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT; else break; }
390 if(j) { j--; continue; } // skip irrespective of occupation
391 if(!jump && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
392 if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
393 if(x == f && y == r) occup = 4; else // start square counts as empty
394 if(board[y][x] < BlackPawn) occup = 0x101; else
395 if(board[y][x] < EmptySquare) occup = 0x102; else
397 if(cont) { // non-final leg
398 if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode
399 if(occup & mode) { // valid intermediate square, do continuation
400 if(occup & mode & 0x104) // no side effects, merge legs to one move
401 MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
402 if(occup & mode & 3 && (killX < 0 || killX == x && killY == y)) { // destructive first leg
404 MovesFromString(board, flags, f, r, x, y, dir, cont, &OK, &cnt); // count possible continuations
405 if(cnt) { // and if there are
406 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); // then generate their first leg
408 MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
413 if(occup != 4) break; // occupied squares always terminate the leg
416 if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper
417 if(mode & 8 && y == board[EP_RANK] && occup == 4 && board[EP_FILE] == x) { // to e.p. square
418 cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
420 if(mode & 1024) { // castling
421 i = 2; // kludge to elongate move indefinitely
422 if(occup == 4) continue; // skip empty squares
423 if(x == BOARD_LEFT && board[y][x] == initialPosition[y][x]) // reached initial corner piece
424 cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
425 if(x == BOARD_RGHT-1 && board[y][x] == initialPosition[y][x])
426 cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
429 if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl); // allowed, generate
430 if(occup != 4) break; // not valid transit square
433 dx = dy = 1; dirSet = ds2; // prepare for diagonal moves of K,Q
434 } while(retry-- && ds2); // and start doing them
435 if(tx >= 0) break; // don't do other atoms in continuation legs
439 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
442 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
447 if (rt >= BOARD_HEIGHT) break;
448 if (SameColor(board[rf][ff], board[rt][ft])) break;
449 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
450 if (board[rt][ft] != EmptySquare) break;
455 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
461 if (SameColor(board[rf][ff], board[rt][ft])) break;
462 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
463 if (board[rt][ft] != EmptySquare) break;
468 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
470 SlideForward(board, flags, rf, ff, callback, closure);
471 SlideBackward(board, flags, rf, ff, callback, closure);
475 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
477 int i, s, rt = rf, ft;
478 for(s = -1; s <= 1; s+= 2) {
481 if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
482 if (SameColor(board[rf][ff], board[rt][ft])) break;
483 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
484 if (board[rt][ft] != EmptySquare) break;
490 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
493 for(s = -1; s <= 1; s+= 2) {
497 if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
498 if (SameColor(board[rf][ff], board[rt][ft])) break;
499 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
500 if (board[rt][ft] != EmptySquare) break;
506 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
509 for(s = -1; s <= 1; s+= 2) {
513 if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
514 if (SameColor(board[rf][ff], board[rt][ft])) break;
515 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
516 if (board[rt][ft] != EmptySquare) break;
522 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
524 SlideVertical(board, flags, rf, ff, callback, closure);
525 SlideSideways(board, flags, rf, ff, callback, closure);
529 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
531 SlideDiagForward(board, flags, rf, ff, callback, closure);
532 SlideDiagBackward(board, flags, rf, ff, callback, closure);
536 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
537 { // Lion-like move of Horned Falcon and Souring Eagle
538 int ft = ff + dx, rt = rf + dy;
539 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
541 if (!SameColor(board[rf][ff], board[rt][ft]))
542 callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
545 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
547 if (!SameColor(board[rf][ff], board[rt][ft]))
548 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
549 if (!SameColor(board[rf][ff], board[rf+dy][ff+dx]))
550 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
555 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
557 int ft = ff, rt = rf + 1;
558 if (rt >= BOARD_HEIGHT) return;
559 if (SameColor(board[rf][ff], board[rt][ft])) return;
560 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
564 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
566 int ft = ff, rt = rf - 1;
568 if (SameColor(board[rf][ff], board[rt][ft])) return;
569 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
573 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
577 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
578 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
580 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
581 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
585 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
588 if (rt >= BOARD_HEIGHT) return;
590 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
591 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
593 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
594 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
598 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
603 if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
604 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
606 if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
607 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
611 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
613 StepForward(board, flags, rf, ff, callback, closure);
614 StepBackward(board, flags, rf, ff, callback, closure);
618 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
620 StepDiagForward(board, flags, rf, ff, callback, closure);
621 StepDiagBackward(board, flags, rf, ff, callback, closure);
625 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
627 StepVertical(board, flags, rf, ff, callback, closure);
628 StepSideways(board, flags, rf, ff, callback, closure);
632 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
635 for (i = -1; i <= 1; i += 2)
636 for (j = -1; j <= 1; j += 2)
637 for (s = 1; s <= 2; s++) {
640 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
641 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
642 && !SameColor(board[rf][ff], board[rt][ft]))
643 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
647 /* Call callback once for each pseudo-legal move in the given
648 position, except castling moves. A move is pseudo-legal if it is
649 legal, or if it would be legal except that it leaves the king in
650 check. In the arguments, epfile is EP_NONE if the previous move
651 was not a double pawn push, or the file 0..7 if it was, or
652 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
653 Promotion moves generated are to Queen only.
656 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
657 // speed: only do moves with this piece type
660 int i, j, d, s, fs, rs, rt, ft, m;
661 int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
662 int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
664 for (rf = 0; rf < BOARD_HEIGHT; rf++)
665 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
668 if(board[rf][ff] == EmptySquare) continue;
669 if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
670 m = 0; piece = board[rf][ff];
671 if(PieceToChar(piece) == '~')
672 piece = (ChessSquare) ( DEMOTED piece );
673 if(filter != EmptySquare && piece != filter) continue;
674 if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
675 MovesFromString(board, flags, ff, rf, -1, -1, 0, pieceDesc[piece], callback, closure);
678 if(IS_SHOGI(gameInfo.variant))
679 piece = (ChessSquare) ( SHOGI piece );
681 switch ((int)piece) {
682 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
684 /* can't happen ([HGM] except for faries...) */
688 if(gameInfo.variant == VariantXiangqi) {
689 /* [HGM] capture and move straight ahead in Xiangqi */
690 if (rf < BOARD_HEIGHT-1 &&
691 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
692 callback(board, flags, NormalMove,
693 rf, ff, rf + 1, ff, closure);
695 /* and move sideways when across the river */
696 for (s = -1; s <= 1; s += 2) {
697 if (rf >= BOARD_HEIGHT>>1 &&
698 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
699 !WhitePiece(board[rf][ff+s]) ) {
700 callback(board, flags, NormalMove,
701 rf, ff, rf, ff+s, closure);
706 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
707 callback(board, flags,
708 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
709 rf, ff, rf + 1, ff, closure);
711 if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
712 gameInfo.variant != VariantShatranj && /* [HGM] */
713 gameInfo.variant != VariantCourier && /* [HGM] */
714 board[rf+2][ff] == EmptySquare ) {
715 callback(board, flags, NormalMove,
716 rf, ff, rf+2, ff, closure);
718 for (s = -1; s <= 1; s += 2) {
719 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
720 ((flags & F_KRIEGSPIEL_CAPTURE) ||
721 BlackPiece(board[rf + 1][ff + s]))) {
722 callback(board, flags,
723 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
724 rf, ff, rf + 1, ff + s, closure);
726 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
727 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
728 (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
729 board[rf][ff + s] == BlackPawn &&
730 board[rf+1][ff + s] == EmptySquare) {
731 callback(board, flags, WhiteCapturesEnPassant,
732 rf, ff, rf+1, ff + s, closure);
739 if(gameInfo.variant == VariantXiangqi) {
740 /* [HGM] capture straight ahead in Xiangqi */
741 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
742 callback(board, flags, NormalMove,
743 rf, ff, rf - 1, ff, closure);
745 /* and move sideways when across the river */
746 for (s = -1; s <= 1; s += 2) {
747 if (rf < BOARD_HEIGHT>>1 &&
748 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
749 !BlackPiece(board[rf][ff+s]) ) {
750 callback(board, flags, NormalMove,
751 rf, ff, rf, ff+s, closure);
756 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
757 callback(board, flags,
758 rf <= promoRank ? BlackPromotion : NormalMove,
759 rf, ff, rf - 1, ff, closure);
761 if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
762 gameInfo.variant != VariantShatranj && /* [HGM] */
763 gameInfo.variant != VariantCourier && /* [HGM] */
764 board[rf-2][ff] == EmptySquare) {
765 callback(board, flags, NormalMove,
766 rf, ff, rf-2, ff, closure);
768 for (s = -1; s <= 1; s += 2) {
769 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
770 ((flags & F_KRIEGSPIEL_CAPTURE) ||
771 WhitePiece(board[rf - 1][ff + s]))) {
772 callback(board, flags,
773 rf <= promoRank ? BlackPromotion : NormalMove,
774 rf, ff, rf - 1, ff + s, closure);
776 if (rf < BOARD_HEIGHT>>1) {
777 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
778 (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
779 board[rf][ff + s] == WhitePawn &&
780 board[rf-1][ff + s] == EmptySquare) {
781 callback(board, flags, BlackCapturesEnPassant,
782 rf, ff, rf-1, ff + s, closure);
792 for (i = -1; i <= 1; i += 2)
793 for (j = -1; j <= 1; j += 2)
794 for (s = 1; s <= 2; s++) {
797 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
798 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
799 && !SameColor(board[rf][ff], board[rt][ft]))
800 callback(board, flags, NormalMove,
801 rf, ff, rt, ft, closure);
805 case SHOGI WhiteKnight:
806 for (s = -1; s <= 1; s += 2) {
807 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
808 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
809 callback(board, flags, NormalMove,
810 rf, ff, rf + 2, ff + s, closure);
815 case SHOGI BlackKnight:
816 for (s = -1; s <= 1; s += 2) {
817 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
818 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
819 callback(board, flags, NormalMove,
820 rf, ff, rf - 2, ff + s, closure);
827 for (d = 0; d <= 1; d++)
828 for (s = -1; s <= 1; s += 2) {
831 rt = rf + (i * s) * d;
832 ft = ff + (i * s) * (1 - d);
833 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
834 if (m == 0 && board[rt][ft] == EmptySquare)
835 callback(board, flags, NormalMove,
836 rf, ff, rt, ft, closure);
837 if (m == 1 && board[rt][ft] != EmptySquare &&
838 !SameColor(board[rf][ff], board[rt][ft]) )
839 callback(board, flags, NormalMove,
840 rf, ff, rt, ft, closure);
841 if (board[rt][ft] != EmptySquare && m++) break;
846 /* Gold General (and all its promoted versions) . First do the */
847 /* diagonal forward steps, then proceed as normal Wazir */
848 case SHOGI (PROMOTED WhitePawn):
849 if(gameInfo.variant == VariantShogi) goto WhiteGold;
850 case SHOGI (PROMOTED BlackPawn):
851 if(gameInfo.variant == VariantShogi) goto BlackGold;
852 SlideVertical(board, flags, rf, ff, callback, closure);
855 case SHOGI (PROMOTED WhiteKnight):
856 if(gameInfo.variant == VariantShogi) goto WhiteGold;
857 case SHOGI BlackDrunk:
858 case SHOGI BlackAlfil:
859 Ferz(board, flags, rf, ff, callback, closure);
860 StepSideways(board, flags, rf, ff, callback, closure);
861 StepBackward(board, flags, rf, ff, callback, closure);
864 case SHOGI (PROMOTED BlackKnight):
865 if(gameInfo.variant == VariantShogi) goto BlackGold;
866 case SHOGI WhiteDrunk:
867 case SHOGI WhiteAlfil:
868 Ferz(board, flags, rf, ff, callback, closure);
869 StepSideways(board, flags, rf, ff, callback, closure);
870 StepForward(board, flags, rf, ff, callback, closure);
874 case SHOGI WhiteStag:
875 case SHOGI BlackStag:
876 if(gameInfo.variant == VariantShogi) goto BlackGold;
877 SlideVertical(board, flags, rf, ff, callback, closure);
878 Ferz(board, flags, rf, ff, callback, closure);
879 StepSideways(board, flags, rf, ff, callback, closure);
882 case SHOGI (PROMOTED WhiteQueen):
883 case SHOGI WhiteTokin:
884 case SHOGI WhiteWazir:
886 StepDiagForward(board, flags, rf, ff, callback, closure);
887 Wazir(board, flags, rf, ff, callback, closure);
890 case SHOGI (PROMOTED BlackQueen):
891 case SHOGI BlackTokin:
892 case SHOGI BlackWazir:
894 StepDiagBackward(board, flags, rf, ff, callback, closure);
895 Wazir(board, flags, rf, ff, callback, closure);
900 Wazir(board, flags, rf, ff, callback, closure);
903 case SHOGI WhiteMarshall:
904 case SHOGI BlackMarshall:
905 Ferz(board, flags, rf, ff, callback, closure);
906 for (d = 0; d <= 1; d++)
907 for (s = -2; s <= 2; s += 4) {
909 ft = ff + s * (1 - d);
910 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
911 if (!SameColor(board[rf][ff], board[rt][ft]) )
912 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
916 case SHOGI WhiteAngel:
917 case SHOGI BlackAngel:
918 Wazir(board, flags, rf, ff, callback, closure);
922 /* [HGM] support Shatranj pieces */
923 for (rs = -1; rs <= 1; rs += 2)
924 for (fs = -1; fs <= 1; fs += 2) {
927 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
928 && ( gameInfo.variant != VariantXiangqi ||
929 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
931 && !SameColor(board[rf][ff], board[rt][ft]))
932 callback(board, flags, NormalMove,
933 rf, ff, rt, ft, closure);
934 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
935 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
936 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
938 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
939 && !SameColor(board[rf][ff], board[rt][ft]))
940 callback(board, flags, NormalMove,
941 rf, ff, rt, ft, closure);
943 if(gameInfo.variant == VariantSpartan)
944 for(fs = -1; fs <= 1; fs += 2) {
946 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
947 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
951 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
954 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
955 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
956 for (s = -2; s <= 2; s += 4) {
958 ft = ff + s * (1 - d);
959 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
960 if (SameColor(board[rf][ff], board[rt][ft])) continue;
961 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
964 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
965 case SHOGI WhiteCardinal:
966 case SHOGI BlackCardinal:
967 case SHOGI WhitePCardinal:
968 case SHOGI BlackPCardinal:
970 Bishop(board, flags, rf, ff, callback, closure);
971 Wazir(board, flags, rf, ff, callback, closure);
974 /* Capablanca Archbishop continues as Knight */
977 Knight(board, flags, rf, ff, callback, closure);
979 /* Shogi Bishops are ordinary Bishops */
980 case SHOGI WhiteBishop:
981 case SHOGI BlackBishop:
982 case SHOGI WhitePBishop:
983 case SHOGI BlackPBishop:
986 Bishop(board, flags, rf, ff, callback, closure);
989 /* Shogi Lance is unlike anything, and asymmetric at that */
990 case SHOGI WhiteQueen:
991 if(gameInfo.variant == VariantChu) goto doQueen;
995 if (rt >= BOARD_HEIGHT) break;
996 if (SameColor(board[rf][ff], board[rt][ft])) break;
997 callback(board, flags, NormalMove,
998 rf, ff, rt, ft, closure);
999 if (board[rt][ft] != EmptySquare) break;
1003 case SHOGI BlackQueen:
1004 if(gameInfo.variant == VariantChu) goto doQueen;
1009 if (SameColor(board[rf][ff], board[rt][ft])) break;
1010 callback(board, flags, NormalMove,
1011 rf, ff, rt, ft, closure);
1012 if (board[rt][ft] != EmptySquare) break;
1016 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
1019 if(gameInfo.variant == VariantChuChess) goto DragonKing;
1020 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1021 for (s = -2; s <= 2; s += 4) {
1023 ft = ff + s * (1 - d);
1024 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1025 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
1026 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1027 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1029 if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
1030 Wazir(board, flags, rf, ff, callback, closure);
1032 Rook(board, flags, rf, ff, callback, closure);
1035 /* Shogi Dragon King has to continue as Ferz after Rook moves */
1036 case SHOGI WhiteDragon:
1037 case SHOGI BlackDragon:
1038 case SHOGI WhitePDragon:
1039 case SHOGI BlackPDragon:
1041 Rook(board, flags, rf, ff, callback, closure);
1042 Ferz(board, flags, rf, ff, callback, closure);
1046 /* Capablanca Chancellor sets flag to continue as Knight */
1049 Rook(board, flags, rf, ff, callback, closure);
1050 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
1051 Ferz(board, flags, rf, ff, callback, closure);
1053 Knight(board, flags, rf, ff, callback, closure);
1056 /* Shogi Rooks are ordinary Rooks */
1057 case SHOGI WhiteRook:
1058 case SHOGI BlackRook:
1059 case SHOGI WhitePRook:
1060 case SHOGI BlackPRook:
1063 Rook(board, flags, rf, ff, callback, closure);
1068 case SHOGI WhiteMother:
1069 case SHOGI BlackMother:
1071 Rook(board, flags, rf, ff, callback, closure);
1072 Bishop(board, flags, rf, ff, callback, closure);
1075 case SHOGI WhitePawn:
1076 StepForward(board, flags, rf, ff, callback, closure);
1079 case SHOGI BlackPawn:
1080 StepBackward(board, flags, rf, ff, callback, closure);
1084 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1085 case SHOGI WhiteFerz:
1086 Ferz(board, flags, rf, ff, callback, closure);
1087 StepForward(board, flags, rf, ff, callback, closure);
1091 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1092 case SHOGI BlackFerz:
1093 StepBackward(board, flags, rf, ff, callback, closure);
1097 /* [HGM] support Shatranj pieces */
1098 Ferz(board, flags, rf, ff, callback, closure);
1103 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
1106 case SHOGI WhiteMonarch:
1107 case SHOGI BlackMonarch:
1108 case SHOGI WhiteKing:
1109 case SHOGI BlackKing:
1112 Ferz(board, flags, rf, ff, callback, closure);
1113 Wazir(board, flags, rf, ff, callback, closure);
1116 case WhiteNightrider:
1117 case BlackNightrider:
1118 for (i = -1; i <= 1; i += 2)
1119 for (j = -1; j <= 1; j += 2)
1120 for (s = 1; s <= 2; s++) { int k;
1123 ft = ff + k*j*(3-s);
1124 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
1125 if (SameColor(board[rf][ff], board[rt][ft])) break;
1126 callback(board, flags, NormalMove,
1127 rf, ff, rt, ft, closure);
1128 if (board[rt][ft] != EmptySquare) break;
1134 Bishop(board, flags, rf, ff, callback, closure);
1135 Rook(board, flags, rf, ff, callback, closure);
1136 Knight(board, flags, rf, ff, callback, closure);
1139 // Use Lance as Berolina / Spartan Pawn.
1141 if(gameInfo.variant == VariantSuper) goto Amazon;
1142 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1143 callback(board, flags,
1144 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1145 rf, ff, rf + 1, ff, closure);
1146 for (s = -1; s <= 1; s += 2) {
1147 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1148 callback(board, flags,
1149 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1150 rf, ff, rf + 1, ff + s, closure);
1151 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1152 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1157 if(gameInfo.variant == VariantSuper) goto Amazon;
1158 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1159 callback(board, flags,
1160 rf <= promoRank ? BlackPromotion : NormalMove,
1161 rf, ff, rf - 1, ff, closure);
1162 for (s = -1; s <= 1; s += 2) {
1163 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1164 callback(board, flags,
1165 rf <= promoRank ? BlackPromotion : NormalMove,
1166 rf, ff, rf - 1, ff + s, closure);
1167 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1168 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1172 case SHOGI WhiteNothing:
1173 case SHOGI BlackNothing:
1174 case SHOGI WhiteLion:
1175 case SHOGI BlackLion:
1178 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1179 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1180 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1181 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
1182 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
1183 rf, ff, rt, ft, closure);
1188 case SHOGI WhiteFalcon:
1189 case SHOGI BlackFalcon:
1190 case SHOGI WhitePDagger:
1191 case SHOGI BlackPDagger:
1192 SlideSideways(board, flags, rf, ff, callback, closure);
1193 StepVertical(board, flags, rf, ff, callback, closure);
1196 case SHOGI WhiteCobra:
1197 case SHOGI BlackCobra:
1198 StepVertical(board, flags, rf, ff, callback, closure);
1201 case SHOGI (PROMOTED WhiteFerz):
1202 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1203 case SHOGI (PROMOTED BlackFerz):
1204 if(gameInfo.variant == VariantShogi) goto BlackGold;
1205 case SHOGI WhitePSword:
1206 case SHOGI BlackPSword:
1207 SlideVertical(board, flags, rf, ff, callback, closure);
1208 StepSideways(board, flags, rf, ff, callback, closure);
1211 case SHOGI WhiteUnicorn:
1212 case SHOGI BlackUnicorn:
1213 Ferz(board, flags, rf, ff, callback, closure);
1214 StepVertical(board, flags, rf, ff, callback, closure);
1217 case SHOGI WhiteMan:
1218 StepDiagForward(board, flags, rf, ff, callback, closure);
1219 StepVertical(board, flags, rf, ff, callback, closure);
1222 case SHOGI BlackMan:
1223 StepDiagBackward(board, flags, rf, ff, callback, closure);
1224 StepVertical(board, flags, rf, ff, callback, closure);
1227 case SHOGI WhiteHCrown:
1228 case SHOGI BlackHCrown:
1229 Bishop(board, flags, rf, ff, callback, closure);
1230 SlideSideways(board, flags, rf, ff, callback, closure);
1233 case SHOGI WhiteCrown:
1234 case SHOGI BlackCrown:
1235 Bishop(board, flags, rf, ff, callback, closure);
1236 SlideVertical(board, flags, rf, ff, callback, closure);
1239 case SHOGI WhiteHorned:
1240 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1241 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1242 if(killX >= 0) break;
1243 Bishop(board, flags, rf, ff, callback, closure);
1244 SlideSideways(board, flags, rf, ff, callback, closure);
1245 SlideBackward(board, flags, rf, ff, callback, closure);
1248 case SHOGI BlackHorned:
1249 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1250 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1251 if(killX >= 0) break;
1252 Bishop(board, flags, rf, ff, callback, closure);
1253 SlideSideways(board, flags, rf, ff, callback, closure);
1254 SlideForward(board, flags, rf, ff, callback, closure);
1257 case SHOGI WhiteEagle:
1258 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1259 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1260 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1261 if(killX >= 0) break;
1262 Rook(board, flags, rf, ff, callback, closure);
1263 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1266 case SHOGI BlackEagle:
1267 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1268 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1269 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1270 if(killX >= 0) break;
1271 Rook(board, flags, rf, ff, callback, closure);
1272 SlideDiagForward(board, flags, rf, ff, callback, closure);
1275 case SHOGI WhiteDolphin:
1276 case SHOGI BlackHorse:
1277 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1278 SlideVertical(board, flags, rf, ff, callback, closure);
1281 case SHOGI BlackDolphin:
1282 case SHOGI WhiteHorse:
1283 SlideDiagForward(board, flags, rf, ff, callback, closure);
1284 SlideVertical(board, flags, rf, ff, callback, closure);
1287 case SHOGI WhiteLance:
1288 SlideForward(board, flags, rf, ff, callback, closure);
1291 case SHOGI BlackLance:
1292 SlideBackward(board, flags, rf, ff, callback, closure);
1295 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1299 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1312 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1313 Board xqCheckers, nullBoard;
1315 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1316 int rf, int ff, int rt, int ft,
1320 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1322 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1324 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1326 if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1328 if (!(flags & F_IGNORE_CHECK) ) {
1329 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1332 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1333 kings += (board[r][f] == BlackKing);
1337 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1339 check = CheckTest(board, flags, rf, ff, rt, ft,
1340 kind == WhiteCapturesEnPassant ||
1341 kind == BlackCapturesEnPassant);
1342 if(promo) board[rf][ff] = BlackLance;
1345 if (flags & F_ATOMIC_CAPTURE) {
1346 if (board[rt][ft] != EmptySquare ||
1347 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1349 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1350 if (board[rf][ff] == king) return;
1351 for (r = rt-1; r <= rt+1; r++) {
1352 for (f = ft-1; f <= ft+1; f++) {
1353 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1354 board[r][f] == king) return;
1359 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1366 int captures; // [HGM] losers
1367 } LegalityTestClosure;
1370 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1371 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1372 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1373 moves that would destroy your own king. The CASTLE_OK flags are
1374 true if castling is not yet ruled out by a move of the king or
1375 rook. Return TRUE if the player on move is currently in check and
1376 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1378 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1381 int ff, ft, k, left, right, swap;
1382 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1383 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1384 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1389 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1390 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1391 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1393 if (inCheck) return TRUE;
1395 /* Generate castling moves */
1396 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1397 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1400 p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1401 if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1403 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1404 if ((flags & F_WHITE_ON_MOVE) &&
1405 (flags & F_WHITE_KCASTLE_OK) &&
1406 board[0][ff] == wKing &&
1407 board[0][ff + 1] == EmptySquare &&
1408 board[0][ff + 2] == EmptySquare &&
1409 board[0][BOARD_RGHT-3] == EmptySquare &&
1410 board[0][BOARD_RGHT-2] == EmptySquare &&
1411 board[0][BOARD_RGHT-1] == WhiteRook &&
1412 castlingRights[0] != NoRights && /* [HGM] check rights */
1413 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1415 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1416 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1417 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1418 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1420 callback(board, flags,
1421 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1422 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1424 if ((flags & F_WHITE_ON_MOVE) &&
1425 (flags & F_WHITE_QCASTLE_OK) &&
1426 board[0][ff] == wKing &&
1427 board[0][ff - 1] == EmptySquare &&
1428 board[0][ff - 2] == EmptySquare &&
1429 board[0][BOARD_LEFT+2] == EmptySquare &&
1430 board[0][BOARD_LEFT+1] == EmptySquare &&
1431 board[0][BOARD_LEFT+0] == WhiteRook &&
1432 castlingRights[1] != NoRights && /* [HGM] check rights */
1433 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1435 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1436 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1437 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1439 callback(board, flags,
1440 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1441 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1443 if (!(flags & F_WHITE_ON_MOVE) &&
1444 (flags & F_BLACK_KCASTLE_OK) &&
1445 board[BOARD_HEIGHT-1][ff] == bKing &&
1446 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1447 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1448 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1449 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1450 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1451 castlingRights[3] != NoRights && /* [HGM] check rights */
1452 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1454 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1455 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1456 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1457 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1459 callback(board, flags,
1460 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1461 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1463 if (!(flags & F_WHITE_ON_MOVE) &&
1464 (flags & F_BLACK_QCASTLE_OK) &&
1465 board[BOARD_HEIGHT-1][ff] == bKing &&
1466 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1467 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1468 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1469 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1470 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1471 castlingRights[4] != NoRights && /* [HGM] check rights */
1472 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1474 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1475 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1476 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1478 callback(board, flags,
1479 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1480 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1484 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1486 /* generate all potential FRC castling moves (KxR), ignoring flags */
1487 /* [HGM] test if the Rooks we find have castling rights */
1488 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1491 if ((flags & F_WHITE_ON_MOVE) != 0) {
1492 ff = castlingRights[2]; /* King file if we have any rights */
1493 if(ff != NoRights && board[0][ff] == WhiteKing) {
1494 if (appData.debugMode) {
1495 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1496 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1498 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1500 right = BOARD_RGHT-2;
1501 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1502 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1503 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1504 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1505 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1506 if(ft != NoRights && board[0][ft] == WhiteRook) {
1507 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1508 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1511 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1512 left = BOARD_LEFT+2;
1514 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1515 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1516 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1517 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1518 if(ff > BOARD_LEFT+2)
1519 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1520 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1521 if(ft != NoRights && board[0][ft] == WhiteRook) {
1522 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1523 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1527 ff = castlingRights[5]; /* King file if we have any rights */
1528 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1529 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1531 right = BOARD_RGHT-2;
1532 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1533 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1534 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1535 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1536 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1537 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1538 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1539 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1542 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1543 left = BOARD_LEFT+2;
1545 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1546 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1547 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1548 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1549 if(ff > BOARD_LEFT+2)
1550 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1551 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1552 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1553 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1554 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1571 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1572 int rf, int ff, int rt, int ft,
1577 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1579 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1581 if (rt == cl->rking && ft == cl->fking) {
1582 if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1584 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1586 if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1587 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1588 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1592 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1593 he leave himself in check? Or if rf == -1, is the player on move
1594 in check now? enPassant must be TRUE if the indicated move is an
1595 e.p. capture. The possibility of castling out of a check along the
1596 back rank is not accounted for (i.e., we still return nonzero), as
1597 this is illegal anyway. Return value is the number of times the
1598 king is in check. */
1600 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1602 CheckTestClosure cl;
1603 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1604 ChessSquare captured = EmptySquare, ep=0, trampled=0;
1605 /* Suppress warnings on uninitialized variables */
1607 if(gameInfo.variant == VariantXiangqi)
1608 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1609 if(gameInfo.variant == VariantKnightmate)
1610 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1611 if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check
1612 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1613 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1614 if(board[r][f] == k || board[r][f] == prince) {
1615 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1616 king = board[r][f]; // remember hich one we had
1623 captured = board[rf][ft];
1624 board[rf][ft] = EmptySquare;
1626 captured = board[rt][ft];
1627 if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
1629 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1630 board[rt][ft] = board[rf][ff];
1631 if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
1633 ep = board[EP_STATUS];
1634 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1635 ChessSquare victim = killX < 0 ? EmptySquare : trampled;
1636 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1637 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1638 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
1639 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1643 /* For compatibility with ICS wild 9, we scan the board in the
1644 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1645 and we test only whether that one is in check. */
1646 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1647 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1648 if (board[cl.rking][cl.fking] == king) {
1650 if(gameInfo.variant == VariantXiangqi) {
1651 /* [HGM] In Xiangqi opposing Kings means check as well */
1653 dir = (king >= BlackPawn) ? -1 : 1;
1654 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1655 board[i][cl.fking] == EmptySquare; i+=dir );
1656 if(i>=0 && i<BOARD_HEIGHT &&
1657 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1660 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1661 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1662 goto undo_move; /* 2-level break */
1669 if(rf != DROP_RANK) // [HGM] drop
1670 board[rf][ff] = board[rt][ft];
1672 board[rf][ft] = captured;
1673 board[rt][ft] = EmptySquare;
1675 if(killX >= 0) board[killY][killX] = trampled;
1676 board[rt][ft] = captured;
1678 board[EP_STATUS] = ep;
1681 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1685 HasLion (Board board, int flags)
1687 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1689 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1690 if(board[r][f] == lion) return 1;
1695 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1696 { // [HGM] put drop legality testing in separate routine for clarity
1698 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1699 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1700 n = PieceToNumber(piece);
1701 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1702 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1703 return ImpossibleMove; // piece not available
1704 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1705 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1706 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1707 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1708 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1709 if(piece == WhitePawn || piece == BlackPawn) {
1710 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1711 for(r=1; r<BOARD_HEIGHT-1; r++)
1712 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1713 // should still test if we mate with this Pawn
1715 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1716 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1718 if( (piece == WhitePawn || piece == BlackPawn) &&
1719 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1720 return IllegalMove; /* no pawn drops on 1st/8th */
1722 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1723 if (!(flags & F_IGNORE_CHECK) &&
1724 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1725 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1728 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1729 int rf, int ff, int rt, int ft,
1733 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1735 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1737 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1738 cl->captures++; // [HGM] losers: count legal captures
1739 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1744 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1746 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1748 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1749 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1750 piece = filterPiece = board[rf][ff];
1751 if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1753 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1754 /* (perhaps we should disallow moves that obviously leave us in check?) */
1755 if((piece == WhiteFalcon || piece == BlackFalcon ||
1756 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
1757 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1761 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1762 cl.ft = fFilter = ft;
1763 cl.kind = IllegalMove;
1764 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1765 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1766 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1767 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1768 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1769 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1771 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1772 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1773 if(board[rf][ff] < BlackPawn) { // white
1774 if(rf != 0) return IllegalMove; // must be on back rank
1775 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1776 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1777 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1778 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1780 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1781 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1782 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1783 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1784 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1787 if(gameInfo.variant == VariantChu) {
1788 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1789 if(promoChar != '+')
1790 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1791 if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
1792 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1794 if(gameInfo.variant == VariantShogi) {
1795 /* [HGM] Shogi promotions. '=' means defer */
1796 if(rf != DROP_RANK && cl.kind == NormalMove) {
1797 ChessSquare piece = board[rf][ff];
1799 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1800 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1801 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1802 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1803 promoChar = '+'; // allowed ICS notations
1804 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1805 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1806 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1807 else if(flags & F_WHITE_ON_MOVE) {
1808 if( (int) piece < (int) WhiteWazir &&
1809 (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1810 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1811 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1812 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1813 else /* promotion optional, default is defer */
1814 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1815 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1817 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1818 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1819 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1820 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1821 else /* promotion optional, default is defer */
1822 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1823 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1827 if (promoChar != NULLCHAR) {
1828 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1829 ChessSquare piece = board[rf][ff];
1830 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1831 // should test if in zone, really
1832 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1834 if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1836 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1837 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1838 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1839 if(piece == EmptySquare)
1840 cl.kind = ImpossibleMove; // non-existing piece
1841 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1842 cl.kind = IllegalMove; // no two Lions
1843 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1844 if(promoChar != PieceToChar(BlackKing)) {
1845 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1846 if(piece == BlackLance) cl.kind = ImpossibleMove;
1847 } else { // promotion to King allowed only if we do not have two yet
1848 int r, f, kings = 0;
1849 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1850 if(kings == 2) cl.kind = IllegalMove;
1852 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1853 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1854 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1855 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1856 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1857 cl.kind = IllegalMove; // promotion to King usually not allowed
1859 cl.kind = IllegalMove;
1869 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1870 int rf, int ff, int rt, int ft,
1874 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1876 register MateTestClosure *cl = (MateTestClosure *) closure;
1881 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1883 MateTest (Board board, int flags)
1886 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1887 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1889 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1890 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1891 nrKing += (board[r][f] == king); // stm has king
1892 if( board[r][f] != EmptySquare ) {
1893 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1898 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1899 case VariantShatranj:
1900 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1904 if(nrKing == 0) return MT_NOKING;
1907 if(myPieces == 1) return MT_BARE;
1910 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1911 // [HGM] 3check: yet to do!
1913 return inCheck ? MT_CHECK : MT_NONE;
1915 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1916 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1917 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1918 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1919 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1920 if(board[n][holdings] != EmptySquare) {
1921 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1922 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1925 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1926 return myPieces == hisPieces ? MT_STALEMATE :
1927 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1928 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1929 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1931 return inCheck ? MT_CHECKMATE
1932 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
1933 MT_STAINMATE : MT_STALEMATE;
1938 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1939 int rf, int ff, int rt, int ft,
1943 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1945 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1946 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1948 // [HGM] wild: for wild-card pieces rt and rf are dummies
1949 if(piece == WhiteFalcon || piece == BlackFalcon ||
1950 piece == WhiteCobra || piece == BlackCobra)
1953 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1954 || PieceToChar(board[rf][ff]) == '~'
1955 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1957 (cl->rfIn == -1 || cl->rfIn == rf) &&
1958 (cl->ffIn == -1 || cl->ffIn == ff) &&
1959 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1960 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1962 if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move
1965 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1966 // [HGM] oneclick: if multiple moves, be sure we remember capture
1967 cl->piece = board[rf][ff];
1970 cl->rt = wildCard ? cl->rtIn : rt;
1971 cl->ft = wildCard ? cl->ftIn : ft;
1974 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1979 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
1981 int illegal = 0; char c = closure->promoCharIn;
1983 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1984 closure->count = closure->captures = 0;
1985 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1986 closure->kind = ImpossibleMove;
1987 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
1988 fFilter = closure->ftIn;
1989 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
1990 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1991 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
1992 closure->count = closure->captures = 0;
1993 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1994 closure->kind = ImpossibleMove;
1995 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1998 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1999 if (closure->count == 0) {
2000 /* See if it's an illegal move due to check */
2002 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2003 if (closure->count == 0) {
2004 /* No, it's not even that */
2005 if(!appData.testLegality && closure->pieceIn != EmptySquare) {
2006 int f, r; // if there is only a single piece of the requested type on the board, use that
2007 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2008 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2009 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2010 if(closure->count > 1) illegal = 0; // ambiguous
2012 if(closure->count == 0) {
2013 if (appData.debugMode) { int i, j;
2014 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2015 for(j=0; j<BOARD_WIDTH; j++)
2016 fprintf(debugFP, "%3d", (int) board[i][j]);
2017 fprintf(debugFP, "\n");
2023 } else if(pieceDefs && closure->count > 1) { // [HGM] gen: move is ambiguous under engine-defined rules
2024 DisambiguateClosure spare = *closure;
2025 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
2026 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2027 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
2031 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2032 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2033 if(closure->piece < BlackPawn) { // white
2034 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2035 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2036 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2037 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2038 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2040 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2041 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2042 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2043 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2044 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2047 if(gameInfo.variant == VariantChu) {
2048 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2050 if(gameInfo.variant == VariantShogi) {
2051 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2052 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2053 ChessSquare piece = closure->piece;
2054 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
2055 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2056 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2057 c = '+'; // allowed ICS notations
2058 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2059 else if(flags & F_WHITE_ON_MOVE) {
2060 if( (int) piece < (int) WhiteWazir &&
2061 (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
2062 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2063 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2064 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2065 else /* promotion optional, default is defer */
2066 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2067 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2069 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
2070 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2071 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2072 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2073 else /* promotion optional, default is defer */
2074 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2075 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2078 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2079 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2081 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2082 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2083 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2084 gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
2085 c = PieceToChar(BlackFerz);
2086 else if(gameInfo.variant == VariantGreat)
2087 c = PieceToChar(BlackMan);
2088 else if(gameInfo.variant == VariantGrand)
2089 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2091 c = PieceToChar(BlackQueen);
2092 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2093 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2094 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2095 ChessSquare p = closure->piece;
2096 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
2097 closure->kind = ImpossibleMove; // used on non-promotable piece
2098 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2099 } else if (c != NULLCHAR) closure->kind = IllegalMove;
2101 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2102 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2103 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2104 if (closure->count > 1) {
2105 closure->kind = AmbiguousMove;
2108 /* Note: If more than one illegal move matches, but no legal
2109 moves, we return IllegalMove, not AmbiguousMove. Caller
2110 can look at closure->count to detect this.
2112 closure->kind = IllegalMove;
2126 } CoordsToAlgebraicClosure;
2128 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2129 ChessMove kind, int rf, int ff,
2130 int rt, int ft, VOIDSTAR closure));
2133 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2135 register CoordsToAlgebraicClosure *cl =
2136 (CoordsToAlgebraicClosure *) closure;
2138 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2139 (board[rf][ff] == cl->piece
2140 || PieceToChar(board[rf][ff]) == '~' &&
2141 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
2145 cl->kind = kind; /* this is the move we want */
2147 cl->file++; /* need file to rule out this move */
2151 cl->rank++; /* need rank to rule out this move */
2153 cl->either++; /* rank or file will rule out this move */
2159 /* Convert coordinates to normal algebraic notation.
2160 promoChar must be NULLCHAR or 'x' if not a promotion.
2163 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2167 char *outp = out, c, capture;
2168 CoordsToAlgebraicClosure cl;
2170 if (rf == DROP_RANK) {
2171 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2172 /* Bughouse piece drop */
2173 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2178 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2180 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2183 if (promoChar == 'x') promoChar = NULLCHAR;
2184 piece = board[rf][ff];
2185 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
2190 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2191 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2192 /* Keep short notation if move is illegal only because it
2193 leaves the player in check, but still return IllegalMove */
2194 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2195 if (kind == IllegalMove) break;
2200 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2201 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2202 /* Non-capture; use style "e5" */
2205 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2207 /* Capture; use style "exd5" */
2209 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2213 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2215 /* Use promotion suffix style "=Q" */
2217 if (promoChar != NULLCHAR) {
2218 if(IS_SHOGI(gameInfo.variant)) {
2219 /* [HGM] ... but not in Shogi! */
2220 *outp++ = promoChar == '=' ? '=' : '+';
2223 *outp++ = ToUpper(promoChar);
2232 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2233 /* Code added by Tord: FRC castling. */
2234 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2235 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2237 safeStrCpy(out, "O-O", MOVE_LEN);
2239 safeStrCpy(out, "O-O-O", MOVE_LEN);
2240 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2242 /* End of code added by Tord */
2243 /* Test for castling or ICS wild castling */
2244 /* Use style "O-O" (oh-oh) for PGN compatibility */
2245 else if (rf == rt &&
2246 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2247 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2248 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2249 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2250 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2251 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2253 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2255 /* This notation is always unambiguous, unless there are
2256 kings on both the d and e files, with "wild castling"
2257 possible for the king on the d file and normal castling
2258 possible for the other. ICS rules for wild 9
2259 effectively make castling illegal for either king in
2260 this situation. So I am not going to worry about it;
2261 I'll just generate an ambiguous O-O in this case.
2263 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2266 /* else fall through */
2271 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2272 cl.ft = fFilter = ft;
2274 cl.kind = IllegalMove;
2275 cl.rank = cl.file = cl.either = 0;
2276 c = PieceToChar(piece) ;
2277 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
2279 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2280 /* Generate pretty moves for moving into check, but
2281 still return IllegalMove.
2283 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
2284 if (cl.kind == IllegalMove) break;
2285 cl.kind = IllegalMove;
2288 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2289 else "Ngf3" or "Ngxf7",
2290 else "N1f3" or "N5xf7",
2291 else "Ng1f3" or "Ng5xf7".
2293 if( c == '~' || c == '+') {
2294 /* [HGM] print nonexistent piece as its demoted version */
2295 piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
2297 if(c=='+') *outp++ = c;
2298 *outp++ = ToUpper(PieceToChar(piece));
2300 if (cl.file || (cl.either && !cl.rank)) {
2306 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2309 if(board[rt][ft] != EmptySquare)
2315 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2316 if (IS_SHOGI(gameInfo.variant)) {
2317 /* [HGM] in Shogi non-pawns can promote */
2318 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2320 else if (gameInfo.variant == VariantChuChess && promoChar ||
2321 gameInfo.variant != VariantSuper && promoChar &&
2322 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2324 *outp++ = ToUpper(promoChar);
2326 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2328 *outp++ = ToUpper(promoChar);
2334 /* Moving a nonexistent piece */
2338 /* Not a legal move, even ignoring check.
2339 If there was a piece on the from square,
2340 use style "Ng1g3" or "Ng1xe8";
2341 if there was a pawn or nothing (!),
2342 use style "g1g3" or "g1xe8". Use "x"
2343 if a piece was on the to square, even
2344 a piece of the same color.
2348 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2350 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2351 c += (board[r][f] == piece); // count on-board pieces of given type
2352 *outp++ = ToUpper(PieceToChar(piece));
2354 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2358 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2360 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2364 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2365 /* Use promotion suffix style "=Q" */
2366 if (promoChar != NULLCHAR && promoChar != 'x') {
2368 *outp++ = ToUpper(promoChar);
2375 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2384 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2386 int preyStackPointer, chaseStackPointer;
2389 unsigned char rf, ff, rt, ft;
2393 unsigned char rank, file;
2399 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2401 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2402 int rf, int ff, int rt, int ft,
2406 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2407 { // For adding captures that can lead to chase indictment to the chaseStack
2408 if(board[rt][ft] == EmptySquare) return; // non-capture
2409 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2410 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2411 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2412 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2413 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2414 chaseStack[chaseStackPointer].rf = rf;
2415 chaseStack[chaseStackPointer].ff = ff;
2416 chaseStack[chaseStackPointer].rt = rt;
2417 chaseStack[chaseStackPointer].ft = ft;
2418 chaseStackPointer++;
2421 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2422 int rf, int ff, int rt, int ft,
2426 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2427 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2429 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2431 if(board[rt][ft] == EmptySquare) return; // no capture
2432 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2433 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2435 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2436 for(i=0; i<chaseStackPointer; i++) {
2437 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2438 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2439 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2440 chaseStack[i] = chaseStack[--chaseStackPointer];
2446 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2447 int rf, int ff, int rt, int ft,
2451 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2452 { // for determining if a piece (given through the closure) is protected
2453 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2455 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2456 if(appData.debugMode && board[rt][ft] != EmptySquare)
2457 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2460 extern char moveList[MAX_MOVES][MOVE_LEN];
2463 PerpetualChase (int first, int last)
2464 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2467 ChessSquare captured;
2469 preyStackPointer = 0; // clear stack of chased pieces
2470 for(i=first; i<last; i+=2) { // for all positions with same side to move
2471 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2472 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2473 // determine all captures possible after the move, and put them on chaseStack
2474 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2475 if(appData.debugMode) { int n;
2476 for(n=0; n<chaseStackPointer; n++)
2477 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2478 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2479 fprintf(debugFP, ": all capts\n");
2481 // determine all captures possible before the move, and delete them from chaseStack
2482 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2483 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2484 cl.rt = moveList[i][3]-ONE;
2485 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2486 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2487 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2488 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2489 if(appData.debugMode) { int n;
2490 for(n=0; n<chaseStackPointer; n++)
2491 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2492 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2493 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2495 // chaseSack now contains all captures made possible by the move
2496 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2497 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2498 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2500 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2501 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2503 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2504 continue; // C or H attack on R is always chase; leave on chaseStack
2506 if(attacker == victim) {
2507 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2508 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2509 // we can capture back with equal piece, so this is no chase but a sacrifice
2510 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2511 j--; /* ! */ continue;
2516 // the attack is on a lower piece, or on a pinned or blocked equal one
2517 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2518 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2519 // test if the victim is protected by a true protector. First make the capture.
2520 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2521 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2522 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2523 // Then test if the opponent can recapture
2524 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2525 cl.rt = chaseStack[j].rt;
2526 cl.ft = chaseStack[j].ft;
2527 if(appData.debugMode) {
2528 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2530 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2531 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2532 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2533 // unmake the capture
2534 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2535 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2536 // if a recapture was found, piece is protected, and we are not chasing it.
2537 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2538 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2542 // chaseStack now contains all moves that chased
2543 if(appData.debugMode) { int n;
2544 for(n=0; n<chaseStackPointer; n++)
2545 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2546 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2547 fprintf(debugFP, ": chases\n");
2549 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2550 for(j=0; j<chaseStackPointer; j++) {
2551 preyStack[j].rank = chaseStack[j].rt;
2552 preyStack[j].file = chaseStack[j].ft;
2554 preyStackPointer = chaseStackPointer;
2557 for(j=0; j<chaseStackPointer; j++) {
2558 for(k=0; k<preyStackPointer; k++) {
2559 // search the victim of each chase move on the preyStack (first occurrence)
2560 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2561 if(k < tail) break; // piece was already identified as still being chased
2562 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2563 preyStack[tail] = preyStack[k]; // by swapping
2564 preyStack[k] = preyStack[preyStackPointer];
2570 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2571 if(appData.debugMode) { int n;
2572 for(n=0; n<preyStackPointer; n++)
2573 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2574 fprintf(debugFP, "always chased upto ply %d\n", i);
2576 // now adjust the location of the chased pieces according to opponent move
2577 for(j=0; j<preyStackPointer; j++) {
2578 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2579 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2580 preyStack[j].rank = moveList[i+1][3]-ONE;
2581 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2586 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2587 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the