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',
\r
200 'W', 'E', 'M', 'O', 'U', 'H', 'A', 'C', 'G', 'S',
\r
201 'K', 'p', 'n', 'b', 'r', 'q', 'f',
\r
202 'w', 'e', 'm', 'o', 'u', 'h', 'a', 'c', 'g', 's',
\r
206 char PieceToChar(p)
\r
209 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
\r
210 return pieceToChar[(int) p];
\r
213 int PieceToNumber(p)
\r
217 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
\r
219 while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
\r
223 ChessSquare CharToPiece(c)
\r
227 for(i=0; i< (int) EmptySquare; i++)
\r
228 if(pieceToChar[i] == c) return (ChessSquare) i;
\r
229 return EmptySquare;
\r
232 void CopyBoard(to, from)
\r
237 for (i = 0; i < BOARD_HEIGHT; i++)
\r
238 for (j = 0; j < BOARD_WIDTH; j++)
\r
239 to[i][j] = from[i][j];
\r
242 int CompareBoards(board1, board2)
\r
243 Board board1, board2;
\r
247 for (i = 0; i < BOARD_HEIGHT; i++)
\r
248 for (j = 0; j < BOARD_WIDTH; j++) {
\r
249 if (board1[i][j] != board2[i][j])
\r
256 /* Call callback once for each pseudo-legal move in the given
\r
257 position, except castling moves. A move is pseudo-legal if it is
\r
258 legal, or if it would be legal except that it leaves the king in
\r
259 check. In the arguments, epfile is EP_NONE if the previous move
\r
260 was not a double pawn push, or the file 0..7 if it was, or
\r
261 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
\r
262 Promotion moves generated are to Queen only.
\r
264 void GenPseudoLegal(board, flags, epfile, callback, closure)
\r
268 MoveCallback callback;
\r
272 int i, j, d, s, fs, rs, rt, ft, m;
\r
274 for (rf = 0; rf < BOARD_HEIGHT; rf++)
\r
275 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
\r
278 if (flags & F_WHITE_ON_MOVE) {
\r
279 if (!WhitePiece(board[rf][ff])) continue;
\r
281 if (!BlackPiece(board[rf][ff])) continue;
\r
283 m = 0; piece = board[rf][ff];
\r
284 if(PieceToChar(piece) == '~')
\r
285 piece = (ChessSquare) ( DEMOTED piece );
\r
286 if(gameInfo.variant == VariantShogi)
\r
287 piece = (ChessSquare) ( SHOGI piece );
\r
290 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
\r
292 /* can't happen ([HGM] except for faries...) */
\r
296 if(gameInfo.variant == VariantXiangqi) {
\r
297 /* [HGM] capture and move straight ahead in Xiangqi */
\r
298 if (rf < BOARD_HEIGHT-1 &&
\r
299 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
\r
300 callback(board, flags, NormalMove,
\r
301 rf, ff, rf + 1, ff, closure);
\r
303 /* and move sideways when across the river */
\r
304 for (s = -1; s <= 1; s += 2) {
\r
305 if (rf >= BOARD_HEIGHT>>1 &&
\r
306 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
307 !WhitePiece(board[rf][ff+s]) ) {
\r
308 callback(board, flags, NormalMove,
\r
309 rf, ff, rf, ff+s, closure);
\r
314 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
\r
315 callback(board, flags,
\r
316 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
317 rf, ff, rf + 1, ff, closure);
\r
319 if (rf == 1 && board[2][ff] == EmptySquare &&
\r
320 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
321 gameInfo.variant != VariantCourier && /* [HGM] */
\r
322 board[3][ff] == EmptySquare ) {
\r
323 callback(board, flags, NormalMove,
\r
324 rf, ff, 3, ff, closure);
\r
326 for (s = -1; s <= 1; s += 2) {
\r
327 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
328 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
329 BlackPiece(board[rf + 1][ff + s]))) {
\r
330 callback(board, flags,
\r
331 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
332 rf, ff, rf + 1, ff + s, closure);
\r
334 if (rf == BOARD_HEIGHT-4) {
\r
335 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
336 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
337 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
\r
338 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
\r
339 callback(board, flags, WhiteCapturesEnPassant,
\r
340 rf, ff, 5, ff + s, closure);
\r
347 if(gameInfo.variant == VariantXiangqi) {
\r
348 /* [HGM] capture straight ahead in Xiangqi */
\r
349 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
\r
350 callback(board, flags, NormalMove,
\r
351 rf, ff, rf - 1, ff, closure);
\r
353 /* and move sideways when across the river */
\r
354 for (s = -1; s <= 1; s += 2) {
\r
355 if (rf < BOARD_HEIGHT>>1 &&
\r
356 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
357 !BlackPiece(board[rf][ff+s]) ) {
\r
358 callback(board, flags, NormalMove,
\r
359 rf, ff, rf, ff+s, closure);
\r
364 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
\r
365 callback(board, flags,
\r
366 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
367 rf, ff, rf - 1, ff, closure);
\r
369 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
\r
370 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
371 gameInfo.variant != VariantCourier && /* [HGM] */
\r
372 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
\r
373 callback(board, flags, NormalMove,
\r
374 rf, ff, BOARD_HEIGHT-4, ff, closure);
\r
376 for (s = -1; s <= 1; s += 2) {
\r
377 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
378 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
379 WhitePiece(board[rf - 1][ff + s]))) {
\r
380 callback(board, flags,
\r
381 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
382 rf, ff, rf - 1, ff + s, closure);
\r
385 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
386 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
387 board[3][ff + s] == WhitePawn &&
\r
388 board[2][ff + s] == EmptySquare) {
\r
389 callback(board, flags, BlackCapturesEnPassant,
\r
390 rf, ff, 2, ff + s, closure);
\r
401 for (i = -1; i <= 1; i += 2)
\r
402 for (j = -1; j <= 1; j += 2)
\r
403 for (s = 1; s <= 2; s++) {
\r
406 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
407 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
\r
408 && !SameColor(board[rf][ff], board[rt][ft]))
\r
409 callback(board, flags, NormalMove,
\r
410 rf, ff, rt, ft, closure);
\r
414 case SHOGI WhiteKnight:
\r
415 for (s = -1; s <= 1; s += 2) {
\r
416 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
417 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
\r
418 callback(board, flags, NormalMove,
\r
419 rf, ff, rf + 2, ff + s, closure);
\r
424 case SHOGI BlackKnight:
\r
425 for (s = -1; s <= 1; s += 2) {
\r
426 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
427 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
\r
428 callback(board, flags, NormalMove,
\r
429 rf, ff, rf - 2, ff + s, closure);
\r
436 for (d = 0; d <= 1; d++)
\r
437 for (s = -1; s <= 1; s += 2) {
\r
439 for (i = 1;; i++) {
\r
440 rt = rf + (i * s) * d;
\r
441 ft = ff + (i * s) * (1 - d);
\r
442 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
443 if (m == 0 && board[rt][ft] == EmptySquare)
\r
444 callback(board, flags, NormalMove,
\r
445 rf, ff, rt, ft, closure);
\r
446 if (m == 1 && board[rt][ft] != EmptySquare &&
\r
447 !SameColor(board[rf][ff], board[rt][ft]) )
\r
448 callback(board, flags, NormalMove,
\r
449 rf, ff, rt, ft, closure);
\r
450 if (board[rt][ft] != EmptySquare && m++) break;
\r
455 /* Gold General (and all its promoted versions) . First do the */
\r
456 /* diagonal forward steps, then proceed as normal Wazir */
\r
457 case SHOGI WhiteWazir:
\r
458 case SHOGI (PROMOTED WhitePawn):
\r
459 case SHOGI (PROMOTED WhiteKnight):
\r
460 case SHOGI (PROMOTED WhiteQueen):
\r
461 case SHOGI (PROMOTED WhiteFerz):
\r
462 for (s = -1; s <= 1; s += 2) {
\r
463 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
464 !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
\r
465 callback(board, flags, NormalMove,
\r
466 rf, ff, rf + 1, ff + s, closure);
\r
471 case SHOGI BlackWazir:
\r
472 case SHOGI (PROMOTED BlackPawn):
\r
473 case SHOGI (PROMOTED BlackKnight):
\r
474 case SHOGI (PROMOTED BlackQueen):
\r
475 case SHOGI (PROMOTED BlackFerz):
\r
476 for (s = -1; s <= 1; s += 2) {
\r
477 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
478 !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
\r
479 callback(board, flags, NormalMove,
\r
480 rf, ff, rf - 1, ff + s, closure);
\r
487 for (d = 0; d <= 1; d++)
\r
488 for (s = -1; s <= 1; s += 2) {
\r
490 ft = ff + s * (1 - d);
\r
491 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
492 && !SameColor(board[rf][ff], board[rt][ft]) &&
\r
493 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
494 callback(board, flags, NormalMove,
\r
495 rf, ff, rt, ft, closure);
\r
501 /* [HGM] support Shatranj pieces */
\r
502 for (rs = -1; rs <= 1; rs += 2)
\r
503 for (fs = -1; fs <= 1; fs += 2) {
\r
506 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
507 && ( gameInfo.variant != VariantXiangqi ||
\r
508 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
\r
510 && !SameColor(board[rf][ff], board[rt][ft]))
\r
511 callback(board, flags, NormalMove,
\r
512 rf, ff, rt, ft, closure);
\r
516 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
\r
517 case SHOGI WhiteCardinal:
\r
518 case SHOGI BlackCardinal:
\r
521 /* Capablanca Archbishop continues as Knight */
\r
522 case WhiteCardinal:
\r
523 case BlackCardinal:
\r
526 /* Shogi Bishops are ordinary Bishops */
\r
527 case SHOGI WhiteBishop:
\r
528 case SHOGI BlackBishop:
\r
531 for (rs = -1; rs <= 1; rs += 2)
\r
532 for (fs = -1; fs <= 1; fs += 2)
\r
533 for (i = 1;; i++) {
\r
534 rt = rf + (i * rs);
\r
535 ft = ff + (i * fs);
\r
536 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
537 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
538 callback(board, flags, NormalMove,
\r
539 rf, ff, rt, ft, closure);
\r
540 if (board[rt][ft] != EmptySquare) break;
\r
542 if(m==1) goto mounted;
\r
543 if(m==2) goto finishGold;
\r
544 /* Bishop falls through */
\r
547 /* Shogi Lance is unlike anything, and asymmetric at that */
\r
548 case SHOGI WhiteQueen:
\r
552 if (rt >= BOARD_HEIGHT) break;
\r
553 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
554 callback(board, flags, NormalMove,
\r
555 rf, ff, rt, ft, closure);
\r
556 if (board[rt][ft] != EmptySquare) break;
\r
560 case SHOGI BlackQueen:
\r
565 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
566 callback(board, flags, NormalMove,
\r
567 rf, ff, rt, ft, closure);
\r
568 if (board[rt][ft] != EmptySquare) break;
\r
572 /* Shogi Dragon King has to continue as Ferz after Rook moves */
\r
573 case SHOGI WhiteMarshall:
\r
574 case SHOGI BlackMarshall:
\r
577 /* Capablanca Chancellor sets flag to continue as Knight */
\r
578 case WhiteMarshall:
\r
579 case BlackMarshall:
\r
582 /* Shogi Rooks are ordinary Rooks */
\r
583 case SHOGI WhiteRook:
\r
584 case SHOGI BlackRook:
\r
587 for (d = 0; d <= 1; d++)
\r
588 for (s = -1; s <= 1; s += 2)
\r
589 for (i = 1;; i++) {
\r
590 rt = rf + (i * s) * d;
\r
591 ft = ff + (i * s) * (1 - d);
\r
592 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
593 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
594 callback(board, flags, NormalMove,
\r
595 rf, ff, rt, ft, closure);
\r
596 if (board[rt][ft] != EmptySquare) break;
\r
598 if(m==1) goto mounted;
\r
599 if(m==2) goto walking;
\r
604 for (rs = -1; rs <= 1; rs++)
\r
605 for (fs = -1; fs <= 1; fs++) {
\r
606 if (rs == 0 && fs == 0) continue;
\r
607 for (i = 1;; i++) {
\r
608 rt = rf + (i * rs);
\r
609 ft = ff + (i * fs);
\r
610 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
611 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
612 callback(board, flags, NormalMove,
\r
613 rf, ff, rt, ft, closure);
\r
614 if (board[rt][ft] != EmptySquare) break;
\r
619 /* Shogi Pawn and Silver General: first the Pawn move, */
\r
620 /* then the General continues like a Ferz */
\r
621 case SHOGI WhitePawn:
\r
622 case SHOGI WhiteFerz:
\r
623 if (rf < BOARD_HEIGHT-1 &&
\r
624 !SameColor(board[rf][ff], board[rf + 1][ff]) )
\r
625 callback(board, flags, NormalMove,
\r
626 rf, ff, rf + 1, ff, closure);
\r
627 if(piece != SHOGI WhitePawn) goto finishSilver;
\r
630 case SHOGI BlackPawn:
\r
631 case SHOGI BlackFerz:
\r
633 !SameColor(board[rf][ff], board[rf - 1][ff]) )
\r
634 callback(board, flags, NormalMove,
\r
635 rf, ff, rf - 1, ff, closure);
\r
636 if(piece == SHOGI BlackPawn) break;
\r
641 /* [HGM] support Shatranj pieces */
\r
642 for (rs = -1; rs <= 1; rs += 2)
\r
643 for (fs = -1; fs <= 1; fs += 2) {
\r
646 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
647 if (!SameColor(board[rf][ff], board[rt][ft]) &&
\r
648 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
649 callback(board, flags, NormalMove,
\r
650 rf, ff, rt, ft, closure);
\r
656 case SHOGI WhiteKing:
\r
657 case SHOGI BlackKing:
\r
661 for (i = -1; i <= 1; i++)
\r
662 for (j = -1; j <= 1; j++) {
\r
663 if (i == 0 && j == 0) continue;
\r
666 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
667 if (SameColor(board[rf][ff], board[rt][ft])) continue;
\r
668 callback(board, flags, NormalMove,
\r
669 rf, ff, rt, ft, closure);
\r
682 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
\r
683 int rf, int ff, int rt, int ft,
\r
684 VOIDSTAR closure));
\r
686 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
690 int rf, ff, rt, ft;
\r
693 register GenLegalClosure *cl = (GenLegalClosure *) closure;
\r
695 if (!(flags & F_IGNORE_CHECK) &&
\r
696 CheckTest(board, flags, rf, ff, rt, ft,
\r
697 kind == WhiteCapturesEnPassant ||
\r
698 kind == BlackCapturesEnPassant)) return;
\r
699 if (flags & F_ATOMIC_CAPTURE) {
\r
700 if (board[rt][ft] != EmptySquare ||
\r
701 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
\r
703 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
\r
704 if (board[rf][ff] == king) return;
\r
705 for (r = rt-1; r <= rt+1; r++) {
\r
706 for (f = ft-1; f <= ft+1; f++) {
\r
707 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
\r
708 board[r][f] == king) return;
\r
713 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
\r
718 int rf, ff, rt, ft;
\r
720 } LegalityTestClosure;
\r
723 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
\r
724 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
\r
725 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
\r
726 moves that would destroy your own king. The CASTLE_OK flags are
\r
727 true if castling is not yet ruled out by a move of the king or
\r
728 rook. Return TRUE if the player on move is currently in check and
\r
729 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
\r
730 int GenLegal(board, flags, epfile, castlingRights, callback, closure)
\r
734 char castlingRights[];
\r
735 MoveCallback callback;
\r
738 GenLegalClosure cl;
\r
740 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
\r
744 GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
\r
746 if (!ignoreCheck &&
\r
747 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
\r
749 /* Generate castling moves */
\r
750 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
\r
751 if ((flags & F_WHITE_ON_MOVE) &&
\r
752 (flags & F_WHITE_KCASTLE_OK) &&
\r
753 board[0][ff] == WhiteKing &&
\r
754 board[0][ff + 1] == EmptySquare &&
\r
755 board[0][ff + 2] == EmptySquare &&
\r
756 board[0][BOARD_RGHT-3] == EmptySquare &&
\r
757 board[0][BOARD_RGHT-2] == EmptySquare &&
\r
758 board[0][BOARD_RGHT-1] == WhiteRook &&
\r
759 castlingRights[0] >= 0 && /* [HGM] check rights */
\r
760 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
762 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
\r
763 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
\r
764 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
\r
766 callback(board, flags,
\r
767 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
\r
768 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2), closure);
\r
770 if ((flags & F_WHITE_ON_MOVE) &&
\r
771 (flags & F_WHITE_QCASTLE_OK) &&
\r
772 board[0][ff] == WhiteKing &&
\r
773 board[0][ff - 1] == EmptySquare &&
\r
774 board[0][ff - 2] == EmptySquare &&
\r
775 board[0][BOARD_LEFT+2] == EmptySquare &&
\r
776 board[0][BOARD_LEFT+1] == EmptySquare &&
\r
777 board[0][BOARD_LEFT+0] == WhiteRook &&
\r
778 castlingRights[1] >= 0 && /* [HGM] check rights */
\r
779 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
781 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
\r
782 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
\r
783 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
\r
785 callback(board, flags,
\r
786 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
\r
787 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
789 if (!(flags & F_WHITE_ON_MOVE) &&
\r
790 (flags & F_BLACK_KCASTLE_OK) &&
\r
791 board[BOARD_HEIGHT-1][ff] == BlackKing &&
\r
792 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
\r
793 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
\r
794 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
\r
795 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
\r
796 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
\r
797 castlingRights[3] >= 0 && /* [HGM] check rights */
\r
798 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
800 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
\r
801 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
\r
802 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
\r
804 callback(board, flags,
\r
805 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
\r
806 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2), closure);
\r
808 if (!(flags & F_WHITE_ON_MOVE) &&
\r
809 (flags & F_BLACK_QCASTLE_OK) &&
\r
810 board[BOARD_HEIGHT-1][ff] == BlackKing &&
\r
811 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
\r
812 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
\r
813 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
\r
814 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
\r
815 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
\r
816 castlingRights[4] >= 0 && /* [HGM] check rights */
\r
817 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
819 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
\r
820 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
\r
821 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE)))) {
\r
823 callback(board, flags,
\r
824 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
\r
825 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
831 /* generate all potential FRC castling moves (KxR), ignoring flags */
\r
832 /* [HGM] Tord! Help requested! */
\r
834 if ((flags & F_WHITE_ON_MOVE) != 0) {
\r
836 for (ff = BOARD_LEFT+1; ff < BOARD_RGHT-1; ff++) {
\r
837 if (board[0][ff] == WhiteKing) {
\r
838 for (ft = BOARD_LEFT+0; ft < BOARD_RGHT; ft++) {
\r
839 if (board[0][ft] == WhiteRook) {
\r
840 callback(board, flags,
\r
841 (ft > ff) ? WhiteHSideCastleFR : WhiteASideCastleFR,
\r
842 0, ff, 0, ft, closure);
\r
850 for (ff = BOARD_LEFT+1; ff < BOARD_RGHT-1; ff++) {
\r
851 if (board[BOARD_HEIGHT-1][ff] == BlackKing) {
\r
852 for (ft = BOARD_LEFT+0; ft < BOARD_RGHT; ft++) {
\r
853 if (board[BOARD_HEIGHT-1][ft] == BlackRook) {
\r
854 callback(board, flags,
\r
855 (ft > ff) ? BlackHSideCastleFR : BlackASideCastleFR,
\r
856 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
872 } CheckTestClosure;
\r
875 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
\r
876 int rf, int ff, int rt, int ft,
\r
877 VOIDSTAR closure));
\r
880 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
884 int rf, ff, rt, ft;
\r
887 register CheckTestClosure *cl = (CheckTestClosure *) closure;
\r
889 if (rt == cl->rking && ft == cl->fking) cl->check++;
\r
893 /* If the player on move were to move from (rf, ff) to (rt, ft), would
\r
894 he leave himself in check? Or if rf == -1, is the player on move
\r
895 in check now? enPassant must be TRUE if the indicated move is an
\r
896 e.p. capture. The possibility of castling out of a check along the
\r
897 back rank is not accounted for (i.e., we still return nonzero), as
\r
898 this is illegal anyway. Return value is the number of times the
\r
899 king is in check. */
\r
900 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
\r
903 int rf, ff, rt, ft, enPassant;
\r
905 CheckTestClosure cl;
\r
906 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
\r
907 ChessSquare captured = EmptySquare;
\r
908 /* Suppress warnings on uninitialized variables */
\r
910 if(gameInfo.variant == VariantXiangqi)
\r
911 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
\r
912 if(gameInfo.variant == VariantKnightmate)
\r
913 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
\r
917 captured = board[rf][ft];
\r
918 board[rf][ft] = EmptySquare;
\r
920 captured = board[rt][ft];
\r
922 board[rt][ft] = board[rf][ff];
\r
923 board[rf][ff] = EmptySquare;
\r
926 /* For compatibility with ICS wild 9, we scan the board in the
\r
927 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
\r
928 and we test only whether that one is in check. */
\r
930 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
\r
931 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
\r
932 if (board[cl.rking][cl.fking] == king) {
\r
933 if(gameInfo.variant == VariantXiangqi) {
\r
934 /* [HGM] In Xiangqi opposing Kings means check as well */
\r
936 dir = (king >= BlackPawn) ? -1 : 1;
\r
937 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
\r
938 board[i][cl.fking] == EmptySquare; i+=dir );
\r
939 if(i>=0 && i<BOARD_HEIGHT &&
\r
940 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
\r
944 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
\r
945 CheckTestCallback, (VOIDSTAR) &cl);
\r
946 goto undo_move; /* 2-level break */
\r
953 board[rf][ff] = board[rt][ft];
\r
955 board[rf][ft] = captured;
\r
956 board[rt][ft] = EmptySquare;
\r
958 board[rt][ft] = captured;
\r
966 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
\r
967 int rf, int ff, int rt, int ft,
\r
968 VOIDSTAR closure));
\r
970 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
974 int rf, ff, rt, ft;
\r
977 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
\r
979 if (appData.debugMode) {
\r
980 fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
982 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
\r
986 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
\r
989 int rf, ff, rt, ft, promoChar;
\r
990 char castlingRights[];
\r
992 LegalityTestClosure cl;
\r
998 cl.kind = IllegalMove;
\r
999 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
\r
1001 if(gameInfo.variant == VariantShogi) {
\r
1002 /* [HGM] Shogi promotions. '=' means defer */
\r
1003 if(rf != DROP_RANK && cl.kind == NormalMove) {
\r
1004 ChessSquare piece = board[rf][ff];
\r
1006 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
\r
1007 if(promoChar != NULLCHAR && promoChar != 'x' &&
\r
1008 promoChar != '+' && promoChar != '=' &&
\r
1009 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
\r
1010 cl.kind = IllegalMove;
\r
1011 else if(flags & F_WHITE_ON_MOVE) {
\r
1012 if( (int) piece < (int) WhiteWazir &&
\r
1013 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1014 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1015 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1016 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1017 else /* promotion optional, default is promote */
\r
1018 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
\r
1020 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1021 NormalMove : IllegalMove;
\r
1023 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1024 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1025 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1026 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1027 else /* promotion optional, default is promote */
\r
1028 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
\r
1030 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1031 NormalMove : IllegalMove;
\r
1035 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1036 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1038 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
\r
1040 cl.kind = IllegalMove;
\r
1043 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
\r
1049 } MateTestClosure;
\r
1051 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
\r
1052 int rf, int ff, int rt, int ft,
\r
1053 VOIDSTAR closure));
\r
1055 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1059 int rf, ff, rt, ft;
\r
1062 register MateTestClosure *cl = (MateTestClosure *) closure;
\r
1067 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
\r
1068 int MateTest(board, flags, epfile, castlingRights)
\r
1070 int flags, epfile;
\r
1071 char castlingRights[];
\r
1073 MateTestClosure cl;
\r
1077 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
\r
1078 if (cl.count > 0) {
\r
1079 return inCheck ? MT_CHECK : MT_NONE;
\r
1081 return inCheck || gameInfo.variant == VariantXiangqi ?
\r
1082 MT_CHECKMATE : MT_STALEMATE;
\r
1087 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
\r
1088 int rf, int ff, int rt, int ft,
\r
1089 VOIDSTAR closure));
\r
1091 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1095 int rf, ff, rt, ft;
\r
1098 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
\r
1100 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
\r
1101 || PieceToChar(board[rf][ff]) == '~'
\r
1102 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
\r
1104 (cl->rfIn == -1 || cl->rfIn == rf) &&
\r
1105 (cl->ffIn == -1 || cl->ffIn == ff) &&
\r
1106 (cl->rtIn == -1 || cl->rtIn == rt) &&
\r
1107 (cl->ftIn == -1 || cl->ftIn == ft)) {
\r
1110 cl->piece = board[rf][ff];
\r
1119 void Disambiguate(board, flags, epfile, closure)
\r
1121 int flags, epfile;
\r
1122 DisambiguateClosure *closure;
\r
1124 int illegal = 0; char c = closure->promoCharIn;
\r
1125 closure->count = 0;
\r
1126 closure->rf = closure->ff = closure->rt = closure->ft = 0;
\r
1127 closure->kind = ImpossibleMove;
\r
1128 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
\r
1129 if (closure->count == 0) {
\r
1130 /* See if it's an illegal move due to check */
\r
1132 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
\r
1133 (VOIDSTAR) closure);
\r
1134 if (closure->count == 0) {
\r
1135 /* No, it's not even that */
\r
1140 if (appData.debugMode) {
\r
1141 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1142 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1144 if(gameInfo.variant == VariantShogi) {
\r
1145 /* [HGM] Shogi promotions. '=' means defer */
\r
1146 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
\r
1147 ChessSquare piece = closure->piece;
\r
1149 if (appData.debugMode) {
\r
1150 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1151 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1153 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
\r
1154 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
\r
1155 closure->kind = IllegalMove;
\r
1156 else if(flags & F_WHITE_ON_MOVE) {
\r
1157 if (appData.debugMode) {
\r
1158 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1159 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1161 if( (int) piece < (int) WhiteWazir &&
\r
1162 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
\r
1163 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
\r
1164 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1165 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
\r
1166 else /* promotion optional, default is promote */
\r
1167 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
\r
1169 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1170 NormalMove : IllegalMove;
\r
1172 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
\r
1173 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
\r
1174 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
\r
1175 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
\r
1176 else /* promotion optional, default is promote */
\r
1177 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
\r
1179 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1180 NormalMove : IllegalMove;
\r
1184 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
\r
1185 if (closure->kind == WhitePromotionQueen
\r
1186 || closure->kind == BlackPromotionQueen) {
\r
1188 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
\r
1189 closure->promoCharIn);
\r
1191 closure->kind = IllegalMove;
\r
1194 if (appData.debugMode) {
\r
1195 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1196 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1198 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
\r
1199 if(closure->promoCharIn != '=')
\r
1200 closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));
\r
1201 else closure->promoChar = '=';
\r
1202 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
\r
1203 if (closure->count > 1) {
\r
1204 closure->kind = AmbiguousMove;
\r
1207 /* Note: If more than one illegal move matches, but no legal
\r
1208 moves, we return IllegalMove, not AmbiguousMove. Caller
\r
1209 can look at closure->count to detect this.
\r
1211 closure->kind = IllegalMove;
\r
1213 if(closure->kind == IllegalMove)
\r
1214 /* [HGM] might be a variant we don't understand, pass on promotion info */
\r
1215 closure->promoChar = closure->promoCharIn;
\r
1216 if (appData.debugMode) {
\r
1217 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1218 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,closure->promoChar);
\r
1225 ChessSquare piece;
\r
1226 int rf, ff, rt, ft;
\r
1232 } CoordsToAlgebraicClosure;
\r
1234 extern void CoordsToAlgebraicCallback P((Board board, int flags,
\r
1235 ChessMove kind, int rf, int ff,
\r
1236 int rt, int ft, VOIDSTAR closure));
\r
1238 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1242 int rf, ff, rt, ft;
\r
1245 register CoordsToAlgebraicClosure *cl =
\r
1246 (CoordsToAlgebraicClosure *) closure;
\r
1248 if (rt == cl->rt && ft == cl->ft &&
\r
1249 (board[rf][ff] == cl->piece
\r
1250 || PieceToChar(board[rf][ff]) == '~' &&
\r
1251 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
\r
1253 if (rf == cl->rf) {
\r
1254 if (ff == cl->ff) {
\r
1255 cl->kind = kind; /* this is the move we want */
\r
1257 cl->file++; /* need file to rule out this move */
\r
1260 if (ff == cl->ff) {
\r
1261 cl->rank++; /* need rank to rule out this move */
\r
1263 cl->either++; /* rank or file will rule out this move */
\r
1269 /* Convert coordinates to normal algebraic notation.
\r
1270 promoChar must be NULLCHAR or 'x' if not a promotion.
\r
1272 ChessMove CoordsToAlgebraic(board, flags, epfile,
\r
1273 rf, ff, rt, ft, promoChar, out)
\r
1275 int flags, epfile;
\r
1276 int rf, ff, rt, ft;
\r
1278 char out[MOVE_LEN];
\r
1280 ChessSquare piece;
\r
1282 char *outp = out, c;
\r
1283 CoordsToAlgebraicClosure cl;
\r
1285 if (rf == DROP_RANK) {
\r
1286 /* Bughouse piece drop */
\r
1287 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
\r
1289 *outp++ = ft + AAA;
\r
1291 *outp++ = rt + ONE;
\r
1292 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1294 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
\r
1297 if (promoChar == 'x') promoChar = NULLCHAR;
\r
1298 piece = board[rf][ff];
\r
1299 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
\r
1301 if (appData.debugMode)
\r
1302 fprintf(debugFP, "CoordsToAlgebraic, piece=%d\n", (int)piece);
\r
1306 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
\r
1307 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1308 /* Keep short notation if move is illegal only because it
\r
1309 leaves the player in check, but still return IllegalMove */
\r
1310 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1311 rf, ff, rt, ft, promoChar);
\r
1312 if (kind == IllegalMove) break;
\r
1313 kind = IllegalMove;
\r
1316 *outp++ = ff + AAA;
\r
1317 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
\r
1318 /* Non-capture; use style "e5" */
\r
1320 *outp++ = rt + ONE;
\r
1321 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1323 /* Capture; use style "exd5" */
\r
1324 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
\r
1325 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
\r
1326 *outp++ = ft + AAA;
\r
1328 *outp++ = rt + ONE;
\r
1329 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1331 /* Use promotion suffix style "=Q" */
\r
1333 if (promoChar != NULLCHAR) {
\r
1334 if(gameInfo.variant == VariantShogi) {
\r
1335 /* [HGM] ... but not in Shogi! */
\r
1336 *outp++ = promoChar == '=' ? '=' : '+';
\r
1339 *outp++ = ToUpper(promoChar);
\r
1348 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
\r
1349 /* Code added by Tord: FRC castling. */
\r
1350 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
\r
1351 (piece == BlackKing && board[rt][ft] == BlackRook)) {
\r
1352 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
\r
1353 return LegalityTest(board, flags, epfile, initialRights,
\r
1354 rf, ff, rt, ft, promoChar);
\r
1356 /* End of code added by Tord */
\r
1357 /* Test for castling or ICS wild castling */
\r
1358 /* Use style "O-O" (oh-oh) for PGN compatibility */
\r
1359 else if (rf == rt &&
\r
1360 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
\r
1361 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
\r
1362 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
\r
1363 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
\r
1364 strcpy(out, "O-O");
\r
1366 strcpy(out, "O-O-O");
\r
1368 /* This notation is always unambiguous, unless there are
\r
1369 kings on both the d and e files, with "wild castling"
\r
1370 possible for the king on the d file and normal castling
\r
1371 possible for the other. ICS rules for wild 9
\r
1372 effectively make castling illegal for either king in
\r
1373 this situation. So I am not going to worry about it;
\r
1374 I'll just generate an ambiguous O-O in this case.
\r
1376 return LegalityTest(board, flags, epfile, initialRights,
\r
1377 rf, ff, rt, ft, promoChar);
\r
1380 /* else fall through */
\r
1388 cl.kind = IllegalMove;
\r
1389 cl.rank = cl.file = cl.either = 0;
\r
1390 GenLegal(board, flags, epfile, initialRights,
\r
1391 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1393 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1394 /* Generate pretty moves for moving into check, but
\r
1395 still return IllegalMove.
\r
1397 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1398 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1399 if (cl.kind == IllegalMove) break;
\r
1400 cl.kind = IllegalMove;
\r
1403 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
\r
1404 else "Ngf3" or "Ngxf7",
\r
1405 else "N1f3" or "N5xf7",
\r
1406 else "Ng1f3" or "Ng5xf7".
\r
1408 c = PieceToChar(piece) ;
\r
1409 if( c == '~' || c == '+') {
\r
1410 /* [HGM] print nonexistent piece as its demoted version */
\r
1411 piece = (ChessSquare) (DEMOTED piece);
\r
1413 if(c=='+') *outp++ = c;
\r
1414 *outp++ = ToUpper(PieceToChar(piece));
\r
1416 if (cl.file || (cl.either && !cl.rank)) {
\r
1417 *outp++ = ff + AAA;
\r
1421 *outp++ = rf + ONE;
\r
1422 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1425 if(board[rt][ft] != EmptySquare)
\r
1428 *outp++ = ft + AAA;
\r
1430 *outp++ = rt + ONE;
\r
1431 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1433 if (gameInfo.variant == VariantShogi) {
\r
1434 /* [HGM] in Shogi non-pawns can promote */
\r
1435 if(flags & F_WHITE_ON_MOVE) {
\r
1436 if( (int) cl.piece < (int) WhiteWazir &&
\r
1437 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1438 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1439 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1440 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1441 else cl.kind = WhitePromotionQueen; /* promotion optional */
\r
1443 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1444 NormalMove : IllegalMove;
\r
1446 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1447 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1448 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1449 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1450 else cl.kind = BlackPromotionQueen; /* promotion optional */
\r
1451 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1452 NormalMove : IllegalMove;
\r
1454 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1455 /* for optional promotions append '+' or '=' */
\r
1456 if(promoChar == '=') {
\r
1458 cl.kind = NormalMove;
\r
1459 } else *outp++ = '+';
\r
1461 } else if(cl.kind == IllegalMove) {
\r
1462 /* Illegal move specifies any given promotion */
\r
1463 if(promoChar != NULLCHAR && promoChar != 'x') {
\r
1465 *outp++ = ToUpper(promoChar);
\r
1472 /* [HGM] Always long notation for fairies we don't know */
\r
1473 case WhiteNightrider:
\r
1474 case BlackNightrider:
\r
1475 case WhiteGrasshopper:
\r
1476 case BlackGrasshopper:
\r
1478 /* Moving a nonexistent piece */
\r
1482 /* Not a legal move, even ignoring check.
\r
1483 If there was a piece on the from square,
\r
1484 use style "Ng1g3" or "Ng1xe8";
\r
1485 if there was a pawn or nothing (!),
\r
1486 use style "g1g3" or "g1xe8". Use "x"
\r
1487 if a piece was on the to square, even
\r
1488 a piece of the same color.
\r
1491 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
\r
1492 *outp++ = ToUpper(PieceToChar(piece));
\r
1494 *outp++ = ff + AAA;
\r
1496 *outp++ = rf + ONE;
\r
1497 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1498 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
\r
1499 *outp++ = ft + AAA;
\r
1501 *outp++ = rt + ONE;
\r
1502 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1503 /* Use promotion suffix style "=Q" */
\r
1504 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1506 *outp++ = ToUpper(promoChar);
\r
1510 return IllegalMove;
\r