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];
79 WhitePiece (ChessSquare piece)
81 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
85 BlackPiece (ChessSquare piece)
87 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
92 SameColor (ChessSquare piece1, ChessSquare piece2)
94 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
95 (int) piece1 < (int) BlackPawn &&
96 (int) piece2 >= (int) WhitePawn &&
97 (int) piece2 < (int) BlackPawn)
98 || ((int) piece1 >= (int) BlackPawn &&
99 (int) piece1 < (int) EmptySquare &&
100 (int) piece2 >= (int) BlackPawn &&
101 (int) piece2 < (int) EmptySquare);
104 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
107 char pieceToChar[] = {
108 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
109 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
110 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
111 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
113 char pieceNickName[EmptySquare];
116 PieceToChar (ChessSquare p)
118 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
119 return pieceToChar[(int) p];
123 PieceToNumber (ChessSquare p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
126 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
128 while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
136 if(c == '.') return EmptySquare;
137 for(i=0; i< (int) EmptySquare; i++)
138 if(pieceNickName[i] == c) return (ChessSquare) i;
139 for(i=0; i< (int) EmptySquare; i++)
140 if(pieceToChar[i] == c) return (ChessSquare) i;
145 CopyBoard (Board to, Board from)
149 for (i = 0; i < BOARD_HEIGHT; i++)
150 for (j = 0; j < BOARD_WIDTH; j++)
151 to[i][j] = from[i][j];
152 for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
153 to[VIRGIN][j] = from[VIRGIN][j],
154 to[CASTLING][j] = from[CASTLING][j];
155 to[HOLDINGS_SET] = 0; // flag used in ICS play
159 CompareBoards (Board board1, Board board2)
163 for (i = 0; i < BOARD_HEIGHT; i++)
164 for (j = 0; j < BOARD_WIDTH; j++) {
165 if (board1[i][j] != board2[i][j])
171 // [HGM] gen: configurable move generation from Betza notation sent by engine.
175 // alphabet "abcdefghijklmnopqrstuvwxyz"
176 char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N";
177 char xStep[] = "2110.130.102.10.00....0..2";
178 char yStep[] = "2132.133.313.20.11....1..3";
179 char dirType[] = "01000104000200000260050000";
180 // 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 "
181 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 };
182 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 };
184 int rot[][4] = { // rotation matrices for each direction
196 MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback cb, VOIDSTAR cl)
199 int mine, his, dir, bit, occup, i;
200 if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
201 while(*p) { // more moves to go
202 int expo = 1, dx, dy, x, y, mode, dirSet, retry=0, initial=0, jump=1;
203 if(*p == 'i') initial = 1, desc = ++p;
204 while(islower(*p)) p++; // skip prefixes
205 if(!isupper(*p)) return; // syntax error: no atom
206 dirSet = 0; // build direction set based on atom symmetry
207 switch(symmetry[*p-'A']) {
208 case 'B': expo = 0; // bishop, slide
209 case 'F': // diagonal atom (degenerate 4-fold)
210 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
211 int b = dirs1[*desc-'a']; // use wide version
212 if( islower(desc[1]) &&
213 ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
214 b = dirs1[*desc-'a'] & dirs1[desc[1]-'a']; // intersect wide & perp wide
219 dirSet &= 0x99; if(!dirSet) dirSet = 0x99;
221 case 'R': expo = 0; // rook, slide
222 case 'W': // orthogonal atom (non-deg 4-fold)
223 while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
224 dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
226 case 'N': // oblique atom (degenerate 8-fold)
227 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
228 int b = dirs2[*desc-'a']; // when alone, use narrow version
229 if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
230 else if(islower(desc[1]) && i < '4'
231 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
232 b = dirs1[*desc-'a'] & dirs2[desc[1]-'a']; // intersect wide & perp narrow
237 if(!dirSet) dirSet = 0xFF;
239 case 'Q': expo = 0; // queen, slide
240 case 'K': // non-deg (pseudo) 8-fold
241 dirSet=0x55; // start with orthogonal moves
242 retry = 1; // and schedule the diagonal moves for later
243 break; // should not have direction indicators
244 default: return; // syntax error: invalid atom
246 if(mine == 2) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
247 mode = 0; // build mode mask
248 if(*desc == 'm') mode |= 4, desc++;
249 if(*desc == 'c') mode |= his, desc++;
250 if(*desc == 'd') mode |= mine, desc++;
251 if(*desc == 'e') mode |= 8, desc++;
252 if(!mode) mode = his + 4;// no mode spec, use default = mc
253 if(*desc == 'p') mode |= 32, desc++;
254 if(*desc == 'g') mode |= 64, desc++;
255 if(*desc == 'o') mode |= 128, desc++;
256 if(*desc == 'n') jump = 0, desc++;
257 while(*desc == 'j') jump++, desc++;
258 dx = xStep[*p-'A'] - '0'; // step vector of atom
259 dy = yStep[*p-'A'] - '0';
260 if(isdigit(*++p)) expo = atoi(p++); // read exponent
261 if(expo > 9) p++; // allow double-digit
262 desc = p; // this is start of next move
263 if(initial && (board[r][f] != initialPosition[r][f] ||
264 r == 0 && board[TOUCHED_W] & 1<<f ||
265 r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f ) ) continue;
266 if(expo > 1 && dx == 0 && dy == 0) { // castling indicated by O + number
270 for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
271 int i = expo, hop = mode, vx, vy;
272 if(!(bit & dirSet)) continue; // does not move in this direction
273 vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector
274 vy = dx*rot[dir][2] + dy*rot[dir][3];
275 x = f; y = r; // start square
277 x += vx; y += vy; // step to next square
278 if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
279 if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT; else break; }
280 if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT; else break; }
281 if(!jump && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
282 if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
283 if(board[y][x] < BlackPawn) occup = 1; else
284 if(board[y][x] < EmptySquare) occup = 2; else
286 if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper
287 if(mode & 8 && y == board[EP_RANK] && occup == 4 && board[EP_FILE] == x) { // to e.p. square
288 cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
290 if(mode & 16) { // castling
291 i = 2; // kludge to elongate move indefinitely
292 if(occup == 4) continue; // skip empty squares
293 if(x == BOARD_LEFT && board[y][x] == initialPosition[y][x]) // reached initial corner piece
294 cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
295 if(x == BOARD_RGHT-1 && board[y][x] == initialPosition[y][x])
296 cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
299 if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl); // allowed, generate
300 if(occup != 4) break; // not valid transit square
303 dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q
304 } while(retry--); // and start doing them
308 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
311 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
316 if (rt >= BOARD_HEIGHT) break;
317 if (SameColor(board[rf][ff], board[rt][ft])) break;
318 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
319 if (board[rt][ft] != EmptySquare) break;
324 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
330 if (SameColor(board[rf][ff], board[rt][ft])) break;
331 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
332 if (board[rt][ft] != EmptySquare) break;
337 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
339 SlideForward(board, flags, rf, ff, callback, closure);
340 SlideBackward(board, flags, rf, ff, callback, closure);
344 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
346 int i, s, rt = rf, ft;
347 for(s = -1; s <= 1; s+= 2) {
350 if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
351 if (SameColor(board[rf][ff], board[rt][ft])) break;
352 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
353 if (board[rt][ft] != EmptySquare) break;
359 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
362 for(s = -1; s <= 1; s+= 2) {
366 if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
367 if (SameColor(board[rf][ff], board[rt][ft])) break;
368 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
369 if (board[rt][ft] != EmptySquare) break;
375 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
378 for(s = -1; s <= 1; s+= 2) {
382 if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
383 if (SameColor(board[rf][ff], board[rt][ft])) break;
384 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
385 if (board[rt][ft] != EmptySquare) break;
391 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
393 SlideVertical(board, flags, rf, ff, callback, closure);
394 SlideSideways(board, flags, rf, ff, callback, closure);
398 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
400 SlideDiagForward(board, flags, rf, ff, callback, closure);
401 SlideDiagBackward(board, flags, rf, ff, callback, closure);
405 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
406 { // Lion-like move of Horned Falcon and Souring Eagle
407 int ft = ff + dx, rt = rf + dy;
408 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
409 if (!SameColor(board[rf][ff], board[rt][ft]))
410 callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
412 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
413 if (!SameColor(board[rf][ff], board[rt][ft]))
414 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
418 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
420 int ft = ff, rt = rf + 1;
421 if (rt >= BOARD_HEIGHT) return;
422 if (SameColor(board[rf][ff], board[rt][ft])) return;
423 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
427 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
429 int ft = ff, rt = rf - 1;
431 if (SameColor(board[rf][ff], board[rt][ft])) return;
432 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
436 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
440 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
441 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
443 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
444 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
448 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
451 if (rt >= BOARD_HEIGHT) return;
453 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
454 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
456 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
457 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
461 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
466 if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
467 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
469 if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
470 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
474 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
476 StepForward(board, flags, rf, ff, callback, closure);
477 StepBackward(board, flags, rf, ff, callback, closure);
481 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
483 StepDiagForward(board, flags, rf, ff, callback, closure);
484 StepDiagBackward(board, flags, rf, ff, callback, closure);
488 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
490 StepVertical(board, flags, rf, ff, callback, closure);
491 StepSideways(board, flags, rf, ff, callback, closure);
495 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
498 for (i = -1; i <= 1; i += 2)
499 for (j = -1; j <= 1; j += 2)
500 for (s = 1; s <= 2; s++) {
503 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
504 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
505 && !SameColor(board[rf][ff], board[rt][ft]))
506 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
510 /* Call callback once for each pseudo-legal move in the given
511 position, except castling moves. A move is pseudo-legal if it is
512 legal, or if it would be legal except that it leaves the king in
513 check. In the arguments, epfile is EP_NONE if the previous move
514 was not a double pawn push, or the file 0..7 if it was, or
515 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
516 Promotion moves generated are to Queen only.
519 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
520 // speed: only do moves with this piece type
523 int i, j, d, s, fs, rs, rt, ft, m;
524 int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
525 int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
527 for (rf = 0; rf < BOARD_HEIGHT; rf++)
528 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
531 if(board[rf][ff] == EmptySquare) continue;
532 if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
533 m = 0; piece = board[rf][ff];
534 if(PieceToChar(piece) == '~')
535 piece = (ChessSquare) ( DEMOTED piece );
536 if(filter != EmptySquare && piece != filter) continue;
537 if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
538 MovesFromString(board, flags, ff, rf, pieceDesc[piece], callback, closure);
541 if(IS_SHOGI(gameInfo.variant))
542 piece = (ChessSquare) ( SHOGI piece );
544 switch ((int)piece) {
545 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
547 /* can't happen ([HGM] except for faries...) */
551 if(gameInfo.variant == VariantXiangqi) {
552 /* [HGM] capture and move straight ahead in Xiangqi */
553 if (rf < BOARD_HEIGHT-1 &&
554 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
555 callback(board, flags, NormalMove,
556 rf, ff, rf + 1, ff, closure);
558 /* and move sideways when across the river */
559 for (s = -1; s <= 1; s += 2) {
560 if (rf >= BOARD_HEIGHT>>1 &&
561 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
562 !WhitePiece(board[rf][ff+s]) ) {
563 callback(board, flags, NormalMove,
564 rf, ff, rf, ff+s, closure);
569 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
570 callback(board, flags,
571 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
572 rf, ff, rf + 1, ff, closure);
574 if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
575 gameInfo.variant != VariantShatranj && /* [HGM] */
576 gameInfo.variant != VariantCourier && /* [HGM] */
577 board[rf+2][ff] == EmptySquare ) {
578 callback(board, flags, NormalMove,
579 rf, ff, rf+2, ff, closure);
581 for (s = -1; s <= 1; s += 2) {
582 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
583 ((flags & F_KRIEGSPIEL_CAPTURE) ||
584 BlackPiece(board[rf + 1][ff + s]))) {
585 callback(board, flags,
586 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
587 rf, ff, rf + 1, ff + s, closure);
589 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
590 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
591 (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
592 board[rf][ff + s] == BlackPawn &&
593 board[rf+1][ff + s] == EmptySquare) {
594 callback(board, flags, WhiteCapturesEnPassant,
595 rf, ff, rf+1, ff + s, closure);
602 if(gameInfo.variant == VariantXiangqi) {
603 /* [HGM] capture straight ahead in Xiangqi */
604 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
605 callback(board, flags, NormalMove,
606 rf, ff, rf - 1, ff, closure);
608 /* and move sideways when across the river */
609 for (s = -1; s <= 1; s += 2) {
610 if (rf < BOARD_HEIGHT>>1 &&
611 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
612 !BlackPiece(board[rf][ff+s]) ) {
613 callback(board, flags, NormalMove,
614 rf, ff, rf, ff+s, closure);
619 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
620 callback(board, flags,
621 rf <= promoRank ? BlackPromotion : NormalMove,
622 rf, ff, rf - 1, ff, closure);
624 if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
625 gameInfo.variant != VariantShatranj && /* [HGM] */
626 gameInfo.variant != VariantCourier && /* [HGM] */
627 board[rf-2][ff] == EmptySquare) {
628 callback(board, flags, NormalMove,
629 rf, ff, rf-2, ff, closure);
631 for (s = -1; s <= 1; s += 2) {
632 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
633 ((flags & F_KRIEGSPIEL_CAPTURE) ||
634 WhitePiece(board[rf - 1][ff + s]))) {
635 callback(board, flags,
636 rf <= promoRank ? BlackPromotion : NormalMove,
637 rf, ff, rf - 1, ff + s, closure);
639 if (rf < BOARD_HEIGHT>>1) {
640 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
641 (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
642 board[rf][ff + s] == WhitePawn &&
643 board[rf-1][ff + s] == EmptySquare) {
644 callback(board, flags, BlackCapturesEnPassant,
645 rf, ff, rf-1, ff + s, closure);
655 for (i = -1; i <= 1; i += 2)
656 for (j = -1; j <= 1; j += 2)
657 for (s = 1; s <= 2; s++) {
660 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
661 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
662 && !SameColor(board[rf][ff], board[rt][ft]))
663 callback(board, flags, NormalMove,
664 rf, ff, rt, ft, closure);
668 case SHOGI WhiteKnight:
669 for (s = -1; s <= 1; s += 2) {
670 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
671 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
672 callback(board, flags, NormalMove,
673 rf, ff, rf + 2, ff + s, closure);
678 case SHOGI BlackKnight:
679 for (s = -1; s <= 1; s += 2) {
680 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
681 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
682 callback(board, flags, NormalMove,
683 rf, ff, rf - 2, ff + s, closure);
690 for (d = 0; d <= 1; d++)
691 for (s = -1; s <= 1; s += 2) {
694 rt = rf + (i * s) * d;
695 ft = ff + (i * s) * (1 - d);
696 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
697 if (m == 0 && board[rt][ft] == EmptySquare)
698 callback(board, flags, NormalMove,
699 rf, ff, rt, ft, closure);
700 if (m == 1 && board[rt][ft] != EmptySquare &&
701 !SameColor(board[rf][ff], board[rt][ft]) )
702 callback(board, flags, NormalMove,
703 rf, ff, rt, ft, closure);
704 if (board[rt][ft] != EmptySquare && m++) break;
709 /* Gold General (and all its promoted versions) . First do the */
710 /* diagonal forward steps, then proceed as normal Wazir */
711 case SHOGI (PROMOTED WhitePawn):
712 if(gameInfo.variant == VariantShogi) goto WhiteGold;
713 case SHOGI (PROMOTED BlackPawn):
714 if(gameInfo.variant == VariantShogi) goto BlackGold;
715 SlideVertical(board, flags, rf, ff, callback, closure);
718 case SHOGI (PROMOTED WhiteKnight):
719 if(gameInfo.variant == VariantShogi) goto WhiteGold;
720 case SHOGI BlackDrunk:
721 case SHOGI BlackAlfil:
722 Ferz(board, flags, rf, ff, callback, closure);
723 StepSideways(board, flags, rf, ff, callback, closure);
724 StepBackward(board, flags, rf, ff, callback, closure);
727 case SHOGI (PROMOTED BlackKnight):
728 if(gameInfo.variant == VariantShogi) goto BlackGold;
729 case SHOGI WhiteDrunk:
730 case SHOGI WhiteAlfil:
731 Ferz(board, flags, rf, ff, callback, closure);
732 StepSideways(board, flags, rf, ff, callback, closure);
733 StepForward(board, flags, rf, ff, callback, closure);
737 case SHOGI WhiteStag:
738 case SHOGI BlackStag:
739 if(gameInfo.variant == VariantShogi) goto BlackGold;
740 SlideVertical(board, flags, rf, ff, callback, closure);
741 Ferz(board, flags, rf, ff, callback, closure);
742 StepSideways(board, flags, rf, ff, callback, closure);
745 case SHOGI (PROMOTED WhiteQueen):
746 case SHOGI WhiteTokin:
747 case SHOGI WhiteWazir:
749 StepDiagForward(board, flags, rf, ff, callback, closure);
750 Wazir(board, flags, rf, ff, callback, closure);
753 case SHOGI (PROMOTED BlackQueen):
754 case SHOGI BlackTokin:
755 case SHOGI BlackWazir:
757 StepDiagBackward(board, flags, rf, ff, callback, closure);
758 Wazir(board, flags, rf, ff, callback, closure);
763 Wazir(board, flags, rf, ff, callback, closure);
766 case SHOGI WhiteMarshall:
767 case SHOGI BlackMarshall:
768 Ferz(board, flags, rf, ff, callback, closure);
769 for (d = 0; d <= 1; d++)
770 for (s = -2; s <= 2; s += 4) {
772 ft = ff + s * (1 - d);
773 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
774 if (!SameColor(board[rf][ff], board[rt][ft]) )
775 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
779 case SHOGI WhiteAngel:
780 case SHOGI BlackAngel:
781 Wazir(board, flags, rf, ff, callback, closure);
785 /* [HGM] support Shatranj pieces */
786 for (rs = -1; rs <= 1; rs += 2)
787 for (fs = -1; fs <= 1; fs += 2) {
790 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
791 && ( gameInfo.variant != VariantXiangqi ||
792 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
794 && !SameColor(board[rf][ff], board[rt][ft]))
795 callback(board, flags, NormalMove,
796 rf, ff, rt, ft, closure);
797 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
798 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
799 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
801 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
802 && !SameColor(board[rf][ff], board[rt][ft]))
803 callback(board, flags, NormalMove,
804 rf, ff, rt, ft, closure);
806 if(gameInfo.variant == VariantSpartan)
807 for(fs = -1; fs <= 1; fs += 2) {
809 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
810 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
814 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
817 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
818 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
819 for (s = -2; s <= 2; s += 4) {
821 ft = ff + s * (1 - d);
822 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
823 if (SameColor(board[rf][ff], board[rt][ft])) continue;
824 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
827 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
828 case SHOGI WhiteCardinal:
829 case SHOGI BlackCardinal:
830 case SHOGI WhitePCardinal:
831 case SHOGI BlackPCardinal:
833 Bishop(board, flags, rf, ff, callback, closure);
834 Wazir(board, flags, rf, ff, callback, closure);
837 /* Capablanca Archbishop continues as Knight */
840 Knight(board, flags, rf, ff, callback, closure);
842 /* Shogi Bishops are ordinary Bishops */
843 case SHOGI WhiteBishop:
844 case SHOGI BlackBishop:
845 case SHOGI WhitePBishop:
846 case SHOGI BlackPBishop:
849 Bishop(board, flags, rf, ff, callback, closure);
852 /* Shogi Lance is unlike anything, and asymmetric at that */
853 case SHOGI WhiteQueen:
854 if(gameInfo.variant == VariantChu) goto doQueen;
858 if (rt >= BOARD_HEIGHT) break;
859 if (SameColor(board[rf][ff], board[rt][ft])) break;
860 callback(board, flags, NormalMove,
861 rf, ff, rt, ft, closure);
862 if (board[rt][ft] != EmptySquare) break;
866 case SHOGI BlackQueen:
867 if(gameInfo.variant == VariantChu) goto doQueen;
872 if (SameColor(board[rf][ff], board[rt][ft])) break;
873 callback(board, flags, NormalMove,
874 rf, ff, rt, ft, closure);
875 if (board[rt][ft] != EmptySquare) break;
879 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
882 if(gameInfo.variant == VariantChuChess) goto DragonKing;
883 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
884 for (s = -2; s <= 2; s += 4) {
886 ft = ff + s * (1 - d);
887 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
888 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
889 if (SameColor(board[rf][ff], board[rt][ft])) continue;
890 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
892 if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
893 Wazir(board, flags, rf, ff, callback, closure);
895 Rook(board, flags, rf, ff, callback, closure);
898 /* Shogi Dragon King has to continue as Ferz after Rook moves */
899 case SHOGI WhiteDragon:
900 case SHOGI BlackDragon:
901 case SHOGI WhitePDragon:
902 case SHOGI BlackPDragon:
904 Rook(board, flags, rf, ff, callback, closure);
905 Ferz(board, flags, rf, ff, callback, closure);
909 /* Capablanca Chancellor sets flag to continue as Knight */
912 Rook(board, flags, rf, ff, callback, closure);
913 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
914 Ferz(board, flags, rf, ff, callback, closure);
916 Knight(board, flags, rf, ff, callback, closure);
919 /* Shogi Rooks are ordinary Rooks */
920 case SHOGI WhiteRook:
921 case SHOGI BlackRook:
922 case SHOGI WhitePRook:
923 case SHOGI BlackPRook:
926 Rook(board, flags, rf, ff, callback, closure);
931 case SHOGI WhiteMother:
932 case SHOGI BlackMother:
934 Rook(board, flags, rf, ff, callback, closure);
935 Bishop(board, flags, rf, ff, callback, closure);
938 case SHOGI WhitePawn:
939 StepForward(board, flags, rf, ff, callback, closure);
942 case SHOGI BlackPawn:
943 StepBackward(board, flags, rf, ff, callback, closure);
947 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
948 case SHOGI WhiteFerz:
949 Ferz(board, flags, rf, ff, callback, closure);
950 StepForward(board, flags, rf, ff, callback, closure);
954 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
955 case SHOGI BlackFerz:
956 StepBackward(board, flags, rf, ff, callback, closure);
960 /* [HGM] support Shatranj pieces */
961 Ferz(board, flags, rf, ff, callback, closure);
966 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
969 case SHOGI WhiteMonarch:
970 case SHOGI BlackMonarch:
971 case SHOGI WhiteKing:
972 case SHOGI BlackKing:
975 Ferz(board, flags, rf, ff, callback, closure);
976 Wazir(board, flags, rf, ff, callback, closure);
979 case WhiteNightrider:
980 case BlackNightrider:
981 for (i = -1; i <= 1; i += 2)
982 for (j = -1; j <= 1; j += 2)
983 for (s = 1; s <= 2; s++) { int k;
987 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
988 if (SameColor(board[rf][ff], board[rt][ft])) break;
989 callback(board, flags, NormalMove,
990 rf, ff, rt, ft, closure);
991 if (board[rt][ft] != EmptySquare) break;
997 Bishop(board, flags, rf, ff, callback, closure);
998 Rook(board, flags, rf, ff, callback, closure);
999 Knight(board, flags, rf, ff, callback, closure);
1002 // Use Lance as Berolina / Spartan Pawn.
1004 if(gameInfo.variant == VariantSuper) goto Amazon;
1005 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1006 callback(board, flags,
1007 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1008 rf, ff, rf + 1, ff, closure);
1009 for (s = -1; s <= 1; s += 2) {
1010 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1011 callback(board, flags,
1012 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1013 rf, ff, rf + 1, ff + s, closure);
1014 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1015 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1020 if(gameInfo.variant == VariantSuper) goto Amazon;
1021 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1022 callback(board, flags,
1023 rf <= promoRank ? BlackPromotion : NormalMove,
1024 rf, ff, rf - 1, ff, closure);
1025 for (s = -1; s <= 1; s += 2) {
1026 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1027 callback(board, flags,
1028 rf <= promoRank ? BlackPromotion : NormalMove,
1029 rf, ff, rf - 1, ff + s, closure);
1030 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1031 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1035 case SHOGI WhiteNothing:
1036 case SHOGI BlackNothing:
1037 case SHOGI WhiteLion:
1038 case SHOGI BlackLion:
1041 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1042 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1043 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1044 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
1045 rf, ff, rt, ft, closure);
1049 case SHOGI WhiteFalcon:
1050 case SHOGI BlackFalcon:
1051 case SHOGI WhitePDagger:
1052 case SHOGI BlackPDagger:
1053 SlideSideways(board, flags, rf, ff, callback, closure);
1054 StepVertical(board, flags, rf, ff, callback, closure);
1057 case SHOGI WhiteCobra:
1058 case SHOGI BlackCobra:
1059 StepVertical(board, flags, rf, ff, callback, closure);
1062 case SHOGI (PROMOTED WhiteFerz):
1063 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1064 case SHOGI (PROMOTED BlackFerz):
1065 if(gameInfo.variant == VariantShogi) goto BlackGold;
1066 case SHOGI WhitePSword:
1067 case SHOGI BlackPSword:
1068 SlideVertical(board, flags, rf, ff, callback, closure);
1069 StepSideways(board, flags, rf, ff, callback, closure);
1072 case SHOGI WhiteUnicorn:
1073 case SHOGI BlackUnicorn:
1074 Ferz(board, flags, rf, ff, callback, closure);
1075 StepVertical(board, flags, rf, ff, callback, closure);
1078 case SHOGI WhiteMan:
1079 StepDiagForward(board, flags, rf, ff, callback, closure);
1080 StepVertical(board, flags, rf, ff, callback, closure);
1083 case SHOGI BlackMan:
1084 StepDiagBackward(board, flags, rf, ff, callback, closure);
1085 StepVertical(board, flags, rf, ff, callback, closure);
1088 case SHOGI WhiteHCrown:
1089 case SHOGI BlackHCrown:
1090 Bishop(board, flags, rf, ff, callback, closure);
1091 SlideSideways(board, flags, rf, ff, callback, closure);
1094 case SHOGI WhiteCrown:
1095 case SHOGI BlackCrown:
1096 Bishop(board, flags, rf, ff, callback, closure);
1097 SlideVertical(board, flags, rf, ff, callback, closure);
1100 case SHOGI WhiteHorned:
1101 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1102 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1103 if(killX >= 0) break;
1104 Bishop(board, flags, rf, ff, callback, closure);
1105 SlideSideways(board, flags, rf, ff, callback, closure);
1106 SlideBackward(board, flags, rf, ff, callback, closure);
1109 case SHOGI BlackHorned:
1110 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1111 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1112 if(killX >= 0) break;
1113 Bishop(board, flags, rf, ff, callback, closure);
1114 SlideSideways(board, flags, rf, ff, callback, closure);
1115 SlideForward(board, flags, rf, ff, callback, closure);
1118 case SHOGI WhiteEagle:
1119 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1120 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1121 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1122 if(killX >= 0) break;
1123 Rook(board, flags, rf, ff, callback, closure);
1124 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1127 case SHOGI BlackEagle:
1128 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1129 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1130 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1131 if(killX >= 0) break;
1132 Rook(board, flags, rf, ff, callback, closure);
1133 SlideDiagForward(board, flags, rf, ff, callback, closure);
1136 case SHOGI WhiteDolphin:
1137 case SHOGI BlackHorse:
1138 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1139 SlideVertical(board, flags, rf, ff, callback, closure);
1142 case SHOGI BlackDolphin:
1143 case SHOGI WhiteHorse:
1144 SlideDiagForward(board, flags, rf, ff, callback, closure);
1145 SlideVertical(board, flags, rf, ff, callback, closure);
1148 case SHOGI WhiteLance:
1149 SlideForward(board, flags, rf, ff, callback, closure);
1152 case SHOGI BlackLance:
1153 SlideBackward(board, flags, rf, ff, callback, closure);
1156 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1160 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1173 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1174 Board xqCheckers, nullBoard;
1176 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1177 int rf, int ff, int rt, int ft,
1181 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1183 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1185 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1187 if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1189 if (!(flags & F_IGNORE_CHECK) ) {
1190 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1193 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1194 kings += (board[r][f] == BlackKing);
1198 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1200 check = CheckTest(board, flags, rf, ff, rt, ft,
1201 kind == WhiteCapturesEnPassant ||
1202 kind == BlackCapturesEnPassant);
1203 if(promo) board[rf][ff] = BlackLance;
1206 if (flags & F_ATOMIC_CAPTURE) {
1207 if (board[rt][ft] != EmptySquare ||
1208 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1210 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1211 if (board[rf][ff] == king) return;
1212 for (r = rt-1; r <= rt+1; r++) {
1213 for (f = ft-1; f <= ft+1; f++) {
1214 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1215 board[r][f] == king) return;
1220 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1227 int captures; // [HGM] losers
1228 } LegalityTestClosure;
1231 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1232 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1233 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1234 moves that would destroy your own king. The CASTLE_OK flags are
1235 true if castling is not yet ruled out by a move of the king or
1236 rook. Return TRUE if the player on move is currently in check and
1237 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1239 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1242 int ff, ft, k, left, right, swap;
1243 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1244 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1245 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1250 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1251 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1252 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1254 if (inCheck) return TRUE;
1256 /* Generate castling moves */
1257 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1258 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1261 p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1262 if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1264 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1265 if ((flags & F_WHITE_ON_MOVE) &&
1266 (flags & F_WHITE_KCASTLE_OK) &&
1267 board[0][ff] == wKing &&
1268 board[0][ff + 1] == EmptySquare &&
1269 board[0][ff + 2] == EmptySquare &&
1270 board[0][BOARD_RGHT-3] == EmptySquare &&
1271 board[0][BOARD_RGHT-2] == EmptySquare &&
1272 board[0][BOARD_RGHT-1] == WhiteRook &&
1273 castlingRights[0] != NoRights && /* [HGM] check rights */
1274 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1276 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1277 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1278 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1279 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1281 callback(board, flags,
1282 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1283 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1285 if ((flags & F_WHITE_ON_MOVE) &&
1286 (flags & F_WHITE_QCASTLE_OK) &&
1287 board[0][ff] == wKing &&
1288 board[0][ff - 1] == EmptySquare &&
1289 board[0][ff - 2] == EmptySquare &&
1290 board[0][BOARD_LEFT+2] == EmptySquare &&
1291 board[0][BOARD_LEFT+1] == EmptySquare &&
1292 board[0][BOARD_LEFT+0] == WhiteRook &&
1293 castlingRights[1] != NoRights && /* [HGM] check rights */
1294 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1296 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1297 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1298 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1300 callback(board, flags,
1301 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1302 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1304 if (!(flags & F_WHITE_ON_MOVE) &&
1305 (flags & F_BLACK_KCASTLE_OK) &&
1306 board[BOARD_HEIGHT-1][ff] == bKing &&
1307 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1308 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1309 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1310 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1311 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1312 castlingRights[3] != NoRights && /* [HGM] check rights */
1313 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1315 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1316 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1317 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1318 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1320 callback(board, flags,
1321 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1322 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1324 if (!(flags & F_WHITE_ON_MOVE) &&
1325 (flags & F_BLACK_QCASTLE_OK) &&
1326 board[BOARD_HEIGHT-1][ff] == bKing &&
1327 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1328 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1329 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1330 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1331 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1332 castlingRights[4] != NoRights && /* [HGM] check rights */
1333 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1335 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1336 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1337 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1339 callback(board, flags,
1340 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1341 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1345 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1347 /* generate all potential FRC castling moves (KxR), ignoring flags */
1348 /* [HGM] test if the Rooks we find have castling rights */
1349 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1352 if ((flags & F_WHITE_ON_MOVE) != 0) {
1353 ff = castlingRights[2]; /* King file if we have any rights */
1354 if(ff != NoRights && board[0][ff] == WhiteKing) {
1355 if (appData.debugMode) {
1356 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1357 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1359 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1361 right = BOARD_RGHT-2;
1362 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1363 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1364 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1365 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1366 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1367 if(ft != NoRights && board[0][ft] == WhiteRook) {
1368 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1369 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1372 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1373 left = BOARD_LEFT+2;
1375 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1376 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1377 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1378 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1379 if(ff > BOARD_LEFT+2)
1380 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1381 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1382 if(ft != NoRights && board[0][ft] == WhiteRook) {
1383 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1384 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1388 ff = castlingRights[5]; /* King file if we have any rights */
1389 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1390 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1392 right = BOARD_RGHT-2;
1393 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1394 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1395 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1396 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1397 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1398 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1399 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1400 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1403 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1404 left = BOARD_LEFT+2;
1406 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1407 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1408 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1409 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1410 if(ff > BOARD_LEFT+2)
1411 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1412 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1413 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1414 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1415 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1432 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1433 int rf, int ff, int rt, int ft,
1438 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1440 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1442 if (rt == cl->rking && ft == cl->fking) {
1443 if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1445 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1447 if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1448 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1449 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1453 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1454 he leave himself in check? Or if rf == -1, is the player on move
1455 in check now? enPassant must be TRUE if the indicated move is an
1456 e.p. capture. The possibility of castling out of a check along the
1457 back rank is not accounted for (i.e., we still return nonzero), as
1458 this is illegal anyway. Return value is the number of times the
1459 king is in check. */
1461 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1463 CheckTestClosure cl;
1464 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1465 ChessSquare captured = EmptySquare, ep=0, trampled=0;
1466 /* Suppress warnings on uninitialized variables */
1468 if(gameInfo.variant == VariantXiangqi)
1469 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1470 if(gameInfo.variant == VariantKnightmate)
1471 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1472 if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check
1473 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1474 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1475 if(board[r][f] == k || board[r][f] == prince) {
1476 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1477 king = board[r][f]; // remember hich one we had
1484 captured = board[rf][ft];
1485 board[rf][ft] = EmptySquare;
1487 captured = board[rt][ft];
1488 if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
1490 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1491 board[rt][ft] = board[rf][ff];
1492 board[rf][ff] = EmptySquare;
1494 ep = board[EP_STATUS];
1495 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1496 ChessSquare victim = killX < 0 ? EmptySquare : trampled;
1497 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1498 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1499 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
1500 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1504 /* For compatibility with ICS wild 9, we scan the board in the
1505 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1506 and we test only whether that one is in check. */
1507 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1508 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1509 if (board[cl.rking][cl.fking] == king) {
1511 if(gameInfo.variant == VariantXiangqi) {
1512 /* [HGM] In Xiangqi opposing Kings means check as well */
1514 dir = (king >= BlackPawn) ? -1 : 1;
1515 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1516 board[i][cl.fking] == EmptySquare; i+=dir );
1517 if(i>=0 && i<BOARD_HEIGHT &&
1518 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1521 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1522 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1523 goto undo_move; /* 2-level break */
1530 if(rf != DROP_RANK) // [HGM] drop
1531 board[rf][ff] = board[rt][ft];
1533 board[rf][ft] = captured;
1534 board[rt][ft] = EmptySquare;
1536 if(killX >= 0) board[killY][killX] = trampled;
1537 board[rt][ft] = captured;
1539 board[EP_STATUS] = ep;
1542 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1546 HasLion (Board board, int flags)
1548 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1550 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1551 if(board[r][f] == lion) return 1;
1556 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1557 { // [HGM] put drop legality testing in separate routine for clarity
1559 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1560 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1561 n = PieceToNumber(piece);
1562 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1563 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1564 return ImpossibleMove; // piece not available
1565 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1566 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1567 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1568 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1569 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1570 if(piece == WhitePawn || piece == BlackPawn) {
1571 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1572 for(r=1; r<BOARD_HEIGHT-1; r++)
1573 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1574 // should still test if we mate with this Pawn
1576 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1577 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1579 if( (piece == WhitePawn || piece == BlackPawn) &&
1580 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1581 return IllegalMove; /* no pawn drops on 1st/8th */
1583 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1584 if (!(flags & F_IGNORE_CHECK) &&
1585 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1586 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1589 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1590 int rf, int ff, int rt, int ft,
1594 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1596 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1598 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1599 cl->captures++; // [HGM] losers: count legal captures
1600 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1605 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1607 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1609 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1610 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1611 piece = filterPiece = board[rf][ff];
1612 if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1614 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1615 /* (perhaps we should disallow moves that obviously leave us in check?) */
1616 if((piece == WhiteFalcon || piece == BlackFalcon ||
1617 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu)
1618 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1622 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1623 cl.ft = fFilter = ft;
1624 cl.kind = IllegalMove;
1625 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1626 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1627 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1628 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1629 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1630 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1632 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1633 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1634 if(board[rf][ff] < BlackPawn) { // white
1635 if(rf != 0) return IllegalMove; // must be on back rank
1636 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1637 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1638 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1639 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1641 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1642 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1643 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1644 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1645 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1648 if(gameInfo.variant == VariantChu) {
1649 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1650 if(promoChar != '+')
1651 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1652 if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
1653 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1655 if(gameInfo.variant == VariantShogi) {
1656 /* [HGM] Shogi promotions. '=' means defer */
1657 if(rf != DROP_RANK && cl.kind == NormalMove) {
1658 ChessSquare piece = board[rf][ff];
1660 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1661 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1662 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1663 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1664 promoChar = '+'; // allowed ICS notations
1665 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1666 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1667 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1668 else if(flags & F_WHITE_ON_MOVE) {
1669 if( (int) piece < (int) WhiteWazir &&
1670 (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1671 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1672 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1673 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1674 else /* promotion optional, default is defer */
1675 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1676 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1678 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1679 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1680 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1681 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1682 else /* promotion optional, default is defer */
1683 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1684 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1688 if (promoChar != NULLCHAR) {
1689 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1690 ChessSquare piece = board[rf][ff];
1691 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1692 // should test if in zone, really
1693 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1695 if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1697 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1698 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1699 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1700 if(piece == EmptySquare)
1701 cl.kind = ImpossibleMove; // non-existing piece
1702 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1703 cl.kind = IllegalMove; // no two Lions
1704 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1705 if(promoChar != PieceToChar(BlackKing)) {
1706 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1707 if(piece == BlackLance) cl.kind = ImpossibleMove;
1708 } else { // promotion to King allowed only if we do not have two yet
1709 int r, f, kings = 0;
1710 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1711 if(kings == 2) cl.kind = IllegalMove;
1713 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1714 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1715 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1716 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1717 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1718 cl.kind = IllegalMove; // promotion to King usually not allowed
1720 cl.kind = IllegalMove;
1730 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1731 int rf, int ff, int rt, int ft,
1735 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1737 register MateTestClosure *cl = (MateTestClosure *) closure;
1742 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1744 MateTest (Board board, int flags)
1747 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1748 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1750 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1751 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1752 nrKing += (board[r][f] == king); // stm has king
1753 if( board[r][f] != EmptySquare ) {
1754 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1759 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1760 case VariantShatranj:
1761 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1765 if(nrKing == 0) return MT_NOKING;
1768 if(myPieces == 1) return MT_BARE;
1771 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1772 // [HGM] 3check: yet to do!
1774 return inCheck ? MT_CHECK : MT_NONE;
1776 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1777 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1778 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1779 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1780 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1781 if(board[n][holdings] != EmptySquare) {
1782 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1783 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1786 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1787 return myPieces == hisPieces ? MT_STALEMATE :
1788 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1789 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1790 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1792 return inCheck ? MT_CHECKMATE
1793 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
1794 MT_STAINMATE : MT_STALEMATE;
1799 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1800 int rf, int ff, int rt, int ft,
1804 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1806 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1807 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1809 // [HGM] wild: for wild-card pieces rt and rf are dummies
1810 if(piece == WhiteFalcon || piece == BlackFalcon ||
1811 piece == WhiteCobra || piece == BlackCobra)
1814 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1815 || PieceToChar(board[rf][ff]) == '~'
1816 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1818 (cl->rfIn == -1 || cl->rfIn == rf) &&
1819 (cl->ffIn == -1 || cl->ffIn == ff) &&
1820 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1821 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1823 if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move
1826 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1827 // [HGM] oneclick: if multiple moves, be sure we remember capture
1828 cl->piece = board[rf][ff];
1831 cl->rt = wildCard ? cl->rtIn : rt;
1832 cl->ft = wildCard ? cl->ftIn : ft;
1835 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1840 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
1842 int illegal = 0; char c = closure->promoCharIn;
1844 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1845 closure->count = closure->captures = 0;
1846 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1847 closure->kind = ImpossibleMove;
1848 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
1849 fFilter = closure->ftIn;
1850 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
1851 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1852 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
1853 closure->count = closure->captures = 0;
1854 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1855 closure->kind = ImpossibleMove;
1856 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1859 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1860 if (closure->count == 0) {
1861 /* See if it's an illegal move due to check */
1863 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1864 if (closure->count == 0) {
1865 /* No, it's not even that */
1866 if(!appData.testLegality && closure->pieceIn != EmptySquare) {
1867 int f, r; // if there is only a single piece of the requested type on the board, use that
1868 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
1869 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1870 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
1871 if(closure->count > 1) illegal = 0; // ambiguous
1873 if(closure->count == 0) {
1874 if (appData.debugMode) { int i, j;
1875 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1876 for(j=0; j<BOARD_WIDTH; j++)
1877 fprintf(debugFP, "%3d", (int) board[i][j]);
1878 fprintf(debugFP, "\n");
1884 } else if(pieceDefs && closure->count > 1) { // [HGM] gen: move is ambiguous under engine-defined rules
1885 DisambiguateClosure spare = *closure;
1886 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
1887 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
1888 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
1892 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1893 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1894 if(closure->piece < BlackPawn) { // white
1895 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1896 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
1897 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1898 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1899 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1901 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1902 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
1903 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1904 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1905 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1908 if(gameInfo.variant == VariantChu) {
1909 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
1911 if(gameInfo.variant == VariantShogi) {
1912 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1913 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1914 ChessSquare piece = closure->piece;
1915 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1916 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1917 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1918 c = '+'; // allowed ICS notations
1919 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1920 else if(flags & F_WHITE_ON_MOVE) {
1921 if( (int) piece < (int) WhiteWazir &&
1922 (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1923 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1924 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1925 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1926 else /* promotion optional, default is defer */
1927 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1928 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1930 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1931 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1932 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1933 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1934 else /* promotion optional, default is defer */
1935 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1936 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1939 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1940 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1942 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1943 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1944 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1945 gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
1946 c = PieceToChar(BlackFerz);
1947 else if(gameInfo.variant == VariantGreat)
1948 c = PieceToChar(BlackMan);
1949 else if(gameInfo.variant == VariantGrand)
1950 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
1952 c = PieceToChar(BlackQueen);
1953 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1954 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1955 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
1956 ChessSquare p = closure->piece;
1957 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
1958 closure->kind = ImpossibleMove; // used on non-promotable piece
1959 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1960 } else if (c != NULLCHAR) closure->kind = IllegalMove;
1962 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1963 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1964 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1965 if (closure->count > 1) {
1966 closure->kind = AmbiguousMove;
1969 /* Note: If more than one illegal move matches, but no legal
1970 moves, we return IllegalMove, not AmbiguousMove. Caller
1971 can look at closure->count to detect this.
1973 closure->kind = IllegalMove;
1987 } CoordsToAlgebraicClosure;
1989 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1990 ChessMove kind, int rf, int ff,
1991 int rt, int ft, VOIDSTAR closure));
1994 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1996 register CoordsToAlgebraicClosure *cl =
1997 (CoordsToAlgebraicClosure *) closure;
1999 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2000 (board[rf][ff] == cl->piece
2001 || PieceToChar(board[rf][ff]) == '~' &&
2002 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
2006 cl->kind = kind; /* this is the move we want */
2008 cl->file++; /* need file to rule out this move */
2012 cl->rank++; /* need rank to rule out this move */
2014 cl->either++; /* rank or file will rule out this move */
2020 /* Convert coordinates to normal algebraic notation.
2021 promoChar must be NULLCHAR or 'x' if not a promotion.
2024 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2028 char *outp = out, c, capture;
2029 CoordsToAlgebraicClosure cl;
2031 if (rf == DROP_RANK) {
2032 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2033 /* Bughouse piece drop */
2034 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2039 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2041 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2044 if (promoChar == 'x') promoChar = NULLCHAR;
2045 piece = board[rf][ff];
2046 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
2051 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2052 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2053 /* Keep short notation if move is illegal only because it
2054 leaves the player in check, but still return IllegalMove */
2055 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2056 if (kind == IllegalMove) break;
2061 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2062 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2063 /* Non-capture; use style "e5" */
2066 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2068 /* Capture; use style "exd5" */
2070 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2074 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2076 /* Use promotion suffix style "=Q" */
2078 if (promoChar != NULLCHAR) {
2079 if(IS_SHOGI(gameInfo.variant)) {
2080 /* [HGM] ... but not in Shogi! */
2081 *outp++ = promoChar == '=' ? '=' : '+';
2084 *outp++ = ToUpper(promoChar);
2093 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2094 /* Code added by Tord: FRC castling. */
2095 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2096 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2098 safeStrCpy(out, "O-O", MOVE_LEN);
2100 safeStrCpy(out, "O-O-O", MOVE_LEN);
2101 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2103 /* End of code added by Tord */
2104 /* Test for castling or ICS wild castling */
2105 /* Use style "O-O" (oh-oh) for PGN compatibility */
2106 else if (rf == rt &&
2107 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2108 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2109 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2110 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2111 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2112 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2114 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2116 /* This notation is always unambiguous, unless there are
2117 kings on both the d and e files, with "wild castling"
2118 possible for the king on the d file and normal castling
2119 possible for the other. ICS rules for wild 9
2120 effectively make castling illegal for either king in
2121 this situation. So I am not going to worry about it;
2122 I'll just generate an ambiguous O-O in this case.
2124 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2127 /* else fall through */
2132 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2133 cl.ft = fFilter = ft;
2135 cl.kind = IllegalMove;
2136 cl.rank = cl.file = cl.either = 0;
2137 c = PieceToChar(piece) ;
2138 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
2140 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2141 /* Generate pretty moves for moving into check, but
2142 still return IllegalMove.
2144 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
2145 if (cl.kind == IllegalMove) break;
2146 cl.kind = IllegalMove;
2149 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2150 else "Ngf3" or "Ngxf7",
2151 else "N1f3" or "N5xf7",
2152 else "Ng1f3" or "Ng5xf7".
2154 if( c == '~' || c == '+') {
2155 /* [HGM] print nonexistent piece as its demoted version */
2156 piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
2158 if(c=='+') *outp++ = c;
2159 *outp++ = ToUpper(PieceToChar(piece));
2161 if (cl.file || (cl.either && !cl.rank)) {
2167 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2170 if(board[rt][ft] != EmptySquare)
2176 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2177 if (IS_SHOGI(gameInfo.variant)) {
2178 /* [HGM] in Shogi non-pawns can promote */
2179 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2181 else if (gameInfo.variant == VariantChuChess && promoChar ||
2182 gameInfo.variant != VariantSuper && promoChar &&
2183 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2185 *outp++ = ToUpper(promoChar);
2187 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2189 *outp++ = ToUpper(promoChar);
2195 /* Moving a nonexistent piece */
2199 /* Not a legal move, even ignoring check.
2200 If there was a piece on the from square,
2201 use style "Ng1g3" or "Ng1xe8";
2202 if there was a pawn or nothing (!),
2203 use style "g1g3" or "g1xe8". Use "x"
2204 if a piece was on the to square, even
2205 a piece of the same color.
2209 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2211 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2212 c += (board[r][f] == piece); // count on-board pieces of given type
2213 *outp++ = ToUpper(PieceToChar(piece));
2215 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2219 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2221 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2225 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2226 /* Use promotion suffix style "=Q" */
2227 if (promoChar != NULLCHAR && promoChar != 'x') {
2229 *outp++ = ToUpper(promoChar);
2236 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2245 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2247 int preyStackPointer, chaseStackPointer;
2250 unsigned char rf, ff, rt, ft;
2254 unsigned char rank, file;
2260 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2262 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2263 int rf, int ff, int rt, int ft,
2267 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2268 { // For adding captures that can lead to chase indictment to the chaseStack
2269 if(board[rt][ft] == EmptySquare) return; // non-capture
2270 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2271 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2272 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2273 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2274 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2275 chaseStack[chaseStackPointer].rf = rf;
2276 chaseStack[chaseStackPointer].ff = ff;
2277 chaseStack[chaseStackPointer].rt = rt;
2278 chaseStack[chaseStackPointer].ft = ft;
2279 chaseStackPointer++;
2282 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2283 int rf, int ff, int rt, int ft,
2287 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2288 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2290 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2292 if(board[rt][ft] == EmptySquare) return; // no capture
2293 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2294 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2296 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2297 for(i=0; i<chaseStackPointer; i++) {
2298 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2299 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2300 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2301 chaseStack[i] = chaseStack[--chaseStackPointer];
2307 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2308 int rf, int ff, int rt, int ft,
2312 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2313 { // for determining if a piece (given through the closure) is protected
2314 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2316 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2317 if(appData.debugMode && board[rt][ft] != EmptySquare)
2318 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2321 extern char moveList[MAX_MOVES][MOVE_LEN];
2324 PerpetualChase (int first, int last)
2325 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2328 ChessSquare captured;
2330 preyStackPointer = 0; // clear stack of chased pieces
2331 for(i=first; i<last; i+=2) { // for all positions with same side to move
2332 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2333 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2334 // determine all captures possible after the move, and put them on chaseStack
2335 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2336 if(appData.debugMode) { int n;
2337 for(n=0; n<chaseStackPointer; n++)
2338 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2339 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2340 fprintf(debugFP, ": all capts\n");
2342 // determine all captures possible before the move, and delete them from chaseStack
2343 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2344 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2345 cl.rt = moveList[i][3]-ONE;
2346 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2347 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2348 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2349 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2350 if(appData.debugMode) { int n;
2351 for(n=0; n<chaseStackPointer; n++)
2352 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2353 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2354 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2356 // chaseSack now contains all captures made possible by the move
2357 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2358 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2359 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2361 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2362 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2364 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2365 continue; // C or H attack on R is always chase; leave on chaseStack
2367 if(attacker == victim) {
2368 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2369 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2370 // we can capture back with equal piece, so this is no chase but a sacrifice
2371 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2372 j--; /* ! */ continue;
2377 // the attack is on a lower piece, or on a pinned or blocked equal one
2378 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2379 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2380 // test if the victim is protected by a true protector. First make the capture.
2381 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2382 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2383 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2384 // Then test if the opponent can recapture
2385 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2386 cl.rt = chaseStack[j].rt;
2387 cl.ft = chaseStack[j].ft;
2388 if(appData.debugMode) {
2389 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2391 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2392 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2393 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2394 // unmake the capture
2395 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2396 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2397 // if a recapture was found, piece is protected, and we are not chasing it.
2398 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2399 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2403 // chaseStack now contains all moves that chased
2404 if(appData.debugMode) { int n;
2405 for(n=0; n<chaseStackPointer; n++)
2406 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2407 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2408 fprintf(debugFP, ": chases\n");
2410 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2411 for(j=0; j<chaseStackPointer; j++) {
2412 preyStack[j].rank = chaseStack[j].rt;
2413 preyStack[j].file = chaseStack[j].ft;
2415 preyStackPointer = chaseStackPointer;
2418 for(j=0; j<chaseStackPointer; j++) {
2419 for(k=0; k<preyStackPointer; k++) {
2420 // search the victim of each chase move on the preyStack (first occurrence)
2421 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2422 if(k < tail) break; // piece was already identified as still being chased
2423 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2424 preyStack[tail] = preyStack[k]; // by swapping
2425 preyStack[k] = preyStack[preyStackPointer];
2431 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2432 if(appData.debugMode) { int n;
2433 for(n=0; n<preyStackPointer; n++)
2434 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2435 fprintf(debugFP, "always chased upto ply %d\n", i);
2437 // now adjust the location of the chased pieces according to opponent move
2438 for(j=0; j<preyStackPointer; j++) {
2439 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2440 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2441 preyStack[j].rank = moveList[i+1][3]-ONE;
2442 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2447 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2448 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the