2 * moves.c - Move generation and checking
\r
3 * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $
\r
5 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
\r
6 * Enhancements Copyright 1992-95 Free Software Foundation, Inc.
\r
8 * The following terms apply to Digital Equipment Corporation's copyright
\r
9 * interest in XBoard:
\r
10 * ------------------------------------------------------------------------
\r
11 * All Rights Reserved
\r
13 * Permission to use, copy, modify, and distribute this software and its
\r
14 * documentation for any purpose and without fee is hereby granted,
\r
15 * provided that the above copyright notice appear in all copies and that
\r
16 * both that copyright notice and this permission notice appear in
\r
17 * supporting documentation, and that the name of Digital not be
\r
18 * used in advertising or publicity pertaining to distribution of the
\r
19 * software without specific, written prior permission.
\r
21 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
22 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
23 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
24 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
25 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
26 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
28 * ------------------------------------------------------------------------
\r
30 * The following terms apply to the enhanced version of XBoard distributed
\r
31 * by the Free Software Foundation:
\r
32 * ------------------------------------------------------------------------
\r
33 * This program is free software; you can redistribute it and/or modify
\r
34 * it under the terms of the GNU General Public License as published by
\r
35 * the Free Software Foundation; either version 2 of the License, or
\r
36 * (at your option) any later version.
\r
38 * This program is distributed in the hope that it will be useful,
\r
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
41 * GNU General Public License for more details.
\r
43 * You should have received a copy of the GNU General Public License
\r
44 * along with this program; if not, write to the Free Software
\r
45 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
46 * ------------------------------------------------------------------------
\r
53 # include <string.h>
\r
54 #else /* not HAVE_STRING_H */
\r
55 # include <strings.h>
\r
56 #endif /* not HAVE_STRING_H */
\r
58 #include "backend.h"
\r
62 int WhitePiece P((ChessSquare));
\r
63 int BlackPiece P((ChessSquare));
\r
64 int SameColor P((ChessSquare, ChessSquare));
\r
66 extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */
\r
69 int WhitePiece(piece)
\r
72 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
\r
75 int BlackPiece(piece)
\r
78 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
\r
81 int SameColor(piece1, piece2)
\r
82 ChessSquare piece1, piece2;
\r
84 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
\r
85 (int) piece1 < (int) BlackPawn &&
\r
86 (int) piece2 >= (int) WhitePawn &&
\r
87 (int) piece2 < (int) BlackPawn)
\r
88 || ((int) piece1 >= (int) BlackPawn &&
\r
89 (int) piece1 < (int) EmptySquare &&
\r
90 (int) piece2 >= (int) BlackPawn &&
\r
91 (int) piece2 < (int) EmptySquare);
\r
94 ChessSquare PromoPiece(moveType)
\r
100 case WhitePromotionQueen:
\r
102 case BlackPromotionQueen:
\r
104 case WhitePromotionRook:
\r
106 case BlackPromotionRook:
\r
108 case WhitePromotionBishop:
\r
109 return WhiteBishop;
\r
110 case BlackPromotionBishop:
\r
111 return BlackBishop;
\r
112 case WhitePromotionKnight:
\r
113 return WhiteKnight;
\r
114 case BlackPromotionKnight:
\r
115 return BlackKnight;
\r
116 case WhitePromotionKing:
\r
118 case BlackPromotionKing:
\r
121 case WhitePromotionChancellor:
\r
122 return WhiteMarshall;
\r
123 case BlackPromotionChancellor:
\r
124 return BlackMarshall;
\r
125 case WhitePromotionArchbishop:
\r
126 return WhiteCardinal;
\r
127 case BlackPromotionArchbishop:
\r
128 return BlackCardinal;
\r
133 ChessMove PromoCharToMoveType(whiteOnMove, promoChar)
\r
138 switch (promoChar) {
\r
141 return WhitePromotionKnight;
\r
144 return WhitePromotionBishop;
\r
147 return WhitePromotionRook;
\r
151 return WhitePromotionArchbishop;
\r
154 return WhitePromotionChancellor;
\r
158 return WhitePromotionQueen;
\r
161 return WhitePromotionKing;
\r
167 switch (promoChar) {
\r
170 return BlackPromotionKnight;
\r
173 return BlackPromotionBishop;
\r
176 return BlackPromotionRook;
\r
180 return BlackPromotionArchbishop;
\r
183 return BlackPromotionChancellor;
\r
187 return BlackPromotionQueen;
\r
190 return BlackPromotionKing;
\r
198 char pieceToChar[] = {
\r
199 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
\r
200 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'S', 'L', 'U', 'K',
\r
201 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
\r
202 'o', 'h', 'i', 'j', 'g', 'd', 'v', 's', 'l', 'u', 'k',
\r
205 char PieceToChar(p)
\r
208 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
\r
209 return pieceToChar[(int) p];
\r
212 int PieceToNumber(p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
\r
216 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
\r
218 while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
\r
222 ChessSquare CharToPiece(c)
\r
226 for(i=0; i< (int) EmptySquare; i++)
\r
227 if(pieceToChar[i] == c) return (ChessSquare) i;
\r
228 return EmptySquare;
\r
231 void CopyBoard(to, from)
\r
236 for (i = 0; i < BOARD_HEIGHT; i++)
\r
237 for (j = 0; j < BOARD_WIDTH; j++)
\r
238 to[i][j] = from[i][j];
\r
241 int CompareBoards(board1, board2)
\r
242 Board board1, board2;
\r
246 for (i = 0; i < BOARD_HEIGHT; i++)
\r
247 for (j = 0; j < BOARD_WIDTH; j++) {
\r
248 if (board1[i][j] != board2[i][j])
\r
255 /* Call callback once for each pseudo-legal move in the given
\r
256 position, except castling moves. A move is pseudo-legal if it is
\r
257 legal, or if it would be legal except that it leaves the king in
\r
258 check. In the arguments, epfile is EP_NONE if the previous move
\r
259 was not a double pawn push, or the file 0..7 if it was, or
\r
260 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
\r
261 Promotion moves generated are to Queen only.
\r
263 void GenPseudoLegal(board, flags, epfile, callback, closure)
\r
267 MoveCallback callback;
\r
271 int i, j, d, s, fs, rs, rt, ft, m;
\r
273 for (rf = 0; rf < BOARD_HEIGHT; rf++)
\r
274 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
\r
277 if (flags & F_WHITE_ON_MOVE) {
\r
278 if (!WhitePiece(board[rf][ff])) continue;
\r
280 if (!BlackPiece(board[rf][ff])) continue;
\r
282 m = 0; piece = board[rf][ff];
\r
283 if(PieceToChar(piece) == '~')
\r
284 piece = (ChessSquare) ( DEMOTED piece );
\r
285 if(gameInfo.variant == VariantShogi)
\r
286 piece = (ChessSquare) ( SHOGI piece );
\r
289 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
\r
291 /* can't happen ([HGM] except for faries...) */
\r
295 if(gameInfo.variant == VariantXiangqi) {
\r
296 /* [HGM] capture and move straight ahead in Xiangqi */
\r
297 if (rf < BOARD_HEIGHT-1 &&
\r
298 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
\r
299 callback(board, flags, NormalMove,
\r
300 rf, ff, rf + 1, ff, closure);
\r
302 /* and move sideways when across the river */
\r
303 for (s = -1; s <= 1; s += 2) {
\r
304 if (rf >= BOARD_HEIGHT>>1 &&
\r
305 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
306 !WhitePiece(board[rf][ff+s]) ) {
\r
307 callback(board, flags, NormalMove,
\r
308 rf, ff, rf, ff+s, closure);
\r
313 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
\r
314 callback(board, flags,
\r
315 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
316 rf, ff, rf + 1, ff, closure);
\r
318 if (rf == 1 && board[2][ff] == EmptySquare &&
\r
319 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
320 gameInfo.variant != VariantCourier && /* [HGM] */
\r
321 board[3][ff] == EmptySquare ) {
\r
322 callback(board, flags, NormalMove,
\r
323 rf, ff, 3, ff, closure);
\r
325 for (s = -1; s <= 1; s += 2) {
\r
326 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
327 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
328 BlackPiece(board[rf + 1][ff + s]))) {
\r
329 callback(board, flags,
\r
330 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
331 rf, ff, rf + 1, ff + s, closure);
\r
333 if (rf == BOARD_HEIGHT-4) {
\r
334 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
335 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
336 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
\r
337 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
\r
338 callback(board, flags, WhiteCapturesEnPassant,
\r
339 rf, ff, 5, ff + s, closure);
\r
346 if(gameInfo.variant == VariantXiangqi) {
\r
347 /* [HGM] capture straight ahead in Xiangqi */
\r
348 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
\r
349 callback(board, flags, NormalMove,
\r
350 rf, ff, rf - 1, ff, closure);
\r
352 /* and move sideways when across the river */
\r
353 for (s = -1; s <= 1; s += 2) {
\r
354 if (rf < BOARD_HEIGHT>>1 &&
\r
355 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
356 !BlackPiece(board[rf][ff+s]) ) {
\r
357 callback(board, flags, NormalMove,
\r
358 rf, ff, rf, ff+s, closure);
\r
363 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
\r
364 callback(board, flags,
\r
365 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
366 rf, ff, rf - 1, ff, closure);
\r
368 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
\r
369 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
370 gameInfo.variant != VariantCourier && /* [HGM] */
\r
371 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
\r
372 callback(board, flags, NormalMove,
\r
373 rf, ff, BOARD_HEIGHT-4, ff, closure);
\r
375 for (s = -1; s <= 1; s += 2) {
\r
376 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
377 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
378 WhitePiece(board[rf - 1][ff + s]))) {
\r
379 callback(board, flags,
\r
380 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
381 rf, ff, rf - 1, ff + s, closure);
\r
384 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
385 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
386 board[3][ff + s] == WhitePawn &&
\r
387 board[2][ff + s] == EmptySquare) {
\r
388 callback(board, flags, BlackCapturesEnPassant,
\r
389 rf, ff, 2, ff + s, closure);
\r
400 for (i = -1; i <= 1; i += 2)
\r
401 for (j = -1; j <= 1; j += 2)
\r
402 for (s = 1; s <= 2; s++) {
\r
405 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
406 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
\r
407 && !SameColor(board[rf][ff], board[rt][ft]))
\r
408 callback(board, flags, NormalMove,
\r
409 rf, ff, rt, ft, closure);
\r
413 case SHOGI WhiteKnight:
\r
414 for (s = -1; s <= 1; s += 2) {
\r
415 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
416 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
\r
417 callback(board, flags, NormalMove,
\r
418 rf, ff, rf + 2, ff + s, closure);
\r
423 case SHOGI BlackKnight:
\r
424 for (s = -1; s <= 1; s += 2) {
\r
425 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
426 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
\r
427 callback(board, flags, NormalMove,
\r
428 rf, ff, rf - 2, ff + s, closure);
\r
435 for (d = 0; d <= 1; d++)
\r
436 for (s = -1; s <= 1; s += 2) {
\r
438 for (i = 1;; i++) {
\r
439 rt = rf + (i * s) * d;
\r
440 ft = ff + (i * s) * (1 - d);
\r
441 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
442 if (m == 0 && board[rt][ft] == EmptySquare)
\r
443 callback(board, flags, NormalMove,
\r
444 rf, ff, rt, ft, closure);
\r
445 if (m == 1 && board[rt][ft] != EmptySquare &&
\r
446 !SameColor(board[rf][ff], board[rt][ft]) )
\r
447 callback(board, flags, NormalMove,
\r
448 rf, ff, rt, ft, closure);
\r
449 if (board[rt][ft] != EmptySquare && m++) break;
\r
454 /* Gold General (and all its promoted versions) . First do the */
\r
455 /* diagonal forward steps, then proceed as normal Wazir */
\r
456 case SHOGI WhiteWazir:
\r
457 case SHOGI (PROMOTED WhitePawn):
\r
458 case SHOGI (PROMOTED WhiteKnight):
\r
459 case SHOGI (PROMOTED WhiteQueen):
\r
460 case SHOGI (PROMOTED WhiteFerz):
\r
461 for (s = -1; s <= 1; s += 2) {
\r
462 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
463 !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
\r
464 callback(board, flags, NormalMove,
\r
465 rf, ff, rf + 1, ff + s, closure);
\r
470 case SHOGI BlackWazir:
\r
471 case SHOGI (PROMOTED BlackPawn):
\r
472 case SHOGI (PROMOTED BlackKnight):
\r
473 case SHOGI (PROMOTED BlackQueen):
\r
474 case SHOGI (PROMOTED BlackFerz):
\r
475 for (s = -1; s <= 1; s += 2) {
\r
476 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
477 !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
\r
478 callback(board, flags, NormalMove,
\r
479 rf, ff, rf - 1, ff + s, closure);
\r
486 for (d = 0; d <= 1; d++)
\r
487 for (s = -1; s <= 1; s += 2) {
\r
489 ft = ff + s * (1 - d);
\r
490 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
491 && !SameColor(board[rf][ff], board[rt][ft]) &&
\r
492 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
493 callback(board, flags, NormalMove,
\r
494 rf, ff, rt, ft, closure);
\r
500 /* [HGM] support Shatranj pieces */
\r
501 for (rs = -1; rs <= 1; rs += 2)
\r
502 for (fs = -1; fs <= 1; fs += 2) {
\r
505 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
506 && ( gameInfo.variant != VariantXiangqi ||
\r
507 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
\r
509 && !SameColor(board[rf][ff], board[rt][ft]))
\r
510 callback(board, flags, NormalMove,
\r
511 rf, ff, rt, ft, closure);
\r
515 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
\r
516 case SHOGI WhiteCardinal:
\r
517 case SHOGI BlackCardinal:
\r
520 /* Capablanca Archbishop continues as Knight */
\r
525 /* Shogi Bishops are ordinary Bishops */
\r
526 case SHOGI WhiteBishop:
\r
527 case SHOGI BlackBishop:
\r
530 for (rs = -1; rs <= 1; rs += 2)
\r
531 for (fs = -1; fs <= 1; fs += 2)
\r
532 for (i = 1;; i++) {
\r
533 rt = rf + (i * rs);
\r
534 ft = ff + (i * fs);
\r
535 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
536 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
537 callback(board, flags, NormalMove,
\r
538 rf, ff, rt, ft, closure);
\r
539 if (board[rt][ft] != EmptySquare) break;
\r
541 if(m==1) goto mounted;
\r
542 if(m==2) goto finishGold;
\r
543 /* Bishop falls through */
\r
546 /* Shogi Lance is unlike anything, and asymmetric at that */
\r
547 case SHOGI WhiteQueen:
\r
551 if (rt >= BOARD_HEIGHT) break;
\r
552 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
553 callback(board, flags, NormalMove,
\r
554 rf, ff, rt, ft, closure);
\r
555 if (board[rt][ft] != EmptySquare) break;
\r
559 case SHOGI BlackQueen:
\r
564 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
565 callback(board, flags, NormalMove,
\r
566 rf, ff, rt, ft, closure);
\r
567 if (board[rt][ft] != EmptySquare) break;
\r
571 /* Shogi Dragon King has to continue as Ferz after Rook moves */
\r
572 case SHOGI WhiteDragon:
\r
573 case SHOGI BlackDragon:
\r
576 /* Capablanca Chancellor sets flag to continue as Knight */
\r
577 case WhiteMarshall:
\r
578 case BlackMarshall:
\r
581 /* Shogi Rooks are ordinary Rooks */
\r
582 case SHOGI WhiteRook:
\r
583 case SHOGI BlackRook:
\r
586 for (d = 0; d <= 1; d++)
\r
587 for (s = -1; s <= 1; s += 2)
\r
588 for (i = 1;; i++) {
\r
589 rt = rf + (i * s) * d;
\r
590 ft = ff + (i * s) * (1 - d);
\r
591 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
592 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
593 callback(board, flags, NormalMove,
\r
594 rf, ff, rt, ft, closure);
\r
595 if (board[rt][ft] != EmptySquare) break;
\r
597 if(m==1) goto mounted;
\r
598 if(m==2) goto walking;
\r
603 for (rs = -1; rs <= 1; rs++)
\r
604 for (fs = -1; fs <= 1; fs++) {
\r
605 if (rs == 0 && fs == 0) continue;
\r
606 for (i = 1;; i++) {
\r
607 rt = rf + (i * rs);
\r
608 ft = ff + (i * fs);
\r
609 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
610 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
611 callback(board, flags, NormalMove,
\r
612 rf, ff, rt, ft, closure);
\r
613 if (board[rt][ft] != EmptySquare) break;
\r
618 /* Shogi Pawn and Silver General: first the Pawn move, */
\r
619 /* then the General continues like a Ferz */
\r
620 case SHOGI WhitePawn:
\r
621 case SHOGI WhiteFerz:
\r
622 if (rf < BOARD_HEIGHT-1 &&
\r
623 !SameColor(board[rf][ff], board[rf + 1][ff]) )
\r
624 callback(board, flags, NormalMove,
\r
625 rf, ff, rf + 1, ff, closure);
\r
626 if(piece != SHOGI WhitePawn) goto finishSilver;
\r
629 case SHOGI BlackPawn:
\r
630 case SHOGI BlackFerz:
\r
632 !SameColor(board[rf][ff], board[rf - 1][ff]) )
\r
633 callback(board, flags, NormalMove,
\r
634 rf, ff, rf - 1, ff, closure);
\r
635 if(piece == SHOGI BlackPawn) break;
\r
640 /* [HGM] support Shatranj pieces */
\r
641 for (rs = -1; rs <= 1; rs += 2)
\r
642 for (fs = -1; fs <= 1; fs += 2) {
\r
645 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
646 if (!SameColor(board[rf][ff], board[rt][ft]) &&
\r
647 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
648 callback(board, flags, NormalMove,
\r
649 rf, ff, rt, ft, closure);
\r
655 case SHOGI WhiteKing:
\r
656 case SHOGI BlackKing:
\r
660 for (i = -1; i <= 1; i++)
\r
661 for (j = -1; j <= 1; j++) {
\r
662 if (i == 0 && j == 0) continue;
\r
665 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
666 if (SameColor(board[rf][ff], board[rt][ft])) continue;
\r
667 callback(board, flags, NormalMove,
\r
668 rf, ff, rt, ft, closure);
\r
672 case WhiteNightrider:
\r
673 case BlackNightrider:
\r
674 for (i = -1; i <= 1; i += 2)
\r
675 for (j = -1; j <= 1; j += 2)
\r
676 for (s = 1; s <= 2; s++) { int k;
\r
679 ft = ff + k*j*(3-s);
\r
680 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
681 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
682 callback(board, flags, NormalMove,
\r
683 rf, ff, rt, ft, closure);
\r
684 if (board[rt][ft] != EmptySquare) break;
\r
699 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
\r
700 int rf, int ff, int rt, int ft,
\r
701 VOIDSTAR closure));
\r
703 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
707 int rf, ff, rt, ft;
\r
710 register GenLegalClosure *cl = (GenLegalClosure *) closure;
\r
712 if (!(flags & F_IGNORE_CHECK) &&
\r
713 CheckTest(board, flags, rf, ff, rt, ft,
\r
714 kind == WhiteCapturesEnPassant ||
\r
715 kind == BlackCapturesEnPassant)) return;
\r
716 if (flags & F_ATOMIC_CAPTURE) {
\r
717 if (board[rt][ft] != EmptySquare ||
\r
718 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
\r
720 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
\r
721 if (board[rf][ff] == king) return;
\r
722 for (r = rt-1; r <= rt+1; r++) {
\r
723 for (f = ft-1; f <= ft+1; f++) {
\r
724 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
\r
725 board[r][f] == king) return;
\r
730 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
\r
735 int rf, ff, rt, ft;
\r
737 } LegalityTestClosure;
\r
740 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
\r
741 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
\r
742 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
\r
743 moves that would destroy your own king. The CASTLE_OK flags are
\r
744 true if castling is not yet ruled out by a move of the king or
\r
745 rook. Return TRUE if the player on move is currently in check and
\r
746 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
\r
747 int GenLegal(board, flags, epfile, castlingRights, callback, closure)
\r
751 char castlingRights[];
\r
752 MoveCallback callback;
\r
755 GenLegalClosure cl;
\r
756 int ff, ft, k, left, right;
\r
757 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
\r
758 ChessSquare wKing = WhiteKing, bKing = BlackKing;
\r
762 GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
\r
764 if (!ignoreCheck &&
\r
765 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
\r
767 /* Generate castling moves */
\r
768 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
\r
769 wKing = WhiteUnicorn; bKing = BlackUnicorn;
\r
772 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
\r
773 if ((flags & F_WHITE_ON_MOVE) &&
\r
774 (flags & F_WHITE_KCASTLE_OK) &&
\r
775 board[0][ff] == wKing &&
\r
776 board[0][ff + 1] == EmptySquare &&
\r
777 board[0][ff + 2] == EmptySquare &&
\r
778 board[0][BOARD_RGHT-3] == EmptySquare &&
\r
779 board[0][BOARD_RGHT-2] == EmptySquare &&
\r
780 board[0][BOARD_RGHT-1] == WhiteRook &&
\r
781 castlingRights[0] >= 0 && /* [HGM] check rights */
\r
782 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
784 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
\r
785 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
\r
786 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
\r
788 callback(board, flags,
\r
789 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
\r
790 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2), closure);
\r
792 if ((flags & F_WHITE_ON_MOVE) &&
\r
793 (flags & F_WHITE_QCASTLE_OK) &&
\r
794 board[0][ff] == wKing &&
\r
795 board[0][ff - 1] == EmptySquare &&
\r
796 board[0][ff - 2] == EmptySquare &&
\r
797 board[0][BOARD_LEFT+2] == EmptySquare &&
\r
798 board[0][BOARD_LEFT+1] == EmptySquare &&
\r
799 board[0][BOARD_LEFT+0] == WhiteRook &&
\r
800 castlingRights[1] >= 0 && /* [HGM] check rights */
\r
801 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
803 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
\r
804 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
\r
805 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
\r
807 callback(board, flags,
\r
808 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
\r
809 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
811 if (!(flags & F_WHITE_ON_MOVE) &&
\r
812 (flags & F_BLACK_KCASTLE_OK) &&
\r
813 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
814 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
\r
815 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
\r
816 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
\r
817 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
\r
818 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
\r
819 castlingRights[3] >= 0 && /* [HGM] check rights */
\r
820 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
822 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
\r
823 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
\r
824 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
\r
826 callback(board, flags,
\r
827 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
\r
828 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2), closure);
\r
830 if (!(flags & F_WHITE_ON_MOVE) &&
\r
831 (flags & F_BLACK_QCASTLE_OK) &&
\r
832 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
833 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
\r
834 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
\r
835 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
\r
836 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
\r
837 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
\r
838 castlingRights[4] >= 0 && /* [HGM] check rights */
\r
839 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
841 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
\r
842 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
\r
843 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
\r
845 callback(board, flags,
\r
846 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
\r
847 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
851 if(gameInfo.variant == VariantFischeRandom) {
\r
853 /* generate all potential FRC castling moves (KxR), ignoring flags */
\r
854 /* [HGM] test if the Rooks we find have castling rights */
\r
857 if ((flags & F_WHITE_ON_MOVE) != 0) {
\r
858 ff = castlingRights[2]; /* King file if we have any rights */
\r
860 if (appData.debugMode) {
\r
861 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
\r
862 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
\r
864 ft = castlingRights[0]; /* Rook file if we have H-side rights */
\r
866 right = BOARD_RGHT-2;
\r
867 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
868 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
869 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
870 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
871 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
873 callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
\r
875 ft = castlingRights[1]; /* Rook file if we have A-side rights */
\r
876 left = BOARD_LEFT+2;
\r
878 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
879 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
880 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
881 if(ff > BOARD_LEFT+2)
\r
882 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
883 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
886 callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
\r
889 ff = castlingRights[5]; /* King file if we have any rights */
\r
891 ft = castlingRights[3]; /* Rook file if we have H-side rights */
\r
893 right = BOARD_RGHT-2;
\r
894 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
895 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
896 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
897 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
898 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
900 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
902 ft = castlingRights[4]; /* Rook file if we have A-side rights */
\r
903 left = BOARD_LEFT+2;
\r
905 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
906 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
907 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
908 if(ff > BOARD_LEFT+2)
\r
909 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
910 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
913 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
926 } CheckTestClosure;
\r
929 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
\r
930 int rf, int ff, int rt, int ft,
\r
931 VOIDSTAR closure));
\r
934 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
938 int rf, ff, rt, ft;
\r
941 register CheckTestClosure *cl = (CheckTestClosure *) closure;
\r
943 if (rt == cl->rking && ft == cl->fking) cl->check++;
\r
947 /* If the player on move were to move from (rf, ff) to (rt, ft), would
\r
948 he leave himself in check? Or if rf == -1, is the player on move
\r
949 in check now? enPassant must be TRUE if the indicated move is an
\r
950 e.p. capture. The possibility of castling out of a check along the
\r
951 back rank is not accounted for (i.e., we still return nonzero), as
\r
952 this is illegal anyway. Return value is the number of times the
\r
953 king is in check. */
\r
954 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
\r
957 int rf, ff, rt, ft, enPassant;
\r
959 CheckTestClosure cl;
\r
960 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
\r
961 ChessSquare captured = EmptySquare;
\r
962 /* Suppress warnings on uninitialized variables */
\r
964 if(gameInfo.variant == VariantXiangqi)
\r
965 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
\r
966 if(gameInfo.variant == VariantKnightmate)
\r
967 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
\r
971 captured = board[rf][ft];
\r
972 board[rf][ft] = EmptySquare;
\r
974 captured = board[rt][ft];
\r
976 board[rt][ft] = board[rf][ff];
\r
977 board[rf][ff] = EmptySquare;
\r
980 /* For compatibility with ICS wild 9, we scan the board in the
\r
981 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
\r
982 and we test only whether that one is in check. */
\r
984 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
\r
985 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
\r
986 if (board[cl.rking][cl.fking] == king) {
\r
987 if(gameInfo.variant == VariantXiangqi) {
\r
988 /* [HGM] In Xiangqi opposing Kings means check as well */
\r
990 dir = (king >= BlackPawn) ? -1 : 1;
\r
991 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
\r
992 board[i][cl.fking] == EmptySquare; i+=dir );
\r
993 if(i>=0 && i<BOARD_HEIGHT &&
\r
994 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
\r
998 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
\r
999 CheckTestCallback, (VOIDSTAR) &cl);
\r
1000 goto undo_move; /* 2-level break */
\r
1007 board[rf][ff] = board[rt][ft];
\r
1009 board[rf][ft] = captured;
\r
1010 board[rt][ft] = EmptySquare;
\r
1012 board[rt][ft] = captured;
\r
1020 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
\r
1021 int rf, int ff, int rt, int ft,
\r
1022 VOIDSTAR closure));
\r
1024 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1028 int rf, ff, rt, ft;
\r
1031 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
\r
1033 // if (appData.debugMode) {
\r
1034 // fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
1036 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
\r
1040 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
\r
1042 int flags, epfile;
\r
1043 int rf, ff, rt, ft, promoChar;
\r
1044 char castlingRights[];
\r
1046 LegalityTestClosure cl; ChessSquare piece = board[rf][ff];
\r
1048 if (appData.debugMode) {
\r
1050 for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
\r
1051 fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
1053 /* [HGM] Lance, Cobra and Falcon are wildcard pieces; consider all their moves legal */
\r
1054 /* (perhaps we should disallow moves that obviously leave us in check?) */
\r
1055 if(piece == WhiteFalcon || piece == BlackFalcon ||
\r
1056 piece == WhiteCobra || piece == BlackCobra ||
\r
1057 piece == WhiteLance || piece == BlackLance)
\r
1058 return NormalMove;
\r
1064 cl.kind = IllegalMove;
\r
1065 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
\r
1067 if(gameInfo.variant == VariantShogi) {
\r
1068 /* [HGM] Shogi promotions. '=' means defer */
\r
1069 if(rf != DROP_RANK && cl.kind == NormalMove) {
\r
1070 ChessSquare piece = board[rf][ff];
\r
1072 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
\r
1073 if(promoChar != NULLCHAR && promoChar != 'x' &&
\r
1074 promoChar != '+' && promoChar != '=' &&
\r
1075 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
\r
1076 cl.kind = IllegalMove;
\r
1077 else if(flags & F_WHITE_ON_MOVE) {
\r
1078 if( (int) piece < (int) WhiteWazir &&
\r
1079 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1080 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1081 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1082 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1083 else /* promotion optional, default is promote */
\r
1084 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
\r
1086 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1087 NormalMove : IllegalMove;
\r
1089 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1090 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1091 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1092 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1093 else /* promotion optional, default is promote */
\r
1094 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
\r
1096 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1097 NormalMove : IllegalMove;
\r
1101 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1102 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1104 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
\r
1106 cl.kind = IllegalMove;
\r
1109 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
\r
1115 } MateTestClosure;
\r
1117 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
\r
1118 int rf, int ff, int rt, int ft,
\r
1119 VOIDSTAR closure));
\r
1121 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1125 int rf, ff, rt, ft;
\r
1128 register MateTestClosure *cl = (MateTestClosure *) closure;
\r
1133 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
\r
1134 int MateTest(board, flags, epfile, castlingRights)
\r
1136 int flags, epfile;
\r
1137 char castlingRights[];
\r
1139 MateTestClosure cl;
\r
1143 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
\r
1144 if (cl.count > 0) {
\r
1145 return inCheck ? MT_CHECK : MT_NONE;
\r
1147 return inCheck || gameInfo.variant == VariantXiangqi ?
\r
1148 MT_CHECKMATE : MT_STALEMATE;
\r
1153 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
\r
1154 int rf, int ff, int rt, int ft,
\r
1155 VOIDSTAR closure));
\r
1157 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1161 int rf, ff, rt, ft;
\r
1164 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
\r
1166 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
\r
1167 || PieceToChar(board[rf][ff]) == '~'
\r
1168 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
\r
1170 (cl->rfIn == -1 || cl->rfIn == rf) &&
\r
1171 (cl->ffIn == -1 || cl->ffIn == ff) &&
\r
1172 (cl->rtIn == -1 || cl->rtIn == rt) &&
\r
1173 (cl->ftIn == -1 || cl->ftIn == ft)) {
\r
1176 cl->piece = board[rf][ff];
\r
1185 void Disambiguate(board, flags, epfile, closure)
\r
1187 int flags, epfile;
\r
1188 DisambiguateClosure *closure;
\r
1190 int illegal = 0; char c = closure->promoCharIn;
\r
1191 closure->count = 0;
\r
1192 closure->rf = closure->ff = closure->rt = closure->ft = 0;
\r
1193 closure->kind = ImpossibleMove;
\r
1194 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
\r
1195 if (closure->count == 0) {
\r
1196 /* See if it's an illegal move due to check */
\r
1198 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
\r
1199 (VOIDSTAR) closure);
\r
1200 if (closure->count == 0) {
\r
1201 /* No, it's not even that */
\r
1206 if (appData.debugMode) {
\r
1207 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1208 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1209 closure->promoCharIn,closure->promoCharIn);
\r
1211 if(gameInfo.variant == VariantShogi) {
\r
1212 /* [HGM] Shogi promotions. '=' means defer */
\r
1213 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
\r
1214 ChessSquare piece = closure->piece;
\r
1216 if (appData.debugMode) {
\r
1217 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1218 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1219 closure->promoCharIn,closure->promoCharIn);
\r
1222 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
\r
1223 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
\r
1224 closure->kind = IllegalMove;
\r
1225 else if(flags & F_WHITE_ON_MOVE) {
\r
1227 if (appData.debugMode) {
\r
1228 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1229 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1230 closure->promoCharIn,closure->promoCharIn);
\r
1233 if( (int) piece < (int) WhiteWazir &&
\r
1234 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
\r
1235 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
\r
1236 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1237 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
\r
1238 else /* promotion optional, default is promote */
\r
1239 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
\r
1241 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1242 NormalMove : IllegalMove;
\r
1244 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
\r
1245 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
\r
1246 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
\r
1247 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
\r
1248 else /* promotion optional, default is promote */
\r
1249 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
\r
1251 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1252 NormalMove : IllegalMove;
\r
1256 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
\r
1257 if (closure->kind == WhitePromotionQueen
\r
1258 || closure->kind == BlackPromotionQueen) {
\r
1260 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
\r
1261 closure->promoCharIn);
\r
1263 closure->kind = IllegalMove;
\r
1267 if (appData.debugMode) {
\r
1268 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1269 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1270 closure->promoCharIn,closure->promoCharIn);
\r
1273 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
\r
1274 if(closure->promoCharIn != '=')
\r
1275 closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));
\r
1276 else closure->promoChar = '=';
\r
1277 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
\r
1278 if (closure->count > 1) {
\r
1279 closure->kind = AmbiguousMove;
\r
1282 /* Note: If more than one illegal move matches, but no legal
\r
1283 moves, we return IllegalMove, not AmbiguousMove. Caller
\r
1284 can look at closure->count to detect this.
\r
1286 closure->kind = IllegalMove;
\r
1288 if(closure->kind == IllegalMove)
\r
1289 /* [HGM] might be a variant we don't understand, pass on promotion info */
\r
1290 closure->promoChar = closure->promoCharIn;
\r
1291 if (appData.debugMode) {
\r
1292 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1293 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,closure->promoChar);
\r
1300 ChessSquare piece;
\r
1301 int rf, ff, rt, ft;
\r
1307 } CoordsToAlgebraicClosure;
\r
1309 extern void CoordsToAlgebraicCallback P((Board board, int flags,
\r
1310 ChessMove kind, int rf, int ff,
\r
1311 int rt, int ft, VOIDSTAR closure));
\r
1313 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1317 int rf, ff, rt, ft;
\r
1320 register CoordsToAlgebraicClosure *cl =
\r
1321 (CoordsToAlgebraicClosure *) closure;
\r
1323 if (rt == cl->rt && ft == cl->ft &&
\r
1324 (board[rf][ff] == cl->piece
\r
1325 || PieceToChar(board[rf][ff]) == '~' &&
\r
1326 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
\r
1328 if (rf == cl->rf) {
\r
1329 if (ff == cl->ff) {
\r
1330 cl->kind = kind; /* this is the move we want */
\r
1332 cl->file++; /* need file to rule out this move */
\r
1335 if (ff == cl->ff) {
\r
1336 cl->rank++; /* need rank to rule out this move */
\r
1338 cl->either++; /* rank or file will rule out this move */
\r
1344 /* Convert coordinates to normal algebraic notation.
\r
1345 promoChar must be NULLCHAR or 'x' if not a promotion.
\r
1347 ChessMove CoordsToAlgebraic(board, flags, epfile,
\r
1348 rf, ff, rt, ft, promoChar, out)
\r
1350 int flags, epfile;
\r
1351 int rf, ff, rt, ft;
\r
1353 char out[MOVE_LEN];
\r
1355 ChessSquare piece;
\r
1357 char *outp = out, c;
\r
1358 CoordsToAlgebraicClosure cl;
\r
1360 if (rf == DROP_RANK) {
\r
1361 /* Bughouse piece drop */
\r
1362 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
\r
1364 *outp++ = ft + AAA;
\r
1366 *outp++ = rt + ONE;
\r
1367 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1369 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
\r
1372 if (promoChar == 'x') promoChar = NULLCHAR;
\r
1373 piece = board[rf][ff];
\r
1374 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
\r
1376 if (appData.debugMode)
\r
1377 fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar );
\r
1381 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
\r
1382 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1383 /* Keep short notation if move is illegal only because it
\r
1384 leaves the player in check, but still return IllegalMove */
\r
1385 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1386 rf, ff, rt, ft, promoChar);
\r
1387 if (kind == IllegalMove) break;
\r
1388 kind = IllegalMove;
\r
1391 *outp++ = ff + AAA;
\r
1392 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
\r
1393 /* Non-capture; use style "e5" */
\r
1395 *outp++ = rt + ONE;
\r
1396 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1398 /* Capture; use style "exd5" */
\r
1399 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
\r
1400 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
\r
1401 *outp++ = ft + AAA;
\r
1403 *outp++ = rt + ONE;
\r
1404 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1406 /* Use promotion suffix style "=Q" */
\r
1408 if (appData.debugMode)
\r
1409 fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar);
\r
1410 if (promoChar != NULLCHAR) {
\r
1411 if(gameInfo.variant == VariantShogi) {
\r
1412 /* [HGM] ... but not in Shogi! */
\r
1413 *outp++ = promoChar == '=' ? '=' : '+';
\r
1416 *outp++ = ToUpper(promoChar);
\r
1425 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
\r
1426 /* Code added by Tord: FRC castling. */
\r
1427 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
\r
1428 (piece == BlackKing && board[rt][ft] == BlackRook)) {
\r
1429 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
\r
1430 return LegalityTest(board, flags, epfile, initialRights,
\r
1431 rf, ff, rt, ft, promoChar);
\r
1433 /* End of code added by Tord */
\r
1434 /* Test for castling or ICS wild castling */
\r
1435 /* Use style "O-O" (oh-oh) for PGN compatibility */
\r
1436 else if (rf == rt &&
\r
1437 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
\r
1438 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
\r
1439 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
\r
1440 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
\r
1441 strcpy(out, "O-O");
\r
1443 strcpy(out, "O-O-O");
\r
1445 /* This notation is always unambiguous, unless there are
\r
1446 kings on both the d and e files, with "wild castling"
\r
1447 possible for the king on the d file and normal castling
\r
1448 possible for the other. ICS rules for wild 9
\r
1449 effectively make castling illegal for either king in
\r
1450 this situation. So I am not going to worry about it;
\r
1451 I'll just generate an ambiguous O-O in this case.
\r
1453 return LegalityTest(board, flags, epfile, initialRights,
\r
1454 rf, ff, rt, ft, promoChar);
\r
1457 /* else fall through */
\r
1465 cl.kind = IllegalMove;
\r
1466 cl.rank = cl.file = cl.either = 0;
\r
1467 GenLegal(board, flags, epfile, initialRights,
\r
1468 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1470 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1471 /* Generate pretty moves for moving into check, but
\r
1472 still return IllegalMove.
\r
1474 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1475 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1476 if (cl.kind == IllegalMove) break;
\r
1477 cl.kind = IllegalMove;
\r
1480 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
\r
1481 else "Ngf3" or "Ngxf7",
\r
1482 else "N1f3" or "N5xf7",
\r
1483 else "Ng1f3" or "Ng5xf7".
\r
1485 c = PieceToChar(piece) ;
\r
1486 if( c == '~' || c == '+') {
\r
1487 /* [HGM] print nonexistent piece as its demoted version */
\r
1488 piece = (ChessSquare) (DEMOTED piece);
\r
1490 if(c=='+') *outp++ = c;
\r
1491 *outp++ = ToUpper(PieceToChar(piece));
\r
1493 if (cl.file || (cl.either && !cl.rank)) {
\r
1494 *outp++ = ff + AAA;
\r
1498 *outp++ = rf + ONE;
\r
1499 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1502 if(board[rt][ft] != EmptySquare)
\r
1505 *outp++ = ft + AAA;
\r
1507 *outp++ = rt + ONE;
\r
1508 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1510 if (gameInfo.variant == VariantShogi) {
\r
1511 /* [HGM] in Shogi non-pawns can promote */
\r
1512 if(flags & F_WHITE_ON_MOVE) {
\r
1513 if( (int) cl.piece < (int) WhiteWazir &&
\r
1514 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1515 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1516 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1517 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1518 else cl.kind = WhitePromotionQueen; /* promotion optional */
\r
1520 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1521 NormalMove : IllegalMove;
\r
1523 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1524 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1525 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1526 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1527 else cl.kind = BlackPromotionQueen; /* promotion optional */
\r
1528 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1529 NormalMove : IllegalMove;
\r
1531 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1532 /* for optional promotions append '+' or '=' */
\r
1533 if(promoChar == '=') {
\r
1535 cl.kind = NormalMove;
\r
1536 } else *outp++ = '+';
\r
1538 } else if(cl.kind == IllegalMove) {
\r
1539 /* Illegal move specifies any given promotion */
\r
1540 if(promoChar != NULLCHAR && promoChar != 'x') {
\r
1542 *outp++ = ToUpper(promoChar);
\r
1549 /* [HGM] Always long notation for fairies we don't know */
\r
1556 case WhiteGrasshopper:
\r
1557 case BlackGrasshopper:
\r
1559 /* Moving a nonexistent piece */
\r
1563 /* Not a legal move, even ignoring check.
\r
1564 If there was a piece on the from square,
\r
1565 use style "Ng1g3" or "Ng1xe8";
\r
1566 if there was a pawn or nothing (!),
\r
1567 use style "g1g3" or "g1xe8". Use "x"
\r
1568 if a piece was on the to square, even
\r
1569 a piece of the same color.
\r
1572 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
\r
1573 *outp++ = ToUpper(PieceToChar(piece));
\r
1575 *outp++ = ff + AAA;
\r
1577 *outp++ = rf + ONE;
\r
1578 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1579 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
\r
1580 *outp++ = ft + AAA;
\r
1582 *outp++ = rt + ONE;
\r
1583 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1584 /* Use promotion suffix style "=Q" */
\r
1585 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1587 *outp++ = ToUpper(promoChar);
\r
1591 return IllegalMove;
\r