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 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
\r
787 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
\r
789 callback(board, flags,
\r
790 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
\r
791 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
\r
793 if ((flags & F_WHITE_ON_MOVE) &&
\r
794 (flags & F_WHITE_QCASTLE_OK) &&
\r
795 board[0][ff] == wKing &&
\r
796 board[0][ff - 1] == EmptySquare &&
\r
797 board[0][ff - 2] == EmptySquare &&
\r
798 board[0][BOARD_LEFT+2] == EmptySquare &&
\r
799 board[0][BOARD_LEFT+1] == EmptySquare &&
\r
800 board[0][BOARD_LEFT+0] == WhiteRook &&
\r
801 castlingRights[1] >= 0 && /* [HGM] check rights */
\r
802 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
804 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
\r
805 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
\r
806 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
\r
808 callback(board, flags,
\r
809 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
\r
810 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
812 if (!(flags & F_WHITE_ON_MOVE) &&
\r
813 (flags & F_BLACK_KCASTLE_OK) &&
\r
814 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
815 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
\r
816 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
\r
817 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
\r
818 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
\r
819 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
\r
820 castlingRights[3] >= 0 && /* [HGM] check rights */
\r
821 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
823 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
\r
824 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
\r
825 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
\r
826 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
\r
828 callback(board, flags,
\r
829 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
\r
830 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
\r
832 if (!(flags & F_WHITE_ON_MOVE) &&
\r
833 (flags & F_BLACK_QCASTLE_OK) &&
\r
834 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
835 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
\r
836 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
\r
837 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
\r
838 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
\r
839 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
\r
840 castlingRights[4] >= 0 && /* [HGM] check rights */
\r
841 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
843 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
\r
844 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
\r
845 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
\r
847 callback(board, flags,
\r
848 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
\r
849 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
853 if(gameInfo.variant == VariantFischeRandom) {
\r
855 /* generate all potential FRC castling moves (KxR), ignoring flags */
\r
856 /* [HGM] test if the Rooks we find have castling rights */
\r
859 if ((flags & F_WHITE_ON_MOVE) != 0) {
\r
860 ff = castlingRights[2]; /* King file if we have any rights */
\r
861 if(ff > 0 && board[0][ff] == WhiteKing) {
\r
862 if (appData.debugMode) {
\r
863 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
\r
864 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
\r
866 ft = castlingRights[0]; /* Rook file if we have H-side rights */
\r
868 right = BOARD_RGHT-2;
\r
869 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
870 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
871 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
872 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
873 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
874 if(ft >= 0 && board[0][ft] == WhiteRook)
\r
875 callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
\r
877 ft = castlingRights[1]; /* Rook file if we have A-side rights */
\r
878 left = BOARD_LEFT+2;
\r
880 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
881 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
882 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
883 if(ff > BOARD_LEFT+2)
\r
884 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
885 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
886 if(ft >= 0 && board[0][ft] == WhiteRook)
\r
887 callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
\r
890 ff = castlingRights[5]; /* King file if we have any rights */
\r
891 if(ff > 0 && board[BOARD_HEIGHT-1][ff] == BlackKing) {
\r
892 ft = castlingRights[3]; /* Rook file if we have H-side rights */
\r
894 right = BOARD_RGHT-2;
\r
895 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
896 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
897 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
898 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
899 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
900 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
\r
901 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
903 ft = castlingRights[4]; /* Rook file if we have A-side rights */
\r
904 left = BOARD_LEFT+2;
\r
906 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
907 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
908 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
909 if(ff > BOARD_LEFT+2)
\r
910 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
911 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
912 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
\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 || gameInfo.variant == VariantShatranj ?
\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 if (appData.debugMode) {
\r
1195 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1196 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1197 closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
\r
1199 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
\r
1200 if (closure->count == 0) {
\r
1201 /* See if it's an illegal move due to check */
\r
1203 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
\r
1204 (VOIDSTAR) closure);
\r
1205 if (closure->count == 0) {
\r
1206 /* No, it's not even that */
\r
1207 if (appData.debugMode) { int i, j;
\r
1208 for(i=BOARD_HEIGHT-1; i>=0; i--) {
\r
1209 for(j=0; j<BOARD_WIDTH; j++)
\r
1210 fprintf(debugFP, "%3d", (int) board[i][j]);
\r
1211 fprintf(debugFP, "\n");
\r
1218 if(gameInfo.variant == VariantShogi) {
\r
1219 /* [HGM] Shogi promotions. '=' means defer */
\r
1220 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
\r
1221 ChessSquare piece = closure->piece;
\r
1223 if (appData.debugMode) {
\r
1224 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1225 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1226 closure->promoCharIn,closure->promoCharIn);
\r
1229 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
\r
1230 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
\r
1231 closure->kind = IllegalMove;
\r
1232 else if(flags & F_WHITE_ON_MOVE) {
\r
1234 if (appData.debugMode) {
\r
1235 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1236 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1237 closure->promoCharIn,closure->promoCharIn);
\r
1240 if( (int) piece < (int) WhiteWazir &&
\r
1241 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
\r
1242 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
\r
1243 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1244 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
\r
1245 else /* promotion optional, default is promote */
\r
1246 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
\r
1248 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1249 NormalMove : IllegalMove;
\r
1251 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
\r
1252 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
\r
1253 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
\r
1254 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
\r
1255 else /* promotion optional, default is promote */
\r
1256 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
\r
1258 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1259 NormalMove : IllegalMove;
\r
1263 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
\r
1264 if (closure->kind == WhitePromotionQueen
\r
1265 || closure->kind == BlackPromotionQueen) {
\r
1267 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
\r
1268 closure->promoCharIn);
\r
1270 closure->kind = IllegalMove;
\r
1274 if (appData.debugMode) {
\r
1275 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1276 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1277 closure->promoCharIn,closure->promoCharIn);
\r
1280 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
\r
1281 if(closure->promoCharIn != '=')
\r
1282 closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));
\r
1283 else closure->promoChar = '=';
\r
1284 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
\r
1285 if (closure->count > 1) {
\r
1286 closure->kind = AmbiguousMove;
\r
1289 /* Note: If more than one illegal move matches, but no legal
\r
1290 moves, we return IllegalMove, not AmbiguousMove. Caller
\r
1291 can look at closure->count to detect this.
\r
1293 closure->kind = IllegalMove;
\r
1295 if(closure->kind == IllegalMove)
\r
1296 /* [HGM] might be a variant we don't understand, pass on promotion info */
\r
1297 closure->promoChar = closure->promoCharIn;
\r
1298 if (appData.debugMode) {
\r
1299 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1300 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
\r
1301 closure->promoChar >= ' ' ? closure->promoChar:'-');
\r
1308 ChessSquare piece;
\r
1309 int rf, ff, rt, ft;
\r
1315 } CoordsToAlgebraicClosure;
\r
1317 extern void CoordsToAlgebraicCallback P((Board board, int flags,
\r
1318 ChessMove kind, int rf, int ff,
\r
1319 int rt, int ft, VOIDSTAR closure));
\r
1321 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1325 int rf, ff, rt, ft;
\r
1328 register CoordsToAlgebraicClosure *cl =
\r
1329 (CoordsToAlgebraicClosure *) closure;
\r
1331 if (rt == cl->rt && ft == cl->ft &&
\r
1332 (board[rf][ff] == cl->piece
\r
1333 || PieceToChar(board[rf][ff]) == '~' &&
\r
1334 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
\r
1336 if (rf == cl->rf) {
\r
1337 if (ff == cl->ff) {
\r
1338 cl->kind = kind; /* this is the move we want */
\r
1340 cl->file++; /* need file to rule out this move */
\r
1343 if (ff == cl->ff) {
\r
1344 cl->rank++; /* need rank to rule out this move */
\r
1346 cl->either++; /* rank or file will rule out this move */
\r
1352 /* Convert coordinates to normal algebraic notation.
\r
1353 promoChar must be NULLCHAR or 'x' if not a promotion.
\r
1355 ChessMove CoordsToAlgebraic(board, flags, epfile,
\r
1356 rf, ff, rt, ft, promoChar, out)
\r
1358 int flags, epfile;
\r
1359 int rf, ff, rt, ft;
\r
1361 char out[MOVE_LEN];
\r
1363 ChessSquare piece;
\r
1365 char *outp = out, c;
\r
1366 CoordsToAlgebraicClosure cl;
\r
1368 if (rf == DROP_RANK) {
\r
1369 /* Bughouse piece drop */
\r
1370 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
\r
1372 *outp++ = ft + AAA;
\r
1374 *outp++ = rt + ONE;
\r
1375 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1377 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
\r
1380 if (promoChar == 'x') promoChar = NULLCHAR;
\r
1381 piece = board[rf][ff];
\r
1382 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
\r
1384 if (appData.debugMode)
\r
1385 fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
\r
1389 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
\r
1390 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1391 /* Keep short notation if move is illegal only because it
\r
1392 leaves the player in check, but still return IllegalMove */
\r
1393 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1394 rf, ff, rt, ft, promoChar);
\r
1395 if (kind == IllegalMove) break;
\r
1396 kind = IllegalMove;
\r
1399 *outp++ = ff + AAA;
\r
1400 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
\r
1401 /* Non-capture; use style "e5" */
\r
1403 *outp++ = rt + ONE;
\r
1404 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1406 /* Capture; use style "exd5" */
\r
1407 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
\r
1408 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
\r
1409 *outp++ = ft + AAA;
\r
1411 *outp++ = rt + ONE;
\r
1412 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1414 /* Use promotion suffix style "=Q" */
\r
1416 if (appData.debugMode)
\r
1417 fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
\r
1418 if (promoChar != NULLCHAR) {
\r
1419 if(gameInfo.variant == VariantShogi) {
\r
1420 /* [HGM] ... but not in Shogi! */
\r
1421 *outp++ = promoChar == '=' ? '=' : '+';
\r
1424 *outp++ = ToUpper(promoChar);
\r
1433 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
\r
1434 /* Code added by Tord: FRC castling. */
\r
1435 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
\r
1436 (piece == BlackKing && board[rt][ft] == BlackRook)) {
\r
1437 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
\r
1438 return LegalityTest(board, flags, epfile, initialRights,
\r
1439 rf, ff, rt, ft, promoChar);
\r
1441 /* End of code added by Tord */
\r
1442 /* Test for castling or ICS wild castling */
\r
1443 /* Use style "O-O" (oh-oh) for PGN compatibility */
\r
1444 else if (rf == rt &&
\r
1445 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
\r
1446 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
\r
1447 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
\r
1448 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
\r
1449 strcpy(out, "O-O");
\r
1451 strcpy(out, "O-O-O");
\r
1453 /* This notation is always unambiguous, unless there are
\r
1454 kings on both the d and e files, with "wild castling"
\r
1455 possible for the king on the d file and normal castling
\r
1456 possible for the other. ICS rules for wild 9
\r
1457 effectively make castling illegal for either king in
\r
1458 this situation. So I am not going to worry about it;
\r
1459 I'll just generate an ambiguous O-O in this case.
\r
1461 return LegalityTest(board, flags, epfile, initialRights,
\r
1462 rf, ff, rt, ft, promoChar);
\r
1465 /* else fall through */
\r
1473 cl.kind = IllegalMove;
\r
1474 cl.rank = cl.file = cl.either = 0;
\r
1475 GenLegal(board, flags, epfile, initialRights,
\r
1476 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1478 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1479 /* Generate pretty moves for moving into check, but
\r
1480 still return IllegalMove.
\r
1482 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1483 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1484 if (cl.kind == IllegalMove) break;
\r
1485 cl.kind = IllegalMove;
\r
1488 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
\r
1489 else "Ngf3" or "Ngxf7",
\r
1490 else "N1f3" or "N5xf7",
\r
1491 else "Ng1f3" or "Ng5xf7".
\r
1493 c = PieceToChar(piece) ;
\r
1494 if( c == '~' || c == '+') {
\r
1495 /* [HGM] print nonexistent piece as its demoted version */
\r
1496 piece = (ChessSquare) (DEMOTED piece);
\r
1498 if(c=='+') *outp++ = c;
\r
1499 *outp++ = ToUpper(PieceToChar(piece));
\r
1501 if (cl.file || (cl.either && !cl.rank)) {
\r
1502 *outp++ = ff + AAA;
\r
1506 *outp++ = rf + ONE;
\r
1507 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1510 if(board[rt][ft] != EmptySquare)
\r
1513 *outp++ = ft + AAA;
\r
1515 *outp++ = rt + ONE;
\r
1516 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1518 if (gameInfo.variant == VariantShogi) {
\r
1519 /* [HGM] in Shogi non-pawns can promote */
\r
1520 if(flags & F_WHITE_ON_MOVE) {
\r
1521 if( (int) cl.piece < (int) WhiteWazir &&
\r
1522 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1523 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1524 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1525 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1526 else cl.kind = WhitePromotionQueen; /* promotion optional */
\r
1528 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1529 NormalMove : IllegalMove;
\r
1531 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1532 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1533 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1534 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1535 else cl.kind = BlackPromotionQueen; /* promotion optional */
\r
1536 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1537 NormalMove : IllegalMove;
\r
1539 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1540 /* for optional promotions append '+' or '=' */
\r
1541 if(promoChar == '=') {
\r
1543 cl.kind = NormalMove;
\r
1544 } else *outp++ = '+';
\r
1546 } else if(cl.kind == IllegalMove) {
\r
1547 /* Illegal move specifies any given promotion */
\r
1548 if(promoChar != NULLCHAR && promoChar != 'x') {
\r
1550 *outp++ = ToUpper(promoChar);
\r
1557 /* [HGM] Always long notation for fairies we don't know */
\r
1564 case WhiteGrasshopper:
\r
1565 case BlackGrasshopper:
\r
1567 /* Moving a nonexistent piece */
\r
1571 /* Not a legal move, even ignoring check.
\r
1572 If there was a piece on the from square,
\r
1573 use style "Ng1g3" or "Ng1xe8";
\r
1574 if there was a pawn or nothing (!),
\r
1575 use style "g1g3" or "g1xe8". Use "x"
\r
1576 if a piece was on the to square, even
\r
1577 a piece of the same color.
\r
1580 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
\r
1581 *outp++ = ToUpper(PieceToChar(piece));
\r
1583 *outp++ = ff + AAA;
\r
1585 *outp++ = rf + ONE;
\r
1586 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1587 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
\r
1588 *outp++ = ft + AAA;
\r
1590 *outp++ = rt + ONE;
\r
1591 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1592 /* Use promotion suffix style "=Q" */
\r
1593 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1595 *outp++ = ToUpper(promoChar);
\r
1599 return IllegalMove;
\r