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(gameInfo.variant == VariantCrazyhouse &&
\r
285 ( (int) piece > (int) WhiteQueen && (int) piece < (int) WhiteKing
\r
286 || (int) piece > (int) BlackQueen && (int) piece < (int) BlackKing ))
\r
287 piece = (ChessSquare) ( DEMOTED piece );
\r
288 if(gameInfo.variant == VariantShogi)
\r
289 piece = (ChessSquare) ( SHOGI piece );
\r
292 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
\r
294 /* can't happen ([HGM] except for faries...) */
\r
298 if(gameInfo.variant == VariantXiangqi) {
\r
299 /* [HGM] capture and move straight ahead in Xiangqi */
\r
300 if (rf < BOARD_HEIGHT-1 &&
\r
301 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
\r
302 callback(board, flags, NormalMove,
\r
303 rf, ff, rf + 1, ff, closure);
\r
305 /* and move sideways when across the river */
\r
306 for (s = -1; s <= 1; s += 2) {
\r
307 if (rf >= BOARD_HEIGHT>>1 &&
\r
308 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
309 !WhitePiece(board[rf][ff+s]) ) {
\r
310 callback(board, flags, NormalMove,
\r
311 rf, ff, rf, ff+s, closure);
\r
316 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
\r
317 callback(board, flags,
\r
318 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
319 rf, ff, rf + 1, ff, closure);
\r
321 if (rf == 1 && board[2][ff] == EmptySquare &&
\r
322 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
323 gameInfo.variant != VariantCourier && /* [HGM] */
\r
324 board[3][ff] == EmptySquare ) {
\r
325 callback(board, flags, NormalMove,
\r
326 rf, ff, 3, ff, closure);
\r
328 for (s = -1; s <= 1; s += 2) {
\r
329 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
330 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
331 BlackPiece(board[rf + 1][ff + s]))) {
\r
332 callback(board, flags,
\r
333 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
334 rf, ff, rf + 1, ff + s, closure);
\r
336 if (rf == BOARD_HEIGHT-4) {
\r
337 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
338 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
339 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
\r
340 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
\r
341 callback(board, flags, WhiteCapturesEnPassant,
\r
342 rf, ff, 5, ff + s, closure);
\r
349 if(gameInfo.variant == VariantXiangqi) {
\r
350 /* [HGM] capture straight ahead in Xiangqi */
\r
351 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
\r
352 callback(board, flags, NormalMove,
\r
353 rf, ff, rf - 1, ff, closure);
\r
355 /* and move sideways when across the river */
\r
356 for (s = -1; s <= 1; s += 2) {
\r
357 if (rf < BOARD_HEIGHT>>1 &&
\r
358 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
359 !BlackPiece(board[rf][ff+s]) ) {
\r
360 callback(board, flags, NormalMove,
\r
361 rf, ff, rf, ff+s, closure);
\r
366 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
\r
367 callback(board, flags,
\r
368 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
369 rf, ff, rf - 1, ff, closure);
\r
371 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
\r
372 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
373 gameInfo.variant != VariantCourier && /* [HGM] */
\r
374 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
\r
375 callback(board, flags, NormalMove,
\r
376 rf, ff, BOARD_HEIGHT-4, ff, closure);
\r
378 for (s = -1; s <= 1; s += 2) {
\r
379 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
380 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
381 WhitePiece(board[rf - 1][ff + s]))) {
\r
382 callback(board, flags,
\r
383 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
384 rf, ff, rf - 1, ff + s, closure);
\r
387 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
388 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
389 board[3][ff + s] == WhitePawn &&
\r
390 board[2][ff + s] == EmptySquare) {
\r
391 callback(board, flags, BlackCapturesEnPassant,
\r
392 rf, ff, 2, ff + s, closure);
\r
403 for (i = -1; i <= 1; i += 2)
\r
404 for (j = -1; j <= 1; j += 2)
\r
405 for (s = 1; s <= 2; s++) {
\r
408 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
409 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
\r
410 && !SameColor(board[rf][ff], board[rt][ft]))
\r
411 callback(board, flags, NormalMove,
\r
412 rf, ff, rt, ft, closure);
\r
416 case SHOGI WhiteKnight:
\r
417 for (s = -1; s <= 1; s += 2) {
\r
418 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
419 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
\r
420 callback(board, flags, NormalMove,
\r
421 rf, ff, rf + 2, ff + s, closure);
\r
426 case SHOGI BlackKnight:
\r
427 for (s = -1; s <= 1; s += 2) {
\r
428 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
429 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
\r
430 callback(board, flags, NormalMove,
\r
431 rf, ff, rf - 2, ff + s, closure);
\r
438 for (d = 0; d <= 1; d++)
\r
439 for (s = -1; s <= 1; s += 2) {
\r
441 for (i = 1;; i++) {
\r
442 rt = rf + (i * s) * d;
\r
443 ft = ff + (i * s) * (1 - d);
\r
444 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
445 if (m == 0 && board[rt][ft] == EmptySquare)
\r
446 callback(board, flags, NormalMove,
\r
447 rf, ff, rt, ft, closure);
\r
448 if (m == 1 && board[rt][ft] != EmptySquare &&
\r
449 !SameColor(board[rf][ff], board[rt][ft]) )
\r
450 callback(board, flags, NormalMove,
\r
451 rf, ff, rt, ft, closure);
\r
452 if (board[rt][ft] != EmptySquare && m++) break;
\r
457 /* Gold General (and all its promoted versions) . First do the */
\r
458 /* diagonal forward steps, then proceed as normal Wazir */
\r
459 case SHOGI WhiteWazir:
\r
460 case SHOGI (PROMOTED WhitePawn):
\r
461 case SHOGI (PROMOTED WhiteKnight):
\r
462 case SHOGI (PROMOTED WhiteQueen):
\r
463 case SHOGI (PROMOTED WhiteFerz):
\r
464 for (s = -1; s <= 1; s += 2) {
\r
465 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
466 !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
\r
467 callback(board, flags, NormalMove,
\r
468 rf, ff, rf + 1, ff + s, closure);
\r
473 case SHOGI BlackWazir:
\r
474 case SHOGI (PROMOTED BlackPawn):
\r
475 case SHOGI (PROMOTED BlackKnight):
\r
476 case SHOGI (PROMOTED BlackQueen):
\r
477 case SHOGI (PROMOTED BlackFerz):
\r
478 for (s = -1; s <= 1; s += 2) {
\r
479 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
480 !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
\r
481 callback(board, flags, NormalMove,
\r
482 rf, ff, rf - 1, ff + s, closure);
\r
489 for (d = 0; d <= 1; d++)
\r
490 for (s = -1; s <= 1; s += 2) {
\r
492 ft = ff + s * (1 - d);
\r
493 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
494 && !SameColor(board[rf][ff], board[rt][ft]) &&
\r
495 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
496 callback(board, flags, NormalMove,
\r
497 rf, ff, rt, ft, closure);
\r
503 /* [HGM] support Shatranj pieces */
\r
504 for (rs = -1; rs <= 1; rs += 2)
\r
505 for (fs = -1; fs <= 1; fs += 2) {
\r
508 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
509 && ( gameInfo.variant != VariantXiangqi ||
\r
510 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
\r
512 && !SameColor(board[rf][ff], board[rt][ft]))
\r
513 callback(board, flags, NormalMove,
\r
514 rf, ff, rt, ft, closure);
\r
518 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
\r
519 case SHOGI WhiteCardinal:
\r
520 case SHOGI BlackCardinal:
\r
523 /* Capablanca Archbishop continues as Knight */
\r
524 case WhiteCardinal:
\r
525 case BlackCardinal:
\r
528 /* Shogi Bishops are ordinary Bishops */
\r
529 case SHOGI WhiteBishop:
\r
530 case SHOGI BlackBishop:
\r
533 for (rs = -1; rs <= 1; rs += 2)
\r
534 for (fs = -1; fs <= 1; fs += 2)
\r
535 for (i = 1;; i++) {
\r
536 rt = rf + (i * rs);
\r
537 ft = ff + (i * fs);
\r
538 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
539 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
540 callback(board, flags, NormalMove,
\r
541 rf, ff, rt, ft, closure);
\r
542 if (board[rt][ft] != EmptySquare) break;
\r
544 if(m==1) goto mounted;
\r
545 if(m==2) goto finishGold;
\r
546 /* Bishop falls through */
\r
549 /* Shogi Lance is unlike anything, and asymmetric at that */
\r
550 case SHOGI WhiteQueen:
\r
554 if (rt >= BOARD_HEIGHT) break;
\r
555 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
556 callback(board, flags, NormalMove,
\r
557 rf, ff, rt, ft, closure);
\r
558 if (board[rt][ft] != EmptySquare) break;
\r
562 case SHOGI BlackQueen:
\r
567 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
568 callback(board, flags, NormalMove,
\r
569 rf, ff, rt, ft, closure);
\r
570 if (board[rt][ft] != EmptySquare) break;
\r
574 /* Shogi Dragon King has to continue as Ferz after Rook moves */
\r
575 case SHOGI WhiteMarshall:
\r
576 case SHOGI BlackMarshall:
\r
579 /* Capablanca Chancellor sets flag to continue as Knight */
\r
580 case WhiteMarshall:
\r
581 case BlackMarshall:
\r
584 /* Shogi Rooks are ordinary Rooks */
\r
585 case SHOGI WhiteRook:
\r
586 case SHOGI BlackRook:
\r
589 for (d = 0; d <= 1; d++)
\r
590 for (s = -1; s <= 1; s += 2)
\r
591 for (i = 1;; i++) {
\r
592 rt = rf + (i * s) * d;
\r
593 ft = ff + (i * s) * (1 - d);
\r
594 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
595 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
596 callback(board, flags, NormalMove,
\r
597 rf, ff, rt, ft, closure);
\r
598 if (board[rt][ft] != EmptySquare) break;
\r
600 if(m==1) goto mounted;
\r
601 if(m==2) goto walking;
\r
606 for (rs = -1; rs <= 1; rs++)
\r
607 for (fs = -1; fs <= 1; fs++) {
\r
608 if (rs == 0 && fs == 0) continue;
\r
609 for (i = 1;; i++) {
\r
610 rt = rf + (i * rs);
\r
611 ft = ff + (i * fs);
\r
612 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
613 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
614 callback(board, flags, NormalMove,
\r
615 rf, ff, rt, ft, closure);
\r
616 if (board[rt][ft] != EmptySquare) break;
\r
621 /* Shogi Pawn and Silver General: first the Pawn move, */
\r
622 /* then the General continues like a Ferz */
\r
623 case SHOGI WhitePawn:
\r
624 case SHOGI WhiteFerz:
\r
625 if (rf < BOARD_HEIGHT-1 &&
\r
626 !SameColor(board[rf][ff], board[rf + 1][ff]) )
\r
627 callback(board, flags, NormalMove,
\r
628 rf, ff, rf + 1, ff, closure);
\r
629 if(piece != SHOGI WhitePawn) goto finishSilver;
\r
632 case SHOGI BlackPawn:
\r
633 case SHOGI BlackFerz:
\r
635 !SameColor(board[rf][ff], board[rf - 1][ff]) )
\r
636 callback(board, flags, NormalMove,
\r
637 rf, ff, rf - 1, ff, closure);
\r
638 if(piece == SHOGI BlackPawn) break;
\r
643 /* [HGM] support Shatranj pieces */
\r
644 for (rs = -1; rs <= 1; rs += 2)
\r
645 for (fs = -1; fs <= 1; fs += 2) {
\r
648 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
649 if (!SameColor(board[rf][ff], board[rt][ft]) &&
\r
650 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
651 callback(board, flags, NormalMove,
\r
652 rf, ff, rt, ft, closure);
\r
658 case SHOGI WhiteKing:
\r
659 case SHOGI BlackKing:
\r
663 for (i = -1; i <= 1; i++)
\r
664 for (j = -1; j <= 1; j++) {
\r
665 if (i == 0 && j == 0) continue;
\r
668 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
669 if (SameColor(board[rf][ff], board[rt][ft])) continue;
\r
670 callback(board, flags, NormalMove,
\r
671 rf, ff, rt, ft, closure);
\r
684 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
\r
685 int rf, int ff, int rt, int ft,
\r
686 VOIDSTAR closure));
\r
688 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
692 int rf, ff, rt, ft;
\r
695 register GenLegalClosure *cl = (GenLegalClosure *) closure;
\r
697 if (!(flags & F_IGNORE_CHECK) &&
\r
698 CheckTest(board, flags, rf, ff, rt, ft,
\r
699 kind == WhiteCapturesEnPassant ||
\r
700 kind == BlackCapturesEnPassant)) return;
\r
701 if (flags & F_ATOMIC_CAPTURE) {
\r
702 if (board[rt][ft] != EmptySquare ||
\r
703 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
\r
705 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
\r
706 if (board[rf][ff] == king) return;
\r
707 for (r = rt-1; r <= rt+1; r++) {
\r
708 for (f = ft-1; f <= ft+1; f++) {
\r
709 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
\r
710 board[r][f] == king) return;
\r
715 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
\r
720 int rf, ff, rt, ft;
\r
722 } LegalityTestClosure;
\r
725 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
\r
726 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
\r
727 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
\r
728 moves that would destroy your own king. The CASTLE_OK flags are
\r
729 true if castling is not yet ruled out by a move of the king or
\r
730 rook. Return TRUE if the player on move is currently in check and
\r
731 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
\r
732 int GenLegal(board, flags, epfile, castlingRights, callback, closure)
\r
736 char castlingRights[];
\r
737 MoveCallback callback;
\r
740 GenLegalClosure cl;
\r
742 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
\r
746 GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
\r
748 if (!ignoreCheck &&
\r
749 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
\r
751 /* Generate castling moves */
\r
752 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
\r
753 if ((flags & F_WHITE_ON_MOVE) &&
\r
754 (flags & F_WHITE_KCASTLE_OK) &&
\r
755 board[0][ff] == WhiteKing &&
\r
756 board[0][ff + 1] == EmptySquare &&
\r
757 board[0][ff + 2] == EmptySquare &&
\r
758 board[0][BOARD_RGHT-3] == EmptySquare &&
\r
759 board[0][BOARD_RGHT-2] == EmptySquare &&
\r
760 board[0][BOARD_RGHT-1] == WhiteRook &&
\r
761 castlingRights[0] >= 0 && /* [HGM] check rights */
\r
762 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
764 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
\r
765 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
\r
766 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
\r
768 callback(board, flags,
\r
769 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
\r
770 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2), closure);
\r
772 if ((flags & F_WHITE_ON_MOVE) &&
\r
773 (flags & F_WHITE_QCASTLE_OK) &&
\r
774 board[0][ff] == WhiteKing &&
\r
775 board[0][ff - 1] == EmptySquare &&
\r
776 board[0][ff - 2] == EmptySquare &&
\r
777 board[0][BOARD_LEFT+2] == EmptySquare &&
\r
778 board[0][BOARD_LEFT+1] == EmptySquare &&
\r
779 board[0][BOARD_LEFT+0] == WhiteRook &&
\r
780 castlingRights[1] >= 0 && /* [HGM] check rights */
\r
781 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
783 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
\r
784 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
\r
785 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
\r
787 callback(board, flags,
\r
788 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
\r
789 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
791 if (!(flags & F_WHITE_ON_MOVE) &&
\r
792 (flags & F_BLACK_KCASTLE_OK) &&
\r
793 board[BOARD_HEIGHT-1][ff] == BlackKing &&
\r
794 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
\r
795 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
\r
796 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
\r
797 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
\r
798 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
\r
799 castlingRights[3] >= 0 && /* [HGM] check rights */
\r
800 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
802 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
\r
803 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
\r
804 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
\r
806 callback(board, flags,
\r
807 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
\r
808 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2), closure);
\r
810 if (!(flags & F_WHITE_ON_MOVE) &&
\r
811 (flags & F_BLACK_QCASTLE_OK) &&
\r
812 board[BOARD_HEIGHT-1][ff] == BlackKing &&
\r
813 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
\r
814 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
\r
815 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
\r
816 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
\r
817 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
\r
818 castlingRights[4] >= 0 && /* [HGM] check rights */
\r
819 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
821 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
\r
822 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
\r
823 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE)))) {
\r
825 callback(board, flags,
\r
826 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
\r
827 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
833 /* generate all potential FRC castling moves (KxR), ignoring flags */
\r
834 /* [HGM] Tord! Help requested! */
\r
836 if ((flags & F_WHITE_ON_MOVE) != 0) {
\r
838 for (ff = BOARD_LEFT+1; ff < BOARD_RGHT-1; ff++) {
\r
839 if (board[0][ff] == WhiteKing) {
\r
840 for (ft = BOARD_LEFT+0; ft < BOARD_RGHT; ft++) {
\r
841 if (board[0][ft] == WhiteRook) {
\r
842 callback(board, flags,
\r
843 (ft > ff) ? WhiteHSideCastleFR : WhiteASideCastleFR,
\r
844 0, ff, 0, ft, closure);
\r
852 for (ff = BOARD_LEFT+1; ff < BOARD_RGHT-1; ff++) {
\r
853 if (board[BOARD_HEIGHT-1][ff] == BlackKing) {
\r
854 for (ft = BOARD_LEFT+0; ft < BOARD_RGHT; ft++) {
\r
855 if (board[BOARD_HEIGHT-1][ft] == BlackRook) {
\r
856 callback(board, flags,
\r
857 (ft > ff) ? BlackHSideCastleFR : BlackASideCastleFR,
\r
858 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
874 } CheckTestClosure;
\r
877 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
\r
878 int rf, int ff, int rt, int ft,
\r
879 VOIDSTAR closure));
\r
882 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
886 int rf, ff, rt, ft;
\r
889 register CheckTestClosure *cl = (CheckTestClosure *) closure;
\r
891 if (rt == cl->rking && ft == cl->fking) cl->check++;
\r
895 /* If the player on move were to move from (rf, ff) to (rt, ft), would
\r
896 he leave himself in check? Or if rf == -1, is the player on move
\r
897 in check now? enPassant must be TRUE if the indicated move is an
\r
898 e.p. capture. The possibility of castling out of a check along the
\r
899 back rank is not accounted for (i.e., we still return nonzero), as
\r
900 this is illegal anyway. Return value is the number of times the
\r
901 king is in check. */
\r
902 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
\r
905 int rf, ff, rt, ft, enPassant;
\r
907 CheckTestClosure cl;
\r
908 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
\r
909 ChessSquare captured = EmptySquare;
\r
910 /* Suppress warnings on uninitialized variables */
\r
912 if(gameInfo.variant == VariantXiangqi)
\r
913 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
\r
914 if(gameInfo.variant == VariantKnightmate)
\r
915 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
\r
919 captured = board[rf][ft];
\r
920 board[rf][ft] = EmptySquare;
\r
922 captured = board[rt][ft];
\r
924 board[rt][ft] = board[rf][ff];
\r
925 board[rf][ff] = EmptySquare;
\r
928 /* For compatibility with ICS wild 9, we scan the board in the
\r
929 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
\r
930 and we test only whether that one is in check. */
\r
932 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
\r
933 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
\r
934 if (board[cl.rking][cl.fking] == king) {
\r
935 if(gameInfo.variant == VariantXiangqi) {
\r
936 /* [HGM] In Xiangqi opposing Kings means check as well */
\r
938 dir = (king >= BlackPawn) ? -1 : 1;
\r
939 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
\r
940 board[i][cl.fking] == EmptySquare; i+=dir );
\r
941 if(i>=0 && i<BOARD_HEIGHT &&
\r
942 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
\r
946 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
\r
947 CheckTestCallback, (VOIDSTAR) &cl);
\r
948 goto undo_move; /* 2-level break */
\r
955 board[rf][ff] = board[rt][ft];
\r
957 board[rf][ft] = captured;
\r
958 board[rt][ft] = EmptySquare;
\r
960 board[rt][ft] = captured;
\r
968 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
\r
969 int rf, int ff, int rt, int ft,
\r
970 VOIDSTAR closure));
\r
972 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
976 int rf, ff, rt, ft;
\r
979 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
\r
981 if (appData.debugMode) {
\r
982 fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
984 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
\r
988 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
\r
991 int rf, ff, rt, ft, promoChar;
\r
992 char castlingRights[];
\r
994 LegalityTestClosure cl;
\r
1000 cl.kind = IllegalMove;
\r
1001 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
\r
1003 if(gameInfo.variant == VariantShogi) {
\r
1004 /* [HGM] Shogi promotions. '=' means defer */
\r
1005 if(rf != DROP_RANK && cl.kind == NormalMove) {
\r
1006 ChessSquare piece = board[rf][ff];
\r
1008 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
\r
1009 if(promoChar != NULLCHAR && promoChar != 'x' &&
\r
1010 promoChar != '+' && promoChar != '=' &&
\r
1011 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
\r
1012 cl.kind = IllegalMove;
\r
1013 else if(flags & F_WHITE_ON_MOVE) {
\r
1014 if( (int) piece < (int) WhiteWazir &&
\r
1015 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1016 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1017 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1018 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1019 else /* promotion optional, default is promote */
\r
1020 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
\r
1022 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1023 NormalMove : IllegalMove;
\r
1025 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1026 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1027 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1028 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1029 else /* promotion optional, default is promote */
\r
1030 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
\r
1032 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1033 NormalMove : IllegalMove;
\r
1037 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1038 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1040 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
\r
1042 cl.kind = IllegalMove;
\r
1045 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
\r
1051 } MateTestClosure;
\r
1053 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
\r
1054 int rf, int ff, int rt, int ft,
\r
1055 VOIDSTAR closure));
\r
1057 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1061 int rf, ff, rt, ft;
\r
1064 register MateTestClosure *cl = (MateTestClosure *) closure;
\r
1069 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
\r
1070 int MateTest(board, flags, epfile, castlingRights)
\r
1072 int flags, epfile;
\r
1073 char castlingRights[];
\r
1075 MateTestClosure cl;
\r
1079 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
\r
1080 if (cl.count > 0) {
\r
1081 return inCheck ? MT_CHECK : MT_NONE;
\r
1083 return inCheck || gameInfo.variant == VariantXiangqi ?
\r
1084 MT_CHECKMATE : MT_STALEMATE;
\r
1089 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
\r
1090 int rf, int ff, int rt, int ft,
\r
1091 VOIDSTAR closure));
\r
1093 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1097 int rf, ff, rt, ft;
\r
1100 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
\r
1102 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]) &&
\r
1103 (cl->rfIn == -1 || cl->rfIn == rf) &&
\r
1104 (cl->ffIn == -1 || cl->ffIn == ff) &&
\r
1105 (cl->rtIn == -1 || cl->rtIn == rt) &&
\r
1106 (cl->ftIn == -1 || cl->ftIn == ft)) {
\r
1109 cl->piece = board[rf][ff];
\r
1118 void Disambiguate(board, flags, epfile, closure)
\r
1120 int flags, epfile;
\r
1121 DisambiguateClosure *closure;
\r
1123 int illegal = 0; char c = closure->promoCharIn;
\r
1124 closure->count = 0;
\r
1125 closure->rf = closure->ff = closure->rt = closure->ft = 0;
\r
1126 closure->kind = ImpossibleMove;
\r
1127 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
\r
1128 if (closure->count == 0) {
\r
1129 /* See if it's an illegal move due to check */
\r
1131 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
\r
1132 (VOIDSTAR) closure);
\r
1133 if (closure->count == 0) {
\r
1134 /* No, it's not even that */
\r
1139 if (appData.debugMode) {
\r
1140 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1141 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1143 if(gameInfo.variant == VariantShogi) {
\r
1144 /* [HGM] Shogi promotions. '=' means defer */
\r
1145 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
\r
1146 ChessSquare piece = closure->piece;
\r
1148 if (appData.debugMode) {
\r
1149 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1150 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1152 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
\r
1153 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
\r
1154 closure->kind = IllegalMove;
\r
1155 else if(flags & F_WHITE_ON_MOVE) {
\r
1156 if (appData.debugMode) {
\r
1157 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1158 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1160 if( (int) piece < (int) WhiteWazir &&
\r
1161 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
\r
1162 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
\r
1163 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1164 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
\r
1165 else /* promotion optional, default is promote */
\r
1166 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
\r
1168 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1169 NormalMove : IllegalMove;
\r
1171 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
\r
1172 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
\r
1173 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
\r
1174 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
\r
1175 else /* promotion optional, default is promote */
\r
1176 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
\r
1178 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1179 NormalMove : IllegalMove;
\r
1183 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
\r
1184 if (closure->kind == WhitePromotionQueen
\r
1185 || closure->kind == BlackPromotionQueen) {
\r
1187 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
\r
1188 closure->promoCharIn);
\r
1190 closure->kind = IllegalMove;
\r
1193 if (appData.debugMode) {
\r
1194 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1195 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1197 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
\r
1198 if(closure->promoCharIn != '=')
\r
1199 closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));
\r
1200 else closure->promoChar = '=';
\r
1201 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
\r
1202 if (closure->count > 1) {
\r
1203 closure->kind = AmbiguousMove;
\r
1206 /* Note: If more than one illegal move matches, but no legal
\r
1207 moves, we return IllegalMove, not AmbiguousMove. Caller
\r
1208 can look at closure->count to detect this.
\r
1210 closure->kind = IllegalMove;
\r
1212 if(closure->kind == IllegalMove)
\r
1213 /* [HGM] might be a variant we don't understand, pass on promotion info */
\r
1214 closure->promoChar = closure->promoCharIn;
\r
1215 if (appData.debugMode) {
\r
1216 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1217 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,closure->promoChar);
\r
1224 ChessSquare piece;
\r
1225 int rf, ff, rt, ft;
\r
1231 } CoordsToAlgebraicClosure;
\r
1233 extern void CoordsToAlgebraicCallback P((Board board, int flags,
\r
1234 ChessMove kind, int rf, int ff,
\r
1235 int rt, int ft, VOIDSTAR closure));
\r
1237 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1241 int rf, ff, rt, ft;
\r
1244 register CoordsToAlgebraicClosure *cl =
\r
1245 (CoordsToAlgebraicClosure *) closure;
\r
1247 if (rt == cl->rt && ft == cl->ft &&
\r
1248 board[rf][ff] == cl->piece) {
\r
1249 if (rf == cl->rf) {
\r
1250 if (ff == cl->ff) {
\r
1251 cl->kind = kind; /* this is the move we want */
\r
1253 cl->file++; /* need file to rule out this move */
\r
1256 if (ff == cl->ff) {
\r
1257 cl->rank++; /* need rank to rule out this move */
\r
1259 cl->either++; /* rank or file will rule out this move */
\r
1265 /* Convert coordinates to normal algebraic notation.
\r
1266 promoChar must be NULLCHAR or 'x' if not a promotion.
\r
1268 ChessMove CoordsToAlgebraic(board, flags, epfile,
\r
1269 rf, ff, rt, ft, promoChar, out)
\r
1271 int flags, epfile;
\r
1272 int rf, ff, rt, ft;
\r
1274 char out[MOVE_LEN];
\r
1276 ChessSquare piece;
\r
1279 CoordsToAlgebraicClosure cl;
\r
1281 if (rf == DROP_RANK) {
\r
1282 /* Bughouse piece drop */
\r
1283 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
\r
1285 *outp++ = ft + AAA;
\r
1287 *outp++ = rt + ONE;
\r
1288 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1290 AlphaRank(out, 5);
\r
1291 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
\r
1294 if (promoChar == 'x') promoChar = NULLCHAR;
\r
1295 piece = board[rf][ff];
\r
1297 if (appData.debugMode)
\r
1298 fprintf(debugFP, "CoordsToAlgebraic, piece=%d\n", (int)piece);
\r
1302 if (appData.debugMode)
\r
1303 fprintf(debugFP, "CoordsToAlgebraic, Pawn\n");
\r
1304 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
\r
1305 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1306 /* Keep short notation if move is illegal only because it
\r
1307 leaves the player in check, but still return IllegalMove */
\r
1308 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1309 rf, ff, rt, ft, promoChar);
\r
1310 if (kind == IllegalMove) break;
\r
1311 kind = IllegalMove;
\r
1314 *outp++ = ff + AAA;
\r
1315 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
\r
1316 /* Non-capture; use style "e5" */
\r
1318 *outp++ = rt + ONE;
\r
1319 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1321 /* Capture; use style "exd5" */
\r
1322 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
\r
1323 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
\r
1324 *outp++ = ft + AAA;
\r
1326 *outp++ = rt + ONE;
\r
1327 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1329 /* Use promotion suffix style "=Q" */
\r
1331 if (promoChar != NULLCHAR) {
\r
1332 if(gameInfo.variant == VariantShogi) {
\r
1333 /* [HGM] ... but not in Shogi! */
\r
1334 *outp++ = promoChar == '=' ? '=' : '+';
\r
1337 *outp++ = ToUpper(promoChar);
\r
1341 AlphaRank(out, 10);
\r
1347 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
\r
1348 /* Code added by Tord: FRC castling. */
\r
1349 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
\r
1350 (piece == BlackKing && board[rt][ft] == BlackRook)) {
\r
1351 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
\r
1352 return LegalityTest(board, flags, epfile, initialRights,
\r
1353 rf, ff, rt, ft, promoChar);
\r
1355 /* End of code added by Tord */
\r
1356 /* Test for castling or ICS wild castling */
\r
1357 /* Use style "O-O" (oh-oh) for PGN compatibility */
\r
1358 else if (rf == rt &&
\r
1359 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
\r
1360 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
\r
1361 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
\r
1362 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
\r
1363 strcpy(out, "O-O");
\r
1365 strcpy(out, "O-O-O");
\r
1367 /* This notation is always unambiguous, unless there are
\r
1368 kings on both the d and e files, with "wild castling"
\r
1369 possible for the king on the d file and normal castling
\r
1370 possible for the other. ICS rules for wild 9
\r
1371 effectively make castling illegal for either king in
\r
1372 this situation. So I am not going to worry about it;
\r
1373 I'll just generate an ambiguous O-O in this case.
\r
1375 return LegalityTest(board, flags, epfile, initialRights,
\r
1376 rf, ff, rt, ft, promoChar);
\r
1379 /* else fall through */
\r
1387 cl.kind = IllegalMove;
\r
1388 cl.rank = cl.file = cl.either = 0;
\r
1389 GenLegal(board, flags, epfile, initialRights,
\r
1390 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1392 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1393 /* Generate pretty moves for moving into check, but
\r
1394 still return IllegalMove.
\r
1396 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1397 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1398 if (cl.kind == IllegalMove) break;
\r
1399 cl.kind = IllegalMove;
\r
1402 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
\r
1403 else "Ngf3" or "Ngxf7",
\r
1404 else "N1f3" or "N5xf7",
\r
1405 else "Ng1f3" or "Ng5xf7".
\r
1407 if(PieceToChar(piece) == '.') {
\r
1408 /* [HGM] print nonexistent piece as its demoted version */
\r
1409 piece = (ChessSquare) (DEMOTED piece);
\r
1410 if( gameInfo.variant == VariantShogi )
\r
1413 *outp++ = ToUpper(PieceToChar(piece));
\r
1414 if (cl.file || (cl.either && !cl.rank)) {
\r
1415 *outp++ = ff + AAA;
\r
1419 *outp++ = rf + ONE;
\r
1420 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1423 if(board[rt][ft] != EmptySquare)
\r
1426 *outp++ = ft + AAA;
\r
1428 *outp++ = rt + ONE;
\r
1429 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1431 if (gameInfo.variant == VariantShogi) {
\r
1432 /* [HGM] in Shogi non-pawns can promote */
\r
1433 if(flags & F_WHITE_ON_MOVE) {
\r
1434 if( (int) cl.piece < (int) WhiteWazir &&
\r
1435 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1436 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1437 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1438 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1439 else cl.kind = WhitePromotionQueen; /* promotion optional */
\r
1441 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1442 NormalMove : IllegalMove;
\r
1444 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1445 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1446 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1447 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1448 else cl.kind = BlackPromotionQueen; /* promotion optional */
\r
1449 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1450 NormalMove : IllegalMove;
\r
1452 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1453 /* for optional promotions append '+' or '=' */
\r
1454 if(promoChar == '=') {
\r
1456 cl.kind = NormalMove;
\r
1457 } else *outp++ = '+';
\r
1459 } else if(cl.kind == IllegalMove) {
\r
1460 /* Illegal move specifies any given promotion */
\r
1461 if(promoChar != NULLCHAR && promoChar != 'x') {
\r
1463 *outp++ = ToUpper(promoChar);
\r
1468 AlphaRank(out, 10);
\r
1471 /* [HGM] Always long notation for fairies we don't know */
\r
1472 case WhiteNightrider:
\r
1473 case BlackNightrider:
\r
1474 case WhiteGrasshopper:
\r
1475 case BlackGrasshopper:
\r
1477 /* Moving a nonexistent piece */
\r
1481 /* Not a legal move, even ignoring check.
\r
1482 If there was a piece on the from square,
\r
1483 use style "Ng1g3" or "Ng1xe8";
\r
1484 if there was a pawn or nothing (!),
\r
1485 use style "g1g3" or "g1xe8". Use "x"
\r
1486 if a piece was on the to square, even
\r
1487 a piece of the same color.
\r
1490 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
\r
1491 *outp++ = ToUpper(PieceToChar(piece));
\r
1493 *outp++ = ff + AAA;
\r
1495 *outp++ = rf + ONE;
\r
1496 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1497 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
\r
1498 *outp++ = ft + AAA;
\r
1500 *outp++ = rt + ONE;
\r
1501 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1502 /* Use promotion suffix style "=Q" */
\r
1503 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1505 *outp++ = ToUpper(promoChar);
\r
1509 AlphaRank(out, 0);
\r
1510 return IllegalMove;
\r