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 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
\r
936 CheckTestCallback, (VOIDSTAR) &cl);
\r
937 goto undo_move; /* 2-level break */
\r
944 board[rf][ff] = board[rt][ft];
\r
946 board[rf][ft] = captured;
\r
947 board[rt][ft] = EmptySquare;
\r
949 board[rt][ft] = captured;
\r
957 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
\r
958 int rf, int ff, int rt, int ft,
\r
959 VOIDSTAR closure));
\r
961 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
965 int rf, ff, rt, ft;
\r
968 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
\r
970 if (appData.debugMode) {
\r
971 fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
973 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
\r
977 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
\r
980 int rf, ff, rt, ft, promoChar;
\r
981 char castlingRights[];
\r
983 LegalityTestClosure cl;
\r
989 cl.kind = IllegalMove;
\r
990 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
\r
992 if(gameInfo.variant == VariantShogi) {
\r
993 /* [HGM] Shogi promotions. '=' means defer */
\r
994 if(rf != DROP_RANK && cl.kind == NormalMove) {
\r
995 ChessSquare piece = board[rf][ff];
\r
997 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
\r
998 if(promoChar != NULLCHAR && promoChar != 'x' &&
\r
999 promoChar != '+' && promoChar != '=' &&
\r
1000 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
\r
1001 cl.kind = IllegalMove;
\r
1002 else if(flags & F_WHITE_ON_MOVE) {
\r
1003 if( (int) piece < (int) WhiteWazir &&
\r
1004 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1005 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1006 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1007 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1008 else /* promotion optional, default is promote */
\r
1009 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
\r
1011 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1012 NormalMove : IllegalMove;
\r
1014 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1015 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1016 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1017 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1018 else /* promotion optional, default is promote */
\r
1019 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
\r
1021 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1022 NormalMove : IllegalMove;
\r
1026 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1027 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1029 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
\r
1031 cl.kind = IllegalMove;
\r
1034 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
\r
1040 } MateTestClosure;
\r
1042 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
\r
1043 int rf, int ff, int rt, int ft,
\r
1044 VOIDSTAR closure));
\r
1046 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1050 int rf, ff, rt, ft;
\r
1053 register MateTestClosure *cl = (MateTestClosure *) closure;
\r
1058 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
\r
1059 int MateTest(board, flags, epfile, castlingRights)
\r
1061 int flags, epfile;
\r
1062 char castlingRights[];
\r
1064 MateTestClosure cl;
\r
1068 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
\r
1069 if (cl.count > 0) {
\r
1070 return inCheck ? MT_CHECK : MT_NONE;
\r
1072 return inCheck || gameInfo.variant == VariantXiangqi ?
\r
1073 MT_CHECKMATE : MT_STALEMATE;
\r
1078 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
\r
1079 int rf, int ff, int rt, int ft,
\r
1080 VOIDSTAR closure));
\r
1082 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1086 int rf, ff, rt, ft;
\r
1089 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
\r
1091 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]) &&
\r
1092 (cl->rfIn == -1 || cl->rfIn == rf) &&
\r
1093 (cl->ffIn == -1 || cl->ffIn == ff) &&
\r
1094 (cl->rtIn == -1 || cl->rtIn == rt) &&
\r
1095 (cl->ftIn == -1 || cl->ftIn == ft)) {
\r
1098 cl->piece = board[rf][ff];
\r
1107 void Disambiguate(board, flags, epfile, closure)
\r
1109 int flags, epfile;
\r
1110 DisambiguateClosure *closure;
\r
1112 int illegal = 0; char c = closure->promoCharIn;
\r
1113 closure->count = 0;
\r
1114 closure->rf = closure->ff = closure->rt = closure->ft = 0;
\r
1115 closure->kind = ImpossibleMove;
\r
1116 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
\r
1117 if (closure->count == 0) {
\r
1118 /* See if it's an illegal move due to check */
\r
1120 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
\r
1121 (VOIDSTAR) closure);
\r
1122 if (closure->count == 0) {
\r
1123 /* No, it's not even that */
\r
1128 if (appData.debugMode) {
\r
1129 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1130 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1132 if(gameInfo.variant == VariantShogi) {
\r
1133 /* [HGM] Shogi promotions. '=' means defer */
\r
1134 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
\r
1135 ChessSquare piece = closure->piece;
\r
1137 if (appData.debugMode) {
\r
1138 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1139 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1141 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
\r
1142 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
\r
1143 closure->kind = IllegalMove;
\r
1144 else if(flags & F_WHITE_ON_MOVE) {
\r
1145 if (appData.debugMode) {
\r
1146 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1147 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1149 if( (int) piece < (int) WhiteWazir &&
\r
1150 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
\r
1151 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
\r
1152 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1153 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
\r
1154 else /* promotion optional, default is promote */
\r
1155 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
\r
1157 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1158 NormalMove : IllegalMove;
\r
1160 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
\r
1161 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
\r
1162 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
\r
1163 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
\r
1164 else /* promotion optional, default is promote */
\r
1165 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
\r
1167 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1168 NormalMove : IllegalMove;
\r
1172 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
\r
1173 if (closure->kind == WhitePromotionQueen
\r
1174 || closure->kind == BlackPromotionQueen) {
\r
1176 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
\r
1177 closure->promoCharIn);
\r
1179 closure->kind = IllegalMove;
\r
1182 if (appData.debugMode) {
\r
1183 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1184 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,closure->promoCharIn,closure->promoCharIn);
\r
1186 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
\r
1187 if(closure->promoCharIn != '=')
\r
1188 closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));
\r
1189 else closure->promoChar = '=';
\r
1190 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
\r
1191 if (closure->count > 1) {
\r
1192 closure->kind = AmbiguousMove;
\r
1195 /* Note: If more than one illegal move matches, but no legal
\r
1196 moves, we return IllegalMove, not AmbiguousMove. Caller
\r
1197 can look at closure->count to detect this.
\r
1199 closure->kind = IllegalMove;
\r
1201 if(closure->kind == IllegalMove)
\r
1202 /* [HGM] might be a variant we don't understand, pass on promotion info */
\r
1203 closure->promoChar = closure->promoCharIn;
\r
1204 if (appData.debugMode) {
\r
1205 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1206 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,closure->promoChar);
\r
1213 ChessSquare piece;
\r
1214 int rf, ff, rt, ft;
\r
1220 } CoordsToAlgebraicClosure;
\r
1222 extern void CoordsToAlgebraicCallback P((Board board, int flags,
\r
1223 ChessMove kind, int rf, int ff,
\r
1224 int rt, int ft, VOIDSTAR closure));
\r
1226 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1230 int rf, ff, rt, ft;
\r
1233 register CoordsToAlgebraicClosure *cl =
\r
1234 (CoordsToAlgebraicClosure *) closure;
\r
1236 if (rt == cl->rt && ft == cl->ft &&
\r
1237 board[rf][ff] == cl->piece) {
\r
1238 if (rf == cl->rf) {
\r
1239 if (ff == cl->ff) {
\r
1240 cl->kind = kind; /* this is the move we want */
\r
1242 cl->file++; /* need file to rule out this move */
\r
1245 if (ff == cl->ff) {
\r
1246 cl->rank++; /* need rank to rule out this move */
\r
1248 cl->either++; /* rank or file will rule out this move */
\r
1254 /* Convert coordinates to normal algebraic notation.
\r
1255 promoChar must be NULLCHAR or 'x' if not a promotion.
\r
1257 ChessMove CoordsToAlgebraic(board, flags, epfile,
\r
1258 rf, ff, rt, ft, promoChar, out)
\r
1260 int flags, epfile;
\r
1261 int rf, ff, rt, ft;
\r
1263 char out[MOVE_LEN];
\r
1265 ChessSquare piece;
\r
1268 CoordsToAlgebraicClosure cl;
\r
1270 if (rf == DROP_RANK) {
\r
1271 /* Bughouse piece drop */
\r
1272 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
\r
1274 *outp++ = ft + AAA;
\r
1276 *outp++ = rt + ONE;
\r
1277 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1279 AlphaRank(out, 5);
\r
1280 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
\r
1283 if (promoChar == 'x') promoChar = NULLCHAR;
\r
1284 piece = board[rf][ff];
\r
1286 if (appData.debugMode)
\r
1287 fprintf(debugFP, "CoordsToAlgebraic, piece=%d\n", (int)piece);
\r
1291 if (appData.debugMode)
\r
1292 fprintf(debugFP, "CoordsToAlgebraic, Pawn\n");
\r
1293 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
\r
1294 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1295 /* Keep short notation if move is illegal only because it
\r
1296 leaves the player in check, but still return IllegalMove */
\r
1297 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1298 rf, ff, rt, ft, promoChar);
\r
1299 if (kind == IllegalMove) break;
\r
1300 kind = IllegalMove;
\r
1303 *outp++ = ff + AAA;
\r
1304 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
\r
1305 /* Non-capture; use style "e5" */
\r
1307 *outp++ = rt + ONE;
\r
1308 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1310 /* Capture; use style "exd5" */
\r
1311 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
\r
1312 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
\r
1313 *outp++ = ft + AAA;
\r
1315 *outp++ = rt + ONE;
\r
1316 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1318 /* Use promotion suffix style "=Q" */
\r
1320 if (promoChar != NULLCHAR) {
\r
1321 if(gameInfo.variant == VariantShogi) {
\r
1322 /* [HGM] ... but not in Shogi! */
\r
1323 *outp++ = promoChar == '=' ? '=' : '+';
\r
1326 *outp++ = ToUpper(promoChar);
\r
1330 AlphaRank(out, 10);
\r
1336 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
\r
1337 /* Code added by Tord: FRC castling. */
\r
1338 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
\r
1339 (piece == BlackKing && board[rt][ft] == BlackRook)) {
\r
1340 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
\r
1341 return LegalityTest(board, flags, epfile, initialRights,
\r
1342 rf, ff, rt, ft, promoChar);
\r
1344 /* End of code added by Tord */
\r
1345 /* Test for castling or ICS wild castling */
\r
1346 /* Use style "O-O" (oh-oh) for PGN compatibility */
\r
1347 else if (rf == rt &&
\r
1348 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
\r
1349 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
\r
1350 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
\r
1351 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
\r
1352 strcpy(out, "O-O");
\r
1354 strcpy(out, "O-O-O");
\r
1356 /* This notation is always unambiguous, unless there are
\r
1357 kings on both the d and e files, with "wild castling"
\r
1358 possible for the king on the d file and normal castling
\r
1359 possible for the other. ICS rules for wild 9
\r
1360 effectively make castling illegal for either king in
\r
1361 this situation. So I am not going to worry about it;
\r
1362 I'll just generate an ambiguous O-O in this case.
\r
1364 return LegalityTest(board, flags, epfile, initialRights,
\r
1365 rf, ff, rt, ft, promoChar);
\r
1368 /* else fall through */
\r
1376 cl.kind = IllegalMove;
\r
1377 cl.rank = cl.file = cl.either = 0;
\r
1378 GenLegal(board, flags, epfile, initialRights,
\r
1379 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1381 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1382 /* Generate pretty moves for moving into check, but
\r
1383 still return IllegalMove.
\r
1385 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1386 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1387 if (cl.kind == IllegalMove) break;
\r
1388 cl.kind = IllegalMove;
\r
1391 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
\r
1392 else "Ngf3" or "Ngxf7",
\r
1393 else "N1f3" or "N5xf7",
\r
1394 else "Ng1f3" or "Ng5xf7".
\r
1396 if(PieceToChar(piece) == '.') {
\r
1397 /* [HGM] print nonexistent piece as its demoted version */
\r
1398 piece = (ChessSquare) (DEMOTED piece);
\r
1399 if( gameInfo.variant == VariantShogi )
\r
1402 *outp++ = ToUpper(PieceToChar(piece));
\r
1403 if (cl.file || (cl.either && !cl.rank)) {
\r
1404 *outp++ = ff + AAA;
\r
1408 *outp++ = rf + ONE;
\r
1409 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1412 if(board[rt][ft] != EmptySquare)
\r
1415 *outp++ = ft + AAA;
\r
1417 *outp++ = rt + ONE;
\r
1418 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1420 if (gameInfo.variant == VariantShogi) {
\r
1421 /* [HGM] in Shogi non-pawns can promote */
\r
1422 if(flags & F_WHITE_ON_MOVE) {
\r
1423 if( (int) cl.piece < (int) WhiteWazir &&
\r
1424 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1425 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1426 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1427 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1428 else cl.kind = WhitePromotionQueen; /* promotion optional */
\r
1430 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1431 NormalMove : IllegalMove;
\r
1433 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1434 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1435 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1436 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1437 else cl.kind = BlackPromotionQueen; /* promotion optional */
\r
1438 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1439 NormalMove : IllegalMove;
\r
1441 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1442 /* for optional promotions append '+' or '=' */
\r
1443 if(promoChar == '=') {
\r
1445 cl.kind = NormalMove;
\r
1446 } else *outp++ = '+';
\r
1448 } else if(cl.kind == IllegalMove) {
\r
1449 /* Illegal move specifies any given promotion */
\r
1450 if(promoChar != NULLCHAR && promoChar != 'x') {
\r
1452 *outp++ = ToUpper(promoChar);
\r
1457 AlphaRank(out, 10);
\r
1460 /* [HGM] Always long notation for fairies we don't know */
\r
1461 case WhiteNightrider:
\r
1462 case BlackNightrider:
\r
1463 case WhiteGrasshopper:
\r
1464 case BlackGrasshopper:
\r
1466 /* Moving a nonexistent piece */
\r
1470 /* Not a legal move, even ignoring check.
\r
1471 If there was a piece on the from square,
\r
1472 use style "Ng1g3" or "Ng1xe8";
\r
1473 if there was a pawn or nothing (!),
\r
1474 use style "g1g3" or "g1xe8". Use "x"
\r
1475 if a piece was on the to square, even
\r
1476 a piece of the same color.
\r
1479 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
\r
1480 *outp++ = ToUpper(PieceToChar(piece));
\r
1482 *outp++ = ff + AAA;
\r
1484 *outp++ = rf + ONE;
\r
1485 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1486 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
\r
1487 *outp++ = ft + AAA;
\r
1489 *outp++ = rt + ONE;
\r
1490 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1491 /* Use promotion suffix style "=Q" */
\r
1492 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1494 *outp++ = ToUpper(promoChar);
\r
1498 AlphaRank(out, 0);
\r
1499 return IllegalMove;
\r