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) );
173 if(promoChar == NULLCHAR) return NormalMove;
\r
177 return WhitePromotionQueen;
\r
179 return WhitePromotionRook;
\r
181 return WhitePromotionBishop;
\r
183 return WhitePromotionKnight;
\r
185 return WhitePromotionKing;
\r
187 return WhitePromotionArchbishop;
\r
188 case WhiteMarshall:
\r
189 return WhitePromotionChancellor;
\r
191 return WhitePromotionCentaur;
\r
193 return BlackPromotionQueen;
\r
195 return BlackPromotionRook;
\r
197 return BlackPromotionBishop;
\r
199 return BlackPromotionKnight;
\r
201 return BlackPromotionKing;
\r
203 return BlackPromotionArchbishop;
\r
204 case BlackMarshall:
\r
205 return BlackPromotionChancellor;
\r
207 return BlackPromotionCentaur;
\r
209 // not all promotion implemented yet! Take Queen for those we don't know.
\r
210 return (whiteOnMove ? WhitePromotionQueen : BlackPromotionQueen);
\r
214 void CopyBoard(to, from)
\r
219 for (i = 0; i < BOARD_HEIGHT; i++)
\r
220 for (j = 0; j < BOARD_WIDTH; j++)
\r
221 to[i][j] = from[i][j];
\r
224 int CompareBoards(board1, board2)
\r
225 Board board1, board2;
\r
229 for (i = 0; i < BOARD_HEIGHT; i++)
\r
230 for (j = 0; j < BOARD_WIDTH; j++) {
\r
231 if (board1[i][j] != board2[i][j])
\r
238 /* Call callback once for each pseudo-legal move in the given
\r
239 position, except castling moves. A move is pseudo-legal if it is
\r
240 legal, or if it would be legal except that it leaves the king in
\r
241 check. In the arguments, epfile is EP_NONE if the previous move
\r
242 was not a double pawn push, or the file 0..7 if it was, or
\r
243 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
\r
244 Promotion moves generated are to Queen only.
\r
246 void GenPseudoLegal(board, flags, epfile, callback, closure)
\r
250 MoveCallback callback;
\r
254 int i, j, d, s, fs, rs, rt, ft, m;
\r
256 for (rf = 0; rf < BOARD_HEIGHT; rf++)
\r
257 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
\r
260 if (flags & F_WHITE_ON_MOVE) {
\r
261 if (!WhitePiece(board[rf][ff])) continue;
\r
263 if (!BlackPiece(board[rf][ff])) continue;
\r
265 m = 0; piece = board[rf][ff];
\r
266 if(PieceToChar(piece) == '~')
\r
267 piece = (ChessSquare) ( DEMOTED piece );
\r
268 if(gameInfo.variant == VariantShogi)
\r
269 piece = (ChessSquare) ( SHOGI piece );
\r
272 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
\r
274 /* can't happen ([HGM] except for faries...) */
\r
278 if(gameInfo.variant == VariantXiangqi) {
\r
279 /* [HGM] capture and move straight ahead in Xiangqi */
\r
280 if (rf < BOARD_HEIGHT-1 &&
\r
281 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
\r
282 callback(board, flags, NormalMove,
\r
283 rf, ff, rf + 1, ff, closure);
\r
285 /* and move sideways when across the river */
\r
286 for (s = -1; s <= 1; s += 2) {
\r
287 if (rf >= BOARD_HEIGHT>>1 &&
\r
288 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
289 !WhitePiece(board[rf][ff+s]) ) {
\r
290 callback(board, flags, NormalMove,
\r
291 rf, ff, rf, ff+s, closure);
\r
296 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
\r
297 callback(board, flags,
\r
298 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
299 rf, ff, rf + 1, ff, closure);
\r
301 if (rf == 1 && board[2][ff] == EmptySquare &&
\r
302 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
303 gameInfo.variant != VariantCourier && /* [HGM] */
\r
304 board[3][ff] == EmptySquare ) {
\r
305 callback(board, flags, NormalMove,
\r
306 rf, ff, 3, ff, closure);
\r
308 for (s = -1; s <= 1; s += 2) {
\r
309 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
310 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
311 BlackPiece(board[rf + 1][ff + s]))) {
\r
312 callback(board, flags,
\r
313 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
314 rf, ff, rf + 1, ff + s, closure);
\r
316 if (rf == BOARD_HEIGHT-4) {
\r
317 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
318 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
319 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
\r
320 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
\r
321 callback(board, flags, WhiteCapturesEnPassant,
\r
322 rf, ff, 5, ff + s, closure);
\r
329 if(gameInfo.variant == VariantXiangqi) {
\r
330 /* [HGM] capture straight ahead in Xiangqi */
\r
331 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
\r
332 callback(board, flags, NormalMove,
\r
333 rf, ff, rf - 1, ff, closure);
\r
335 /* and move sideways when across the river */
\r
336 for (s = -1; s <= 1; s += 2) {
\r
337 if (rf < BOARD_HEIGHT>>1 &&
\r
338 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
339 !BlackPiece(board[rf][ff+s]) ) {
\r
340 callback(board, flags, NormalMove,
\r
341 rf, ff, rf, ff+s, closure);
\r
346 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
\r
347 callback(board, flags,
\r
348 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
349 rf, ff, rf - 1, ff, closure);
\r
351 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
\r
352 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
353 gameInfo.variant != VariantCourier && /* [HGM] */
\r
354 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
\r
355 callback(board, flags, NormalMove,
\r
356 rf, ff, BOARD_HEIGHT-4, ff, closure);
\r
358 for (s = -1; s <= 1; s += 2) {
\r
359 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
360 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
361 WhitePiece(board[rf - 1][ff + s]))) {
\r
362 callback(board, flags,
\r
363 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
364 rf, ff, rf - 1, ff + s, closure);
\r
367 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
368 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
369 board[3][ff + s] == WhitePawn &&
\r
370 board[2][ff + s] == EmptySquare) {
\r
371 callback(board, flags, BlackCapturesEnPassant,
\r
372 rf, ff, 2, ff + s, closure);
\r
383 for (i = -1; i <= 1; i += 2)
\r
384 for (j = -1; j <= 1; j += 2)
\r
385 for (s = 1; s <= 2; s++) {
\r
388 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
389 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
\r
390 && !SameColor(board[rf][ff], board[rt][ft]))
\r
391 callback(board, flags, NormalMove,
\r
392 rf, ff, rt, ft, closure);
\r
396 case SHOGI WhiteKnight:
\r
397 for (s = -1; s <= 1; s += 2) {
\r
398 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
399 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
\r
400 callback(board, flags, NormalMove,
\r
401 rf, ff, rf + 2, ff + s, closure);
\r
406 case SHOGI BlackKnight:
\r
407 for (s = -1; s <= 1; s += 2) {
\r
408 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
409 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
\r
410 callback(board, flags, NormalMove,
\r
411 rf, ff, rf - 2, ff + s, closure);
\r
418 for (d = 0; d <= 1; d++)
\r
419 for (s = -1; s <= 1; s += 2) {
\r
421 for (i = 1;; i++) {
\r
422 rt = rf + (i * s) * d;
\r
423 ft = ff + (i * s) * (1 - d);
\r
424 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
425 if (m == 0 && board[rt][ft] == EmptySquare)
\r
426 callback(board, flags, NormalMove,
\r
427 rf, ff, rt, ft, closure);
\r
428 if (m == 1 && board[rt][ft] != EmptySquare &&
\r
429 !SameColor(board[rf][ff], board[rt][ft]) )
\r
430 callback(board, flags, NormalMove,
\r
431 rf, ff, rt, ft, closure);
\r
432 if (board[rt][ft] != EmptySquare && m++) break;
\r
437 /* Gold General (and all its promoted versions) . First do the */
\r
438 /* diagonal forward steps, then proceed as normal Wazir */
\r
439 case SHOGI WhiteWazir:
\r
440 case SHOGI (PROMOTED WhitePawn):
\r
441 case SHOGI (PROMOTED WhiteKnight):
\r
442 case SHOGI (PROMOTED WhiteQueen):
\r
443 case SHOGI (PROMOTED WhiteFerz):
\r
444 for (s = -1; s <= 1; s += 2) {
\r
445 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
446 !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
\r
447 callback(board, flags, NormalMove,
\r
448 rf, ff, rf + 1, ff + s, closure);
\r
453 case SHOGI BlackWazir:
\r
454 case SHOGI (PROMOTED BlackPawn):
\r
455 case SHOGI (PROMOTED BlackKnight):
\r
456 case SHOGI (PROMOTED BlackQueen):
\r
457 case SHOGI (PROMOTED BlackFerz):
\r
458 for (s = -1; s <= 1; s += 2) {
\r
459 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
460 !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
\r
461 callback(board, flags, NormalMove,
\r
462 rf, ff, rf - 1, ff + s, closure);
\r
469 for (d = 0; d <= 1; d++)
\r
470 for (s = -1; s <= 1; s += 2) {
\r
472 ft = ff + s * (1 - d);
\r
473 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
474 && !SameColor(board[rf][ff], board[rt][ft]) &&
\r
475 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
476 callback(board, flags, NormalMove,
\r
477 rf, ff, rt, ft, closure);
\r
483 /* [HGM] support Shatranj pieces */
\r
484 for (rs = -1; rs <= 1; rs += 2)
\r
485 for (fs = -1; fs <= 1; fs += 2) {
\r
488 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
489 && ( gameInfo.variant != VariantXiangqi ||
\r
490 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
\r
492 && !SameColor(board[rf][ff], board[rt][ft]))
\r
493 callback(board, flags, NormalMove,
\r
494 rf, ff, rt, ft, closure);
\r
498 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
\r
499 case SHOGI WhiteCardinal:
\r
500 case SHOGI BlackCardinal:
\r
503 /* Capablanca Archbishop continues as Knight */
\r
508 /* Shogi Bishops are ordinary Bishops */
\r
509 case SHOGI WhiteBishop:
\r
510 case SHOGI BlackBishop:
\r
513 for (rs = -1; rs <= 1; rs += 2)
\r
514 for (fs = -1; fs <= 1; fs += 2)
\r
515 for (i = 1;; i++) {
\r
516 rt = rf + (i * rs);
\r
517 ft = ff + (i * fs);
\r
518 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
519 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
520 callback(board, flags, NormalMove,
\r
521 rf, ff, rt, ft, closure);
\r
522 if (board[rt][ft] != EmptySquare) break;
\r
524 if(m==1) goto mounted;
\r
525 if(m==2) goto finishGold;
\r
526 /* Bishop falls through */
\r
529 /* Shogi Lance is unlike anything, and asymmetric at that */
\r
530 case SHOGI WhiteQueen:
\r
534 if (rt >= BOARD_HEIGHT) break;
\r
535 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
536 callback(board, flags, NormalMove,
\r
537 rf, ff, rt, ft, closure);
\r
538 if (board[rt][ft] != EmptySquare) break;
\r
542 case SHOGI BlackQueen:
\r
547 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
548 callback(board, flags, NormalMove,
\r
549 rf, ff, rt, ft, closure);
\r
550 if (board[rt][ft] != EmptySquare) break;
\r
554 /* Shogi Dragon King has to continue as Ferz after Rook moves */
\r
555 case SHOGI WhiteDragon:
\r
556 case SHOGI BlackDragon:
\r
559 /* Capablanca Chancellor sets flag to continue as Knight */
\r
560 case WhiteMarshall:
\r
561 case BlackMarshall:
\r
564 /* Shogi Rooks are ordinary Rooks */
\r
565 case SHOGI WhiteRook:
\r
566 case SHOGI BlackRook:
\r
569 for (d = 0; d <= 1; d++)
\r
570 for (s = -1; s <= 1; s += 2)
\r
571 for (i = 1;; i++) {
\r
572 rt = rf + (i * s) * d;
\r
573 ft = ff + (i * s) * (1 - d);
\r
574 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
575 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
576 callback(board, flags, NormalMove,
\r
577 rf, ff, rt, ft, closure);
\r
578 if (board[rt][ft] != EmptySquare) break;
\r
580 if(m==1) goto mounted;
\r
581 if(m==2) goto finishGold;
\r
586 for (rs = -1; rs <= 1; rs++)
\r
587 for (fs = -1; fs <= 1; fs++) {
\r
588 if (rs == 0 && fs == 0) continue;
\r
589 for (i = 1;; i++) {
\r
590 rt = rf + (i * rs);
\r
591 ft = ff + (i * fs);
\r
592 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
593 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
594 callback(board, flags, NormalMove,
\r
595 rf, ff, rt, ft, closure);
\r
596 if (board[rt][ft] != EmptySquare) break;
\r
601 /* Shogi Pawn and Silver General: first the Pawn move, */
\r
602 /* then the General continues like a Ferz */
\r
603 case SHOGI WhitePawn:
\r
604 case SHOGI WhiteFerz:
\r
605 if (rf < BOARD_HEIGHT-1 &&
\r
606 !SameColor(board[rf][ff], board[rf + 1][ff]) )
\r
607 callback(board, flags, NormalMove,
\r
608 rf, ff, rf + 1, ff, closure);
\r
609 if(piece != SHOGI WhitePawn) goto finishSilver;
\r
612 case SHOGI BlackPawn:
\r
613 case SHOGI BlackFerz:
\r
615 !SameColor(board[rf][ff], board[rf - 1][ff]) )
\r
616 callback(board, flags, NormalMove,
\r
617 rf, ff, rf - 1, ff, closure);
\r
618 if(piece == SHOGI BlackPawn) break;
\r
623 /* [HGM] support Shatranj pieces */
\r
624 for (rs = -1; rs <= 1; rs += 2)
\r
625 for (fs = -1; fs <= 1; fs += 2) {
\r
628 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
629 if (!SameColor(board[rf][ff], board[rt][ft]) &&
\r
630 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
631 callback(board, flags, NormalMove,
\r
632 rf, ff, rt, ft, closure);
\r
638 m++; // [HGM] superchess: use for Centaur
\r
641 case SHOGI WhiteKing:
\r
642 case SHOGI BlackKing:
\r
646 for (i = -1; i <= 1; i++)
\r
647 for (j = -1; j <= 1; j++) {
\r
648 if (i == 0 && j == 0) continue;
\r
651 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
652 if (SameColor(board[rf][ff], board[rt][ft])) continue;
\r
653 callback(board, flags, NormalMove,
\r
654 rf, ff, rt, ft, closure);
\r
656 if(m==1) goto mounted;
\r
659 case WhiteNightrider:
\r
660 case BlackNightrider:
\r
661 for (i = -1; i <= 1; i += 2)
\r
662 for (j = -1; j <= 1; j += 2)
\r
663 for (s = 1; s <= 2; s++) { int k;
\r
666 ft = ff + k*j*(3-s);
\r
667 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
668 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
669 callback(board, flags, NormalMove,
\r
670 rf, ff, rt, ft, closure);
\r
671 if (board[rt][ft] != EmptySquare) break;
\r
686 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
\r
687 int rf, int ff, int rt, int ft,
\r
688 VOIDSTAR closure));
\r
690 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
694 int rf, ff, rt, ft;
\r
697 register GenLegalClosure *cl = (GenLegalClosure *) closure;
\r
699 if (!(flags & F_IGNORE_CHECK) &&
\r
700 CheckTest(board, flags, rf, ff, rt, ft,
\r
701 kind == WhiteCapturesEnPassant ||
\r
702 kind == BlackCapturesEnPassant)) return;
\r
703 if (flags & F_ATOMIC_CAPTURE) {
\r
704 if (board[rt][ft] != EmptySquare ||
\r
705 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
\r
707 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
\r
708 if (board[rf][ff] == king) return;
\r
709 for (r = rt-1; r <= rt+1; r++) {
\r
710 for (f = ft-1; f <= ft+1; f++) {
\r
711 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
\r
712 board[r][f] == king) return;
\r
717 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
\r
722 int rf, ff, rt, ft;
\r
724 } LegalityTestClosure;
\r
727 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
\r
728 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
\r
729 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
\r
730 moves that would destroy your own king. The CASTLE_OK flags are
\r
731 true if castling is not yet ruled out by a move of the king or
\r
732 rook. Return TRUE if the player on move is currently in check and
\r
733 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
\r
734 int GenLegal(board, flags, epfile, castlingRights, callback, closure)
\r
738 char castlingRights[];
\r
739 MoveCallback callback;
\r
742 GenLegalClosure cl;
\r
743 int ff, ft, k, left, right;
\r
744 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
\r
745 ChessSquare wKing = WhiteKing, bKing = BlackKing;
\r
749 GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
\r
751 if (!ignoreCheck &&
\r
752 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
\r
754 /* Generate castling moves */
\r
755 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
\r
756 wKing = WhiteUnicorn; bKing = BlackUnicorn;
\r
759 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
\r
760 if ((flags & F_WHITE_ON_MOVE) &&
\r
761 (flags & F_WHITE_KCASTLE_OK) &&
\r
762 board[0][ff] == wKing &&
\r
763 board[0][ff + 1] == EmptySquare &&
\r
764 board[0][ff + 2] == EmptySquare &&
\r
765 board[0][BOARD_RGHT-3] == EmptySquare &&
\r
766 board[0][BOARD_RGHT-2] == EmptySquare &&
\r
767 board[0][BOARD_RGHT-1] == WhiteRook &&
\r
768 castlingRights[0] >= 0 && /* [HGM] check rights */
\r
769 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
771 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
\r
772 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
\r
773 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
\r
774 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
\r
776 callback(board, flags,
\r
777 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
\r
778 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
\r
780 if ((flags & F_WHITE_ON_MOVE) &&
\r
781 (flags & F_WHITE_QCASTLE_OK) &&
\r
782 board[0][ff] == wKing &&
\r
783 board[0][ff - 1] == EmptySquare &&
\r
784 board[0][ff - 2] == EmptySquare &&
\r
785 board[0][BOARD_LEFT+2] == EmptySquare &&
\r
786 board[0][BOARD_LEFT+1] == EmptySquare &&
\r
787 board[0][BOARD_LEFT+0] == WhiteRook &&
\r
788 castlingRights[1] >= 0 && /* [HGM] check rights */
\r
789 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
791 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
\r
792 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
\r
793 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
\r
795 callback(board, flags,
\r
796 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
\r
797 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
799 if (!(flags & F_WHITE_ON_MOVE) &&
\r
800 (flags & F_BLACK_KCASTLE_OK) &&
\r
801 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
802 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
\r
803 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
\r
804 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
\r
805 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
\r
806 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
\r
807 castlingRights[3] >= 0 && /* [HGM] check rights */
\r
808 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
810 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
\r
811 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
\r
812 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
\r
813 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
\r
815 callback(board, flags,
\r
816 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
\r
817 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
\r
819 if (!(flags & F_WHITE_ON_MOVE) &&
\r
820 (flags & F_BLACK_QCASTLE_OK) &&
\r
821 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
822 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
\r
823 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
\r
824 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
\r
825 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
\r
826 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
\r
827 castlingRights[4] >= 0 && /* [HGM] check rights */
\r
828 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
830 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
\r
831 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
\r
832 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
\r
834 callback(board, flags,
\r
835 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
\r
836 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
840 if(gameInfo.variant == VariantFischeRandom) {
\r
842 /* generate all potential FRC castling moves (KxR), ignoring flags */
\r
843 /* [HGM] test if the Rooks we find have castling rights */
\r
846 if ((flags & F_WHITE_ON_MOVE) != 0) {
\r
847 ff = castlingRights[2]; /* King file if we have any rights */
\r
848 if(ff > 0 && board[0][ff] == WhiteKing) {
\r
849 if (appData.debugMode) {
\r
850 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
\r
851 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
\r
853 ft = castlingRights[0]; /* Rook file if we have H-side rights */
\r
855 right = BOARD_RGHT-2;
\r
856 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
857 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
858 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
859 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
860 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
861 if(ft >= 0 && board[0][ft] == WhiteRook)
\r
862 callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
\r
864 ft = castlingRights[1]; /* Rook file if we have A-side rights */
\r
865 left = BOARD_LEFT+2;
\r
867 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
868 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
869 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
870 if(ff > BOARD_LEFT+2)
\r
871 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
872 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
873 if(ft >= 0 && board[0][ft] == WhiteRook)
\r
874 callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
\r
877 ff = castlingRights[5]; /* King file if we have any rights */
\r
878 if(ff > 0 && board[BOARD_HEIGHT-1][ff] == BlackKing) {
\r
879 ft = castlingRights[3]; /* Rook file if we have H-side rights */
\r
881 right = BOARD_RGHT-2;
\r
882 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
883 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
884 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
885 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
886 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
887 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
\r
888 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
890 ft = castlingRights[4]; /* Rook file if we have A-side rights */
\r
891 left = BOARD_LEFT+2;
\r
893 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
894 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
895 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
896 if(ff > BOARD_LEFT+2)
\r
897 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
898 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
899 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
\r
900 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
913 } CheckTestClosure;
\r
916 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
\r
917 int rf, int ff, int rt, int ft,
\r
918 VOIDSTAR closure));
\r
921 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
925 int rf, ff, rt, ft;
\r
928 register CheckTestClosure *cl = (CheckTestClosure *) closure;
\r
930 if (rt == cl->rking && ft == cl->fking) cl->check++;
\r
934 /* If the player on move were to move from (rf, ff) to (rt, ft), would
\r
935 he leave himself in check? Or if rf == -1, is the player on move
\r
936 in check now? enPassant must be TRUE if the indicated move is an
\r
937 e.p. capture. The possibility of castling out of a check along the
\r
938 back rank is not accounted for (i.e., we still return nonzero), as
\r
939 this is illegal anyway. Return value is the number of times the
\r
940 king is in check. */
\r
941 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
\r
944 int rf, ff, rt, ft, enPassant;
\r
946 CheckTestClosure cl;
\r
947 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
\r
948 ChessSquare captured = EmptySquare;
\r
949 /* Suppress warnings on uninitialized variables */
\r
951 if(gameInfo.variant == VariantXiangqi)
\r
952 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
\r
953 if(gameInfo.variant == VariantKnightmate)
\r
954 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
\r
958 captured = board[rf][ft];
\r
959 board[rf][ft] = EmptySquare;
\r
961 captured = board[rt][ft];
\r
963 board[rt][ft] = board[rf][ff];
\r
964 board[rf][ff] = EmptySquare;
\r
967 /* For compatibility with ICS wild 9, we scan the board in the
\r
968 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
\r
969 and we test only whether that one is in check. */
\r
971 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
\r
972 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
\r
973 if (board[cl.rking][cl.fking] == king) {
\r
974 if(gameInfo.variant == VariantXiangqi) {
\r
975 /* [HGM] In Xiangqi opposing Kings means check as well */
\r
977 dir = (king >= BlackPawn) ? -1 : 1;
\r
978 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
\r
979 board[i][cl.fking] == EmptySquare; i+=dir );
\r
980 if(i>=0 && i<BOARD_HEIGHT &&
\r
981 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
\r
985 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
\r
986 CheckTestCallback, (VOIDSTAR) &cl);
\r
987 goto undo_move; /* 2-level break */
\r
994 board[rf][ff] = board[rt][ft];
\r
996 board[rf][ft] = captured;
\r
997 board[rt][ft] = EmptySquare;
\r
999 board[rt][ft] = captured;
\r
1007 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
\r
1008 int rf, int ff, int rt, int ft,
\r
1009 VOIDSTAR closure));
\r
1011 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1015 int rf, ff, rt, ft;
\r
1018 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
\r
1020 // if (appData.debugMode) {
\r
1021 // fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
1023 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
\r
1027 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
\r
1029 int flags, epfile;
\r
1030 int rf, ff, rt, ft, promoChar;
\r
1031 char castlingRights[];
\r
1033 LegalityTestClosure cl; ChessSquare piece = board[rf][ff];
\r
1035 if (appData.debugMode) {
\r
1037 for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
\r
1038 fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
1040 /* [HGM] Lance, Cobra and Falcon are wildcard pieces; consider all their moves legal */
\r
1041 /* (perhaps we should disallow moves that obviously leave us in check?) */
\r
1042 if(piece == WhiteFalcon || piece == BlackFalcon ||
\r
1043 piece == WhiteCobra || piece == BlackCobra ||
\r
1044 piece == WhiteLance || piece == BlackLance)
\r
1045 return NormalMove;
\r
1051 cl.kind = IllegalMove;
\r
1052 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
\r
1054 if(gameInfo.variant == VariantShogi) {
\r
1055 /* [HGM] Shogi promotions. '=' means defer */
\r
1056 if(rf != DROP_RANK && cl.kind == NormalMove) {
\r
1057 ChessSquare piece = board[rf][ff];
\r
1059 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
\r
1060 if(promoChar != NULLCHAR && promoChar != 'x' &&
\r
1061 promoChar != '+' && promoChar != '=' &&
\r
1062 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
\r
1063 cl.kind = IllegalMove;
\r
1064 else if(flags & F_WHITE_ON_MOVE) {
\r
1065 if( (int) piece < (int) WhiteWazir &&
\r
1066 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1067 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1068 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1069 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1070 else /* promotion optional, default is promote */
\r
1071 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
\r
1073 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1074 NormalMove : IllegalMove;
\r
1076 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1077 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1078 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1079 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1080 else /* promotion optional, default is promote */
\r
1081 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
\r
1083 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1084 NormalMove : IllegalMove;
\r
1088 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1089 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1091 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
\r
1093 cl.kind = IllegalMove;
\r
1096 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
\r
1102 } MateTestClosure;
\r
1104 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
\r
1105 int rf, int ff, int rt, int ft,
\r
1106 VOIDSTAR closure));
\r
1108 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1112 int rf, ff, rt, ft;
\r
1115 register MateTestClosure *cl = (MateTestClosure *) closure;
\r
1120 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
\r
1121 int MateTest(board, flags, epfile, castlingRights)
\r
1123 int flags, epfile;
\r
1124 char castlingRights[];
\r
1126 MateTestClosure cl;
\r
1130 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
\r
1131 if (cl.count > 0) {
\r
1132 return inCheck ? MT_CHECK : MT_NONE;
\r
1134 return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ?
\r
1135 MT_CHECKMATE : MT_STALEMATE;
\r
1140 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
\r
1141 int rf, int ff, int rt, int ft,
\r
1142 VOIDSTAR closure));
\r
1144 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1148 int rf, ff, rt, ft;
\r
1151 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
\r
1153 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
\r
1154 || PieceToChar(board[rf][ff]) == '~'
\r
1155 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
\r
1157 (cl->rfIn == -1 || cl->rfIn == rf) &&
\r
1158 (cl->ffIn == -1 || cl->ffIn == ff) &&
\r
1159 (cl->rtIn == -1 || cl->rtIn == rt) &&
\r
1160 (cl->ftIn == -1 || cl->ftIn == ft)) {
\r
1163 cl->piece = board[rf][ff];
\r
1172 void Disambiguate(board, flags, epfile, closure)
\r
1174 int flags, epfile;
\r
1175 DisambiguateClosure *closure;
\r
1177 int illegal = 0; char c = closure->promoCharIn;
\r
1178 closure->count = 0;
\r
1179 closure->rf = closure->ff = closure->rt = closure->ft = 0;
\r
1180 closure->kind = ImpossibleMove;
\r
1181 if (appData.debugMode) {
\r
1182 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1183 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1184 closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
\r
1186 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
\r
1187 if (closure->count == 0) {
\r
1188 /* See if it's an illegal move due to check */
\r
1190 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
\r
1191 (VOIDSTAR) closure);
\r
1192 if (closure->count == 0) {
\r
1193 /* No, it's not even that */
\r
1194 if (appData.debugMode) { int i, j;
\r
1195 for(i=BOARD_HEIGHT-1; i>=0; i--) {
\r
1196 for(j=0; j<BOARD_WIDTH; j++)
\r
1197 fprintf(debugFP, "%3d", (int) board[i][j]);
\r
1198 fprintf(debugFP, "\n");
\r
1205 if(gameInfo.variant == VariantShogi) {
\r
1206 /* [HGM] Shogi promotions. '=' means defer */
\r
1207 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
\r
1208 ChessSquare piece = closure->piece;
\r
1210 if (appData.debugMode) {
\r
1211 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1212 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1213 closure->promoCharIn,closure->promoCharIn);
\r
1216 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
\r
1217 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
\r
1218 closure->kind = IllegalMove;
\r
1219 else if(flags & F_WHITE_ON_MOVE) {
\r
1221 if (appData.debugMode) {
\r
1222 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1223 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1224 closure->promoCharIn,closure->promoCharIn);
\r
1227 if( (int) piece < (int) WhiteWazir &&
\r
1228 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
\r
1229 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
\r
1230 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1231 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
\r
1232 else /* promotion optional, default is promote */
\r
1233 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
\r
1235 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1236 NormalMove : IllegalMove;
\r
1238 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
\r
1239 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
\r
1240 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
\r
1241 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
\r
1242 else /* promotion optional, default is promote */
\r
1243 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
\r
1245 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1246 NormalMove : IllegalMove;
\r
1250 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
\r
1251 if (closure->kind == WhitePromotionQueen
\r
1252 || closure->kind == BlackPromotionQueen) {
\r
1254 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
\r
1255 closure->promoCharIn);
\r
1257 closure->kind = IllegalMove;
\r
1261 if (appData.debugMode) {
\r
1262 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1263 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1264 closure->promoCharIn,closure->promoCharIn);
\r
1267 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
\r
1268 if(closure->promoCharIn != '=')
\r
1269 closure->promoChar = ToLower(closure->promoCharIn);
\r
1270 else closure->promoChar = '=';
\r
1271 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
\r
1272 if (closure->count > 1) {
\r
1273 closure->kind = AmbiguousMove;
\r
1276 /* Note: If more than one illegal move matches, but no legal
\r
1277 moves, we return IllegalMove, not AmbiguousMove. Caller
\r
1278 can look at closure->count to detect this.
\r
1280 closure->kind = IllegalMove;
\r
1282 if(closure->kind == IllegalMove)
\r
1283 /* [HGM] might be a variant we don't understand, pass on promotion info */
\r
1284 closure->promoChar = ToLower(closure->promoCharIn);
\r
1285 if (appData.debugMode) {
\r
1286 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1287 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
\r
1288 closure->promoChar >= ' ' ? closure->promoChar:'-');
\r
1295 ChessSquare piece;
\r
1296 int rf, ff, rt, ft;
\r
1302 } CoordsToAlgebraicClosure;
\r
1304 extern void CoordsToAlgebraicCallback P((Board board, int flags,
\r
1305 ChessMove kind, int rf, int ff,
\r
1306 int rt, int ft, VOIDSTAR closure));
\r
1308 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1312 int rf, ff, rt, ft;
\r
1315 register CoordsToAlgebraicClosure *cl =
\r
1316 (CoordsToAlgebraicClosure *) closure;
\r
1318 if (rt == cl->rt && ft == cl->ft &&
\r
1319 (board[rf][ff] == cl->piece
\r
1320 || PieceToChar(board[rf][ff]) == '~' &&
\r
1321 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
\r
1323 if (rf == cl->rf) {
\r
1324 if (ff == cl->ff) {
\r
1325 cl->kind = kind; /* this is the move we want */
\r
1327 cl->file++; /* need file to rule out this move */
\r
1330 if (ff == cl->ff) {
\r
1331 cl->rank++; /* need rank to rule out this move */
\r
1333 cl->either++; /* rank or file will rule out this move */
\r
1339 /* Convert coordinates to normal algebraic notation.
\r
1340 promoChar must be NULLCHAR or 'x' if not a promotion.
\r
1342 ChessMove CoordsToAlgebraic(board, flags, epfile,
\r
1343 rf, ff, rt, ft, promoChar, out)
\r
1345 int flags, epfile;
\r
1346 int rf, ff, rt, ft;
\r
1348 char out[MOVE_LEN];
\r
1350 ChessSquare piece;
\r
1352 char *outp = out, c;
\r
1353 CoordsToAlgebraicClosure cl;
\r
1355 if (rf == DROP_RANK) {
\r
1356 /* Bughouse piece drop */
\r
1357 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
\r
1359 *outp++ = ft + AAA;
\r
1361 *outp++ = rt + ONE;
\r
1362 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1364 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
\r
1367 if (promoChar == 'x') promoChar = NULLCHAR;
\r
1368 piece = board[rf][ff];
\r
1369 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
\r
1371 if (appData.debugMode)
\r
1372 fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
\r
1376 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
\r
1377 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1378 /* Keep short notation if move is illegal only because it
\r
1379 leaves the player in check, but still return IllegalMove */
\r
1380 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1381 rf, ff, rt, ft, promoChar);
\r
1382 if (kind == IllegalMove) break;
\r
1383 kind = IllegalMove;
\r
1386 *outp++ = ff + AAA;
\r
1387 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
\r
1388 /* Non-capture; use style "e5" */
\r
1390 *outp++ = rt + ONE;
\r
1391 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1393 /* Capture; use style "exd5" */
\r
1394 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
\r
1395 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
\r
1396 *outp++ = ft + AAA;
\r
1398 *outp++ = rt + ONE;
\r
1399 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1401 /* Use promotion suffix style "=Q" */
\r
1403 if (appData.debugMode)
\r
1404 fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
\r
1405 if (promoChar != NULLCHAR) {
\r
1406 if(gameInfo.variant == VariantShogi) {
\r
1407 /* [HGM] ... but not in Shogi! */
\r
1408 *outp++ = promoChar == '=' ? '=' : '+';
\r
1411 *outp++ = ToUpper(promoChar);
\r
1420 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
\r
1421 /* Code added by Tord: FRC castling. */
\r
1422 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
\r
1423 (piece == BlackKing && board[rt][ft] == BlackRook)) {
\r
1424 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
\r
1425 return LegalityTest(board, flags, epfile, initialRights,
\r
1426 rf, ff, rt, ft, promoChar);
\r
1428 /* End of code added by Tord */
\r
1429 /* Test for castling or ICS wild castling */
\r
1430 /* Use style "O-O" (oh-oh) for PGN compatibility */
\r
1431 else if (rf == rt &&
\r
1432 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
\r
1433 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
\r
1434 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
\r
1435 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
\r
1436 strcpy(out, "O-O");
\r
1438 strcpy(out, "O-O-O");
\r
1440 /* This notation is always unambiguous, unless there are
\r
1441 kings on both the d and e files, with "wild castling"
\r
1442 possible for the king on the d file and normal castling
\r
1443 possible for the other. ICS rules for wild 9
\r
1444 effectively make castling illegal for either king in
\r
1445 this situation. So I am not going to worry about it;
\r
1446 I'll just generate an ambiguous O-O in this case.
\r
1448 return LegalityTest(board, flags, epfile, initialRights,
\r
1449 rf, ff, rt, ft, promoChar);
\r
1452 /* else fall through */
\r
1460 cl.kind = IllegalMove;
\r
1461 cl.rank = cl.file = cl.either = 0;
\r
1462 GenLegal(board, flags, epfile, initialRights,
\r
1463 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1465 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1466 /* Generate pretty moves for moving into check, but
\r
1467 still return IllegalMove.
\r
1469 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1470 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1471 if (cl.kind == IllegalMove) break;
\r
1472 cl.kind = IllegalMove;
\r
1475 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
\r
1476 else "Ngf3" or "Ngxf7",
\r
1477 else "N1f3" or "N5xf7",
\r
1478 else "Ng1f3" or "Ng5xf7".
\r
1480 c = PieceToChar(piece) ;
\r
1481 if( c == '~' || c == '+') {
\r
1482 /* [HGM] print nonexistent piece as its demoted version */
\r
1483 piece = (ChessSquare) (DEMOTED piece);
\r
1485 if(c=='+') *outp++ = c;
\r
1486 *outp++ = ToUpper(PieceToChar(piece));
\r
1488 if (cl.file || (cl.either && !cl.rank)) {
\r
1489 *outp++ = ff + AAA;
\r
1493 *outp++ = rf + ONE;
\r
1494 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1497 if(board[rt][ft] != EmptySquare)
\r
1500 *outp++ = ft + AAA;
\r
1502 *outp++ = rt + ONE;
\r
1503 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1505 if (gameInfo.variant == VariantShogi) {
\r
1506 /* [HGM] in Shogi non-pawns can promote */
\r
1507 if(flags & F_WHITE_ON_MOVE) {
\r
1508 if( (int) cl.piece < (int) WhiteWazir &&
\r
1509 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1510 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1511 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1512 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1513 else cl.kind = WhitePromotionQueen; /* promotion optional */
\r
1515 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1516 NormalMove : IllegalMove;
\r
1518 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1519 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1520 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1521 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1522 else cl.kind = BlackPromotionQueen; /* promotion optional */
\r
1523 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1524 NormalMove : IllegalMove;
\r
1526 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1527 /* for optional promotions append '+' or '=' */
\r
1528 if(promoChar == '=') {
\r
1530 cl.kind = NormalMove;
\r
1531 } else *outp++ = '+';
\r
1533 } else if(cl.kind == IllegalMove) {
\r
1534 /* Illegal move specifies any given promotion */
\r
1535 if(promoChar != NULLCHAR && promoChar != 'x') {
\r
1537 *outp++ = ToUpper(promoChar);
\r
1544 /* [HGM] Always long notation for fairies we don't know */
\r
1549 case WhiteGrasshopper:
\r
1550 case BlackGrasshopper:
\r
1552 /* Moving a nonexistent piece */
\r
1556 /* Not a legal move, even ignoring check.
\r
1557 If there was a piece on the from square,
\r
1558 use style "Ng1g3" or "Ng1xe8";
\r
1559 if there was a pawn or nothing (!),
\r
1560 use style "g1g3" or "g1xe8". Use "x"
\r
1561 if a piece was on the to square, even
\r
1562 a piece of the same color.
\r
1565 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
\r
1566 *outp++ = ToUpper(PieceToChar(piece));
\r
1568 *outp++ = ff + AAA;
\r
1570 *outp++ = rf + ONE;
\r
1571 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1572 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
\r
1573 *outp++ = ft + AAA;
\r
1575 *outp++ = rt + ONE;
\r
1576 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1577 /* Use promotion suffix style "=Q" */
\r
1578 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1580 *outp++ = ToUpper(promoChar);
\r
1584 return IllegalMove;
\r