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
120 case WhitePromotionChancellor:
\r
121 return WhiteMarshall;
\r
122 case BlackPromotionChancellor:
\r
123 return BlackMarshall;
\r
124 case WhitePromotionArchbishop:
\r
126 case BlackPromotionArchbishop:
\r
128 case WhitePromotionCentaur:
\r
129 return WhiteSilver;
\r
130 case BlackPromotionCentaur:
\r
131 return BlackSilver;
\r
135 char pieceToChar[] = {
\r
136 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
\r
137 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 's', 'U', 'K',
\r
138 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
\r
139 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
\r
142 char PieceToChar(p)
\r
145 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
\r
146 return pieceToChar[(int) p];
\r
149 int PieceToNumber(p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
\r
153 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
\r
155 while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
\r
159 ChessSquare CharToPiece(c)
\r
163 for(i=0; i< (int) EmptySquare; i++)
\r
164 if(pieceToChar[i] == c) return (ChessSquare) i;
\r
165 return EmptySquare;
\r
168 ChessMove PromoCharToMoveType(whiteOnMove, promoChar)
\r
171 { /* [HGM] made dependent on CharToPiece to alow alternate piece letters */
\r
172 ChessSquare piece = CharToPiece(whiteOnMove ? ToUpper(promoChar) : ToLower(promoChar) );
\r
175 if(promoChar == NULLCHAR) return NormalMove;
\r
179 return WhitePromotionQueen;
\r
181 return WhitePromotionRook;
\r
183 return WhitePromotionBishop;
\r
185 return WhitePromotionKnight;
\r
187 return WhitePromotionKing;
\r
189 return WhitePromotionArchbishop;
\r
190 case WhiteMarshall:
\r
191 return WhitePromotionChancellor;
\r
193 return WhitePromotionCentaur;
\r
195 return BlackPromotionQueen;
\r
197 return BlackPromotionRook;
\r
199 return BlackPromotionBishop;
\r
201 return BlackPromotionKnight;
\r
203 return BlackPromotionKing;
\r
205 return BlackPromotionArchbishop;
\r
206 case BlackMarshall:
\r
207 return BlackPromotionChancellor;
\r
209 return BlackPromotionCentaur;
\r
211 // not all promotion implemented yet! Take Queen for those we don't know.
\r
212 return (whiteOnMove ? WhitePromotionQueen : BlackPromotionQueen);
\r
216 void CopyBoard(to, from)
\r
221 for (i = 0; i < BOARD_HEIGHT; i++)
\r
222 for (j = 0; j < BOARD_WIDTH; j++)
\r
223 to[i][j] = from[i][j];
\r
226 int CompareBoards(board1, board2)
\r
227 Board board1, board2;
\r
231 for (i = 0; i < BOARD_HEIGHT; i++)
\r
232 for (j = 0; j < BOARD_WIDTH; j++) {
\r
233 if (board1[i][j] != board2[i][j])
\r
240 /* Call callback once for each pseudo-legal move in the given
\r
241 position, except castling moves. A move is pseudo-legal if it is
\r
242 legal, or if it would be legal except that it leaves the king in
\r
243 check. In the arguments, epfile is EP_NONE if the previous move
\r
244 was not a double pawn push, or the file 0..7 if it was, or
\r
245 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
\r
246 Promotion moves generated are to Queen only.
\r
248 void GenPseudoLegal(board, flags, epfile, callback, closure)
\r
252 MoveCallback callback;
\r
256 int i, j, d, s, fs, rs, rt, ft, m;
\r
258 for (rf = 0; rf < BOARD_HEIGHT; rf++)
\r
259 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
\r
262 if (flags & F_WHITE_ON_MOVE) {
\r
263 if (!WhitePiece(board[rf][ff])) continue;
\r
265 if (!BlackPiece(board[rf][ff])) continue;
\r
267 m = 0; piece = board[rf][ff];
\r
268 if(PieceToChar(piece) == '~')
\r
269 piece = (ChessSquare) ( DEMOTED piece );
\r
270 if(gameInfo.variant == VariantShogi)
\r
271 piece = (ChessSquare) ( SHOGI piece );
\r
274 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
\r
276 /* can't happen ([HGM] except for faries...) */
\r
280 if(gameInfo.variant == VariantXiangqi) {
\r
281 /* [HGM] capture and move straight ahead in Xiangqi */
\r
282 if (rf < BOARD_HEIGHT-1 &&
\r
283 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
\r
284 callback(board, flags, NormalMove,
\r
285 rf, ff, rf + 1, ff, closure);
\r
287 /* and move sideways when across the river */
\r
288 for (s = -1; s <= 1; s += 2) {
\r
289 if (rf >= BOARD_HEIGHT>>1 &&
\r
290 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
291 !WhitePiece(board[rf][ff+s]) ) {
\r
292 callback(board, flags, NormalMove,
\r
293 rf, ff, rf, ff+s, closure);
\r
298 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
\r
299 callback(board, flags,
\r
300 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
301 rf, ff, rf + 1, ff, closure);
\r
303 if (rf == 1 && board[2][ff] == EmptySquare &&
\r
304 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
305 gameInfo.variant != VariantCourier && /* [HGM] */
\r
306 board[3][ff] == EmptySquare ) {
\r
307 callback(board, flags, NormalMove,
\r
308 rf, ff, 3, ff, closure);
\r
310 for (s = -1; s <= 1; s += 2) {
\r
311 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
312 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
313 BlackPiece(board[rf + 1][ff + s]))) {
\r
314 callback(board, flags,
\r
315 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
316 rf, ff, rf + 1, ff + s, closure);
\r
318 if (rf == BOARD_HEIGHT-4) {
\r
319 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
320 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
321 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
\r
322 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
\r
323 callback(board, flags, WhiteCapturesEnPassant,
\r
324 rf, ff, 5, ff + s, closure);
\r
331 if(gameInfo.variant == VariantXiangqi) {
\r
332 /* [HGM] capture straight ahead in Xiangqi */
\r
333 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
\r
334 callback(board, flags, NormalMove,
\r
335 rf, ff, rf - 1, ff, closure);
\r
337 /* and move sideways when across the river */
\r
338 for (s = -1; s <= 1; s += 2) {
\r
339 if (rf < BOARD_HEIGHT>>1 &&
\r
340 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
341 !BlackPiece(board[rf][ff+s]) ) {
\r
342 callback(board, flags, NormalMove,
\r
343 rf, ff, rf, ff+s, closure);
\r
348 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
\r
349 callback(board, flags,
\r
350 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
351 rf, ff, rf - 1, ff, closure);
\r
353 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
\r
354 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
355 gameInfo.variant != VariantCourier && /* [HGM] */
\r
356 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
\r
357 callback(board, flags, NormalMove,
\r
358 rf, ff, BOARD_HEIGHT-4, ff, closure);
\r
360 for (s = -1; s <= 1; s += 2) {
\r
361 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
362 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
363 WhitePiece(board[rf - 1][ff + s]))) {
\r
364 callback(board, flags,
\r
365 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
366 rf, ff, rf - 1, ff + s, closure);
\r
369 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
370 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
371 board[3][ff + s] == WhitePawn &&
\r
372 board[2][ff + s] == EmptySquare) {
\r
373 callback(board, flags, BlackCapturesEnPassant,
\r
374 rf, ff, 2, ff + s, closure);
\r
385 for (i = -1; i <= 1; i += 2)
\r
386 for (j = -1; j <= 1; j += 2)
\r
387 for (s = 1; s <= 2; s++) {
\r
390 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
391 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
\r
392 && !SameColor(board[rf][ff], board[rt][ft]))
\r
393 callback(board, flags, NormalMove,
\r
394 rf, ff, rt, ft, closure);
\r
398 case SHOGI WhiteKnight:
\r
399 for (s = -1; s <= 1; s += 2) {
\r
400 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
401 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
\r
402 callback(board, flags, NormalMove,
\r
403 rf, ff, rf + 2, ff + s, closure);
\r
408 case SHOGI BlackKnight:
\r
409 for (s = -1; s <= 1; s += 2) {
\r
410 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
411 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
\r
412 callback(board, flags, NormalMove,
\r
413 rf, ff, rf - 2, ff + s, closure);
\r
420 for (d = 0; d <= 1; d++)
\r
421 for (s = -1; s <= 1; s += 2) {
\r
423 for (i = 1;; i++) {
\r
424 rt = rf + (i * s) * d;
\r
425 ft = ff + (i * s) * (1 - d);
\r
426 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
427 if (m == 0 && board[rt][ft] == EmptySquare)
\r
428 callback(board, flags, NormalMove,
\r
429 rf, ff, rt, ft, closure);
\r
430 if (m == 1 && board[rt][ft] != EmptySquare &&
\r
431 !SameColor(board[rf][ff], board[rt][ft]) )
\r
432 callback(board, flags, NormalMove,
\r
433 rf, ff, rt, ft, closure);
\r
434 if (board[rt][ft] != EmptySquare && m++) break;
\r
439 /* Gold General (and all its promoted versions) . First do the */
\r
440 /* diagonal forward steps, then proceed as normal Wazir */
\r
441 case SHOGI WhiteWazir:
\r
442 case SHOGI (PROMOTED WhitePawn):
\r
443 case SHOGI (PROMOTED WhiteKnight):
\r
444 case SHOGI (PROMOTED WhiteQueen):
\r
445 case SHOGI (PROMOTED WhiteFerz):
\r
446 for (s = -1; s <= 1; s += 2) {
\r
447 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
448 !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
\r
449 callback(board, flags, NormalMove,
\r
450 rf, ff, rf + 1, ff + s, closure);
\r
455 case SHOGI BlackWazir:
\r
456 case SHOGI (PROMOTED BlackPawn):
\r
457 case SHOGI (PROMOTED BlackKnight):
\r
458 case SHOGI (PROMOTED BlackQueen):
\r
459 case SHOGI (PROMOTED BlackFerz):
\r
460 for (s = -1; s <= 1; s += 2) {
\r
461 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
462 !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
\r
463 callback(board, flags, NormalMove,
\r
464 rf, ff, rf - 1, ff + s, closure);
\r
471 for (d = 0; d <= 1; d++)
\r
472 for (s = -1; s <= 1; s += 2) {
\r
474 ft = ff + s * (1 - d);
\r
475 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
476 && !SameColor(board[rf][ff], board[rt][ft]) &&
\r
477 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
478 callback(board, flags, NormalMove,
\r
479 rf, ff, rt, ft, closure);
\r
485 /* [HGM] support Shatranj pieces */
\r
486 for (rs = -1; rs <= 1; rs += 2)
\r
487 for (fs = -1; fs <= 1; fs += 2) {
\r
490 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
491 && ( gameInfo.variant != VariantXiangqi ||
\r
492 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
\r
494 && !SameColor(board[rf][ff], board[rt][ft]))
\r
495 callback(board, flags, NormalMove,
\r
496 rf, ff, rt, ft, closure);
\r
500 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
\r
501 case SHOGI WhiteCardinal:
\r
502 case SHOGI BlackCardinal:
\r
505 /* Capablanca Archbishop continues as Knight */
\r
510 /* Shogi Bishops are ordinary Bishops */
\r
511 case SHOGI WhiteBishop:
\r
512 case SHOGI BlackBishop:
\r
515 for (rs = -1; rs <= 1; rs += 2)
\r
516 for (fs = -1; fs <= 1; fs += 2)
\r
517 for (i = 1;; i++) {
\r
518 rt = rf + (i * rs);
\r
519 ft = ff + (i * fs);
\r
520 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
521 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
522 callback(board, flags, NormalMove,
\r
523 rf, ff, rt, ft, closure);
\r
524 if (board[rt][ft] != EmptySquare) break;
\r
526 if(m==1) goto mounted;
\r
527 if(m==2) goto finishGold;
\r
528 /* Bishop falls through */
\r
531 /* Shogi Lance is unlike anything, and asymmetric at that */
\r
532 case SHOGI WhiteQueen:
\r
536 if (rt >= BOARD_HEIGHT) 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
544 case SHOGI BlackQueen:
\r
549 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
550 callback(board, flags, NormalMove,
\r
551 rf, ff, rt, ft, closure);
\r
552 if (board[rt][ft] != EmptySquare) break;
\r
556 /* Shogi Dragon King has to continue as Ferz after Rook moves */
\r
557 case SHOGI WhiteDragon:
\r
558 case SHOGI BlackDragon:
\r
561 /* Capablanca Chancellor sets flag to continue as Knight */
\r
562 case WhiteMarshall:
\r
563 case BlackMarshall:
\r
566 /* Shogi Rooks are ordinary Rooks */
\r
567 case SHOGI WhiteRook:
\r
568 case SHOGI BlackRook:
\r
571 for (d = 0; d <= 1; d++)
\r
572 for (s = -1; s <= 1; s += 2)
\r
573 for (i = 1;; i++) {
\r
574 rt = rf + (i * s) * d;
\r
575 ft = ff + (i * s) * (1 - d);
\r
576 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
577 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
578 callback(board, flags, NormalMove,
\r
579 rf, ff, rt, ft, closure);
\r
580 if (board[rt][ft] != EmptySquare) break;
\r
582 if(m==1) goto mounted;
\r
583 if(m==2) goto finishGold;
\r
588 for (rs = -1; rs <= 1; rs++)
\r
589 for (fs = -1; fs <= 1; fs++) {
\r
590 if (rs == 0 && fs == 0) continue;
\r
591 for (i = 1;; i++) {
\r
592 rt = rf + (i * rs);
\r
593 ft = ff + (i * fs);
\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
603 /* Shogi Pawn and Silver General: first the Pawn move, */
\r
604 /* then the General continues like a Ferz */
\r
605 case SHOGI WhitePawn:
\r
606 case SHOGI WhiteFerz:
\r
607 if (rf < BOARD_HEIGHT-1 &&
\r
608 !SameColor(board[rf][ff], board[rf + 1][ff]) )
\r
609 callback(board, flags, NormalMove,
\r
610 rf, ff, rf + 1, ff, closure);
\r
611 if(piece != SHOGI WhitePawn) goto finishSilver;
\r
614 case SHOGI BlackPawn:
\r
615 case SHOGI BlackFerz:
\r
617 !SameColor(board[rf][ff], board[rf - 1][ff]) )
\r
618 callback(board, flags, NormalMove,
\r
619 rf, ff, rf - 1, ff, closure);
\r
620 if(piece == SHOGI BlackPawn) break;
\r
625 /* [HGM] support Shatranj pieces */
\r
626 for (rs = -1; rs <= 1; rs += 2)
\r
627 for (fs = -1; fs <= 1; fs += 2) {
\r
630 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
631 if (!SameColor(board[rf][ff], board[rt][ft]) &&
\r
632 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
633 callback(board, flags, NormalMove,
\r
634 rf, ff, rt, ft, closure);
\r
640 m++; // [HGM] superchess: use for Centaur
\r
643 case SHOGI WhiteKing:
\r
644 case SHOGI BlackKing:
\r
648 for (i = -1; i <= 1; i++)
\r
649 for (j = -1; j <= 1; j++) {
\r
650 if (i == 0 && j == 0) continue;
\r
653 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
654 if (SameColor(board[rf][ff], board[rt][ft])) continue;
\r
655 callback(board, flags, NormalMove,
\r
656 rf, ff, rt, ft, closure);
\r
658 if(m==1) goto mounted;
\r
661 case WhiteNightrider:
\r
662 case BlackNightrider:
\r
663 for (i = -1; i <= 1; i += 2)
\r
664 for (j = -1; j <= 1; j += 2)
\r
665 for (s = 1; s <= 2; s++) { int k;
\r
668 ft = ff + k*j*(3-s);
\r
669 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
670 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
671 callback(board, flags, NormalMove,
\r
672 rf, ff, rt, ft, closure);
\r
673 if (board[rt][ft] != EmptySquare) break;
\r
688 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
\r
689 int rf, int ff, int rt, int ft,
\r
690 VOIDSTAR closure));
\r
692 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
696 int rf, ff, rt, ft;
\r
699 register GenLegalClosure *cl = (GenLegalClosure *) closure;
\r
701 if (!(flags & F_IGNORE_CHECK) &&
\r
702 CheckTest(board, flags, rf, ff, rt, ft,
\r
703 kind == WhiteCapturesEnPassant ||
\r
704 kind == BlackCapturesEnPassant)) return;
\r
705 if (flags & F_ATOMIC_CAPTURE) {
\r
706 if (board[rt][ft] != EmptySquare ||
\r
707 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
\r
709 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
\r
710 if (board[rf][ff] == king) return;
\r
711 for (r = rt-1; r <= rt+1; r++) {
\r
712 for (f = ft-1; f <= ft+1; f++) {
\r
713 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
\r
714 board[r][f] == king) return;
\r
719 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
\r
724 int rf, ff, rt, ft;
\r
726 } LegalityTestClosure;
\r
729 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
\r
730 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
\r
731 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
\r
732 moves that would destroy your own king. The CASTLE_OK flags are
\r
733 true if castling is not yet ruled out by a move of the king or
\r
734 rook. Return TRUE if the player on move is currently in check and
\r
735 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
\r
736 int GenLegal(board, flags, epfile, castlingRights, callback, closure)
\r
740 char castlingRights[];
\r
741 MoveCallback callback;
\r
744 GenLegalClosure cl;
\r
745 int ff, ft, k, left, right;
\r
746 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
\r
747 ChessSquare wKing = WhiteKing, bKing = BlackKing;
\r
751 GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
\r
753 if (!ignoreCheck &&
\r
754 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
\r
756 /* Generate castling moves */
\r
757 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
\r
758 wKing = WhiteUnicorn; bKing = BlackUnicorn;
\r
761 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
\r
762 if ((flags & F_WHITE_ON_MOVE) &&
\r
763 (flags & F_WHITE_KCASTLE_OK) &&
\r
764 board[0][ff] == wKing &&
\r
765 board[0][ff + 1] == EmptySquare &&
\r
766 board[0][ff + 2] == EmptySquare &&
\r
767 board[0][BOARD_RGHT-3] == EmptySquare &&
\r
768 board[0][BOARD_RGHT-2] == EmptySquare &&
\r
769 board[0][BOARD_RGHT-1] == WhiteRook &&
\r
770 castlingRights[0] >= 0 && /* [HGM] check rights */
\r
771 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
773 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
\r
774 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
\r
775 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
\r
776 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
\r
778 callback(board, flags,
\r
779 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
\r
780 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
\r
782 if ((flags & F_WHITE_ON_MOVE) &&
\r
783 (flags & F_WHITE_QCASTLE_OK) &&
\r
784 board[0][ff] == wKing &&
\r
785 board[0][ff - 1] == EmptySquare &&
\r
786 board[0][ff - 2] == EmptySquare &&
\r
787 board[0][BOARD_LEFT+2] == EmptySquare &&
\r
788 board[0][BOARD_LEFT+1] == EmptySquare &&
\r
789 board[0][BOARD_LEFT+0] == WhiteRook &&
\r
790 castlingRights[1] >= 0 && /* [HGM] check rights */
\r
791 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
793 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
\r
794 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
\r
795 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
\r
797 callback(board, flags,
\r
798 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
\r
799 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
801 if (!(flags & F_WHITE_ON_MOVE) &&
\r
802 (flags & F_BLACK_KCASTLE_OK) &&
\r
803 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
804 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
\r
805 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
\r
806 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
\r
807 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
\r
808 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
\r
809 castlingRights[3] >= 0 && /* [HGM] check rights */
\r
810 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
812 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
\r
813 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
\r
814 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
\r
815 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
\r
817 callback(board, flags,
\r
818 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
\r
819 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
\r
821 if (!(flags & F_WHITE_ON_MOVE) &&
\r
822 (flags & F_BLACK_QCASTLE_OK) &&
\r
823 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
824 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
\r
825 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
\r
826 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
\r
827 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
\r
828 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
\r
829 castlingRights[4] >= 0 && /* [HGM] check rights */
\r
830 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
832 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
\r
833 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
\r
834 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
\r
836 callback(board, flags,
\r
837 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
\r
838 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
842 if(gameInfo.variant == VariantFischeRandom) {
\r
844 /* generate all potential FRC castling moves (KxR), ignoring flags */
\r
845 /* [HGM] test if the Rooks we find have castling rights */
\r
848 if ((flags & F_WHITE_ON_MOVE) != 0) {
\r
849 ff = castlingRights[2]; /* King file if we have any rights */
\r
850 if(ff > 0 && board[0][ff] == WhiteKing) {
\r
851 if (appData.debugMode) {
\r
852 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
\r
853 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
\r
855 ft = castlingRights[0]; /* Rook file if we have H-side rights */
\r
857 right = BOARD_RGHT-2;
\r
858 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
859 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
860 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
861 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
862 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
863 if(ft >= 0 && board[0][ft] == WhiteRook)
\r
864 callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
\r
866 ft = castlingRights[1]; /* Rook file if we have A-side rights */
\r
867 left = BOARD_LEFT+2;
\r
869 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
870 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
871 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
872 if(ff > BOARD_LEFT+2)
\r
873 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
874 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
875 if(ft >= 0 && board[0][ft] == WhiteRook)
\r
876 callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
\r
879 ff = castlingRights[5]; /* King file if we have any rights */
\r
880 if(ff > 0 && board[BOARD_HEIGHT-1][ff] == BlackKing) {
\r
881 ft = castlingRights[3]; /* Rook file if we have H-side rights */
\r
883 right = BOARD_RGHT-2;
\r
884 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
885 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
886 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
887 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
888 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
889 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
\r
890 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
892 ft = castlingRights[4]; /* Rook file if we have A-side rights */
\r
893 left = BOARD_LEFT+2;
\r
895 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
896 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
897 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
898 if(ff > BOARD_LEFT+2)
\r
899 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
900 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
901 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
\r
902 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
915 } CheckTestClosure;
\r
918 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
\r
919 int rf, int ff, int rt, int ft,
\r
920 VOIDSTAR closure));
\r
923 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
927 int rf, ff, rt, ft;
\r
930 register CheckTestClosure *cl = (CheckTestClosure *) closure;
\r
932 if (rt == cl->rking && ft == cl->fking) cl->check++;
\r
936 /* If the player on move were to move from (rf, ff) to (rt, ft), would
\r
937 he leave himself in check? Or if rf == -1, is the player on move
\r
938 in check now? enPassant must be TRUE if the indicated move is an
\r
939 e.p. capture. The possibility of castling out of a check along the
\r
940 back rank is not accounted for (i.e., we still return nonzero), as
\r
941 this is illegal anyway. Return value is the number of times the
\r
942 king is in check. */
\r
943 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
\r
946 int rf, ff, rt, ft, enPassant;
\r
948 CheckTestClosure cl;
\r
949 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
\r
950 ChessSquare captured = EmptySquare;
\r
951 /* Suppress warnings on uninitialized variables */
\r
953 if(gameInfo.variant == VariantXiangqi)
\r
954 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
\r
955 if(gameInfo.variant == VariantKnightmate)
\r
956 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
\r
960 captured = board[rf][ft];
\r
961 board[rf][ft] = EmptySquare;
\r
963 captured = board[rt][ft];
\r
965 board[rt][ft] = board[rf][ff];
\r
966 board[rf][ff] = EmptySquare;
\r
969 /* For compatibility with ICS wild 9, we scan the board in the
\r
970 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
\r
971 and we test only whether that one is in check. */
\r
973 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
\r
974 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
\r
975 if (board[cl.rking][cl.fking] == king) {
\r
976 if(gameInfo.variant == VariantXiangqi) {
\r
977 /* [HGM] In Xiangqi opposing Kings means check as well */
\r
979 dir = (king >= BlackPawn) ? -1 : 1;
\r
980 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
\r
981 board[i][cl.fking] == EmptySquare; i+=dir );
\r
982 if(i>=0 && i<BOARD_HEIGHT &&
\r
983 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
\r
987 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
\r
988 CheckTestCallback, (VOIDSTAR) &cl);
\r
989 goto undo_move; /* 2-level break */
\r
996 board[rf][ff] = board[rt][ft];
\r
998 board[rf][ft] = captured;
\r
999 board[rt][ft] = EmptySquare;
\r
1001 board[rt][ft] = captured;
\r
1009 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
\r
1010 int rf, int ff, int rt, int ft,
\r
1011 VOIDSTAR closure));
\r
1013 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1017 int rf, ff, rt, ft;
\r
1020 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
\r
1022 // if (appData.debugMode) {
\r
1023 // fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
1025 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
\r
1029 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
\r
1031 int flags, epfile;
\r
1032 int rf, ff, rt, ft, promoChar;
\r
1033 char castlingRights[];
\r
1035 LegalityTestClosure cl; ChessSquare piece = board[rf][ff];
\r
1037 if (appData.debugMode) {
\r
1039 for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
\r
1040 fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
1042 /* [HGM] Lance, Cobra and Falcon are wildcard pieces; consider all their moves legal */
\r
1043 /* (perhaps we should disallow moves that obviously leave us in check?) */
\r
1044 if(piece == WhiteFalcon || piece == BlackFalcon ||
\r
1045 piece == WhiteCobra || piece == BlackCobra ||
\r
1046 piece == WhiteLance || piece == BlackLance)
\r
1047 return NormalMove;
\r
1053 cl.kind = IllegalMove;
\r
1054 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
\r
1056 if(gameInfo.variant == VariantShogi) {
\r
1057 /* [HGM] Shogi promotions. '=' means defer */
\r
1058 if(rf != DROP_RANK && cl.kind == NormalMove) {
\r
1059 ChessSquare piece = board[rf][ff];
\r
1061 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
\r
1062 if(promoChar != NULLCHAR && promoChar != 'x' &&
\r
1063 promoChar != '+' && promoChar != '=' &&
\r
1064 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
\r
1065 cl.kind = IllegalMove;
\r
1066 else if(flags & F_WHITE_ON_MOVE) {
\r
1067 if( (int) piece < (int) WhiteWazir &&
\r
1068 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1069 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1070 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1071 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1072 else /* promotion optional, default is promote */
\r
1073 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
\r
1075 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1076 NormalMove : IllegalMove;
\r
1078 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1079 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1080 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1081 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1082 else /* promotion optional, default is promote */
\r
1083 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
\r
1085 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1086 NormalMove : IllegalMove;
\r
1090 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1091 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
\r
1092 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1094 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
\r
1096 cl.kind = IllegalMove;
\r
1099 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
\r
1105 } MateTestClosure;
\r
1107 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
\r
1108 int rf, int ff, int rt, int ft,
\r
1109 VOIDSTAR closure));
\r
1111 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1115 int rf, ff, rt, ft;
\r
1118 register MateTestClosure *cl = (MateTestClosure *) closure;
\r
1123 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
\r
1124 int MateTest(board, flags, epfile, castlingRights)
\r
1126 int flags, epfile;
\r
1127 char castlingRights[];
\r
1129 MateTestClosure cl;
\r
1133 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
\r
1134 if (cl.count > 0) {
\r
1135 return inCheck ? MT_CHECK : MT_NONE;
\r
1137 return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ?
\r
1138 MT_CHECKMATE : MT_STALEMATE;
\r
1143 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
\r
1144 int rf, int ff, int rt, int ft,
\r
1145 VOIDSTAR closure));
\r
1147 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1151 int rf, ff, rt, ft;
\r
1154 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
\r
1156 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
\r
1157 || PieceToChar(board[rf][ff]) == '~'
\r
1158 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
\r
1160 (cl->rfIn == -1 || cl->rfIn == rf) &&
\r
1161 (cl->ffIn == -1 || cl->ffIn == ff) &&
\r
1162 (cl->rtIn == -1 || cl->rtIn == rt) &&
\r
1163 (cl->ftIn == -1 || cl->ftIn == ft)) {
\r
1166 cl->piece = board[rf][ff];
\r
1175 void Disambiguate(board, flags, epfile, closure)
\r
1177 int flags, epfile;
\r
1178 DisambiguateClosure *closure;
\r
1180 int illegal = 0; char c = closure->promoCharIn;
\r
1181 closure->count = 0;
\r
1182 closure->rf = closure->ff = closure->rt = closure->ft = 0;
\r
1183 closure->kind = ImpossibleMove;
\r
1184 if (appData.debugMode) {
\r
1185 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1186 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1187 closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
\r
1189 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
\r
1190 if (closure->count == 0) {
\r
1191 /* See if it's an illegal move due to check */
\r
1193 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
\r
1194 (VOIDSTAR) closure);
\r
1195 if (closure->count == 0) {
\r
1196 /* No, it's not even that */
\r
1197 if (appData.debugMode) { int i, j;
\r
1198 for(i=BOARD_HEIGHT-1; i>=0; i--) {
\r
1199 for(j=0; j<BOARD_WIDTH; j++)
\r
1200 fprintf(debugFP, "%3d", (int) board[i][j]);
\r
1201 fprintf(debugFP, "\n");
\r
1208 if(gameInfo.variant == VariantShogi) {
\r
1209 /* [HGM] Shogi promotions. '=' means defer */
\r
1210 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
\r
1211 ChessSquare piece = closure->piece;
\r
1213 if (appData.debugMode) {
\r
1214 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1215 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1216 closure->promoCharIn,closure->promoCharIn);
\r
1219 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
\r
1220 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
\r
1221 closure->kind = IllegalMove;
\r
1222 else if(flags & F_WHITE_ON_MOVE) {
\r
1224 if (appData.debugMode) {
\r
1225 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1226 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1227 closure->promoCharIn,closure->promoCharIn);
\r
1230 if( (int) piece < (int) WhiteWazir &&
\r
1231 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
\r
1232 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
\r
1233 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1234 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
\r
1235 else /* promotion optional, default is promote */
\r
1236 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
\r
1238 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1239 NormalMove : IllegalMove;
\r
1241 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
\r
1242 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
\r
1243 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
\r
1244 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
\r
1245 else /* promotion optional, default is promote */
\r
1246 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
\r
1248 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1249 NormalMove : IllegalMove;
\r
1253 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
\r
1254 if (closure->kind == WhitePromotionQueen
\r
1255 || closure->kind == BlackPromotionQueen) {
\r
1257 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
\r
1258 closure->promoCharIn);
\r
1260 closure->kind = IllegalMove;
\r
1264 if (appData.debugMode) {
\r
1265 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1266 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1267 closure->promoCharIn,closure->promoCharIn);
\r
1270 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
\r
1271 if(closure->promoCharIn != '=')
\r
1272 closure->promoChar = ToLower(closure->promoCharIn);
\r
1273 else closure->promoChar = '=';
\r
1274 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
\r
1275 if (closure->count > 1) {
\r
1276 closure->kind = AmbiguousMove;
\r
1279 /* Note: If more than one illegal move matches, but no legal
\r
1280 moves, we return IllegalMove, not AmbiguousMove. Caller
\r
1281 can look at closure->count to detect this.
\r
1283 closure->kind = IllegalMove;
\r
1285 if(closure->kind == IllegalMove)
\r
1286 /* [HGM] might be a variant we don't understand, pass on promotion info */
\r
1287 closure->promoChar = ToLower(closure->promoCharIn);
\r
1288 if (appData.debugMode) {
\r
1289 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1290 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
\r
1291 closure->promoChar >= ' ' ? closure->promoChar:'-');
\r
1298 ChessSquare piece;
\r
1299 int rf, ff, rt, ft;
\r
1305 } CoordsToAlgebraicClosure;
\r
1307 extern void CoordsToAlgebraicCallback P((Board board, int flags,
\r
1308 ChessMove kind, int rf, int ff,
\r
1309 int rt, int ft, VOIDSTAR closure));
\r
1311 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1315 int rf, ff, rt, ft;
\r
1318 register CoordsToAlgebraicClosure *cl =
\r
1319 (CoordsToAlgebraicClosure *) closure;
\r
1321 if (rt == cl->rt && ft == cl->ft &&
\r
1322 (board[rf][ff] == cl->piece
\r
1323 || PieceToChar(board[rf][ff]) == '~' &&
\r
1324 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
\r
1326 if (rf == cl->rf) {
\r
1327 if (ff == cl->ff) {
\r
1328 cl->kind = kind; /* this is the move we want */
\r
1330 cl->file++; /* need file to rule out this move */
\r
1333 if (ff == cl->ff) {
\r
1334 cl->rank++; /* need rank to rule out this move */
\r
1336 cl->either++; /* rank or file will rule out this move */
\r
1342 /* Convert coordinates to normal algebraic notation.
\r
1343 promoChar must be NULLCHAR or 'x' if not a promotion.
\r
1345 ChessMove CoordsToAlgebraic(board, flags, epfile,
\r
1346 rf, ff, rt, ft, promoChar, out)
\r
1348 int flags, epfile;
\r
1349 int rf, ff, rt, ft;
\r
1351 char out[MOVE_LEN];
\r
1353 ChessSquare piece;
\r
1355 char *outp = out, c;
\r
1356 CoordsToAlgebraicClosure cl;
\r
1358 if (rf == DROP_RANK) {
\r
1359 /* Bughouse piece drop */
\r
1360 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
\r
1362 *outp++ = ft + AAA;
\r
1364 *outp++ = rt + ONE;
\r
1365 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1367 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
\r
1370 if (promoChar == 'x') promoChar = NULLCHAR;
\r
1371 piece = board[rf][ff];
\r
1372 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
\r
1374 if (appData.debugMode)
\r
1375 fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
\r
1379 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
\r
1380 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1381 /* Keep short notation if move is illegal only because it
\r
1382 leaves the player in check, but still return IllegalMove */
\r
1383 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1384 rf, ff, rt, ft, promoChar);
\r
1385 if (kind == IllegalMove) break;
\r
1386 kind = IllegalMove;
\r
1389 *outp++ = ff + AAA;
\r
1390 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
\r
1391 /* Non-capture; use style "e5" */
\r
1393 *outp++ = rt + ONE;
\r
1394 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1396 /* Capture; use style "exd5" */
\r
1397 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
\r
1398 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
\r
1399 *outp++ = ft + AAA;
\r
1401 *outp++ = rt + ONE;
\r
1402 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1404 /* Use promotion suffix style "=Q" */
\r
1406 if (appData.debugMode)
\r
1407 fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
\r
1408 if (promoChar != NULLCHAR) {
\r
1409 if(gameInfo.variant == VariantShogi) {
\r
1410 /* [HGM] ... but not in Shogi! */
\r
1411 *outp++ = promoChar == '=' ? '=' : '+';
\r
1414 *outp++ = ToUpper(promoChar);
\r
1423 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
\r
1424 /* Code added by Tord: FRC castling. */
\r
1425 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
\r
1426 (piece == BlackKing && board[rt][ft] == BlackRook)) {
\r
1427 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
\r
1428 return LegalityTest(board, flags, epfile, initialRights,
\r
1429 rf, ff, rt, ft, promoChar);
\r
1431 /* End of code added by Tord */
\r
1432 /* Test for castling or ICS wild castling */
\r
1433 /* Use style "O-O" (oh-oh) for PGN compatibility */
\r
1434 else if (rf == rt &&
\r
1435 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
\r
1436 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
\r
1437 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
\r
1438 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
\r
1439 strcpy(out, "O-O");
\r
1441 strcpy(out, "O-O-O");
\r
1443 /* This notation is always unambiguous, unless there are
\r
1444 kings on both the d and e files, with "wild castling"
\r
1445 possible for the king on the d file and normal castling
\r
1446 possible for the other. ICS rules for wild 9
\r
1447 effectively make castling illegal for either king in
\r
1448 this situation. So I am not going to worry about it;
\r
1449 I'll just generate an ambiguous O-O in this case.
\r
1451 return LegalityTest(board, flags, epfile, initialRights,
\r
1452 rf, ff, rt, ft, promoChar);
\r
1455 /* else fall through */
\r
1463 cl.kind = IllegalMove;
\r
1464 cl.rank = cl.file = cl.either = 0;
\r
1465 GenLegal(board, flags, epfile, initialRights,
\r
1466 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1468 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1469 /* Generate pretty moves for moving into check, but
\r
1470 still return IllegalMove.
\r
1472 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1473 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1474 if (cl.kind == IllegalMove) break;
\r
1475 cl.kind = IllegalMove;
\r
1478 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
\r
1479 else "Ngf3" or "Ngxf7",
\r
1480 else "N1f3" or "N5xf7",
\r
1481 else "Ng1f3" or "Ng5xf7".
\r
1483 c = PieceToChar(piece) ;
\r
1484 if( c == '~' || c == '+') {
\r
1485 /* [HGM] print nonexistent piece as its demoted version */
\r
1486 piece = (ChessSquare) (DEMOTED piece);
\r
1488 if(c=='+') *outp++ = c;
\r
1489 *outp++ = ToUpper(PieceToChar(piece));
\r
1491 if (cl.file || (cl.either && !cl.rank)) {
\r
1492 *outp++ = ff + AAA;
\r
1496 *outp++ = rf + ONE;
\r
1497 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1500 if(board[rt][ft] != EmptySquare)
\r
1503 *outp++ = ft + AAA;
\r
1505 *outp++ = rt + ONE;
\r
1506 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1508 if (gameInfo.variant == VariantShogi) {
\r
1509 /* [HGM] in Shogi non-pawns can promote */
\r
1510 if(flags & F_WHITE_ON_MOVE) {
\r
1511 if( (int) cl.piece < (int) WhiteWazir &&
\r
1512 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1513 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1514 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1515 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1516 else cl.kind = WhitePromotionQueen; /* promotion optional */
\r
1518 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1519 NormalMove : IllegalMove;
\r
1521 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1522 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1523 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1524 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1525 else cl.kind = BlackPromotionQueen; /* promotion optional */
\r
1526 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1527 NormalMove : IllegalMove;
\r
1529 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1530 /* for optional promotions append '+' or '=' */
\r
1531 if(promoChar == '=') {
\r
1533 cl.kind = NormalMove;
\r
1534 } else *outp++ = '+';
\r
1536 } else if(cl.kind == IllegalMove) {
\r
1537 /* Illegal move specifies any given promotion */
\r
1538 if(promoChar != NULLCHAR && promoChar != 'x') {
\r
1540 *outp++ = ToUpper(promoChar);
\r
1547 /* [HGM] Always long notation for fairies we don't know */
\r
1552 case WhiteGrasshopper:
\r
1553 case BlackGrasshopper:
\r
1555 /* Moving a nonexistent piece */
\r
1559 /* Not a legal move, even ignoring check.
\r
1560 If there was a piece on the from square,
\r
1561 use style "Ng1g3" or "Ng1xe8";
\r
1562 if there was a pawn or nothing (!),
\r
1563 use style "g1g3" or "g1xe8". Use "x"
\r
1564 if a piece was on the to square, even
\r
1565 a piece of the same color.
\r
1568 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
\r
1569 *outp++ = ToUpper(PieceToChar(piece));
\r
1571 *outp++ = ff + AAA;
\r
1573 *outp++ = rf + ONE;
\r
1574 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1575 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
\r
1576 *outp++ = ft + AAA;
\r
1578 *outp++ = rt + ONE;
\r
1579 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1580 /* Use promotion suffix style "=Q" */
\r
1581 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1583 *outp++ = ToUpper(promoChar);
\r
1587 return IllegalMove;
\r