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
176 return WhitePromotionQueen;
\r
178 return WhitePromotionRook;
\r
180 return WhitePromotionBishop;
\r
182 return WhitePromotionKnight;
\r
184 return WhitePromotionKing;
\r
186 return WhitePromotionArchbishop;
\r
187 case WhiteMarshall:
\r
188 return WhitePromotionChancellor;
\r
190 return WhitePromotionCentaur;
\r
192 return BlackPromotionQueen;
\r
194 return BlackPromotionRook;
\r
196 return BlackPromotionBishop;
\r
198 return BlackPromotionKnight;
\r
200 return BlackPromotionKing;
\r
202 return BlackPromotionArchbishop;
\r
203 case BlackMarshall:
\r
204 return BlackPromotionChancellor;
\r
206 return BlackPromotionCentaur;
\r
208 // not all promotion implemented yet! Take Queen for those we don't know.
\r
209 return (whiteOnMove ? WhitePromotionQueen : BlackPromotionQueen);
\r
213 void CopyBoard(to, from)
\r
218 for (i = 0; i < BOARD_HEIGHT; i++)
\r
219 for (j = 0; j < BOARD_WIDTH; j++)
\r
220 to[i][j] = from[i][j];
\r
223 int CompareBoards(board1, board2)
\r
224 Board board1, board2;
\r
228 for (i = 0; i < BOARD_HEIGHT; i++)
\r
229 for (j = 0; j < BOARD_WIDTH; j++) {
\r
230 if (board1[i][j] != board2[i][j])
\r
237 /* Call callback once for each pseudo-legal move in the given
\r
238 position, except castling moves. A move is pseudo-legal if it is
\r
239 legal, or if it would be legal except that it leaves the king in
\r
240 check. In the arguments, epfile is EP_NONE if the previous move
\r
241 was not a double pawn push, or the file 0..7 if it was, or
\r
242 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
\r
243 Promotion moves generated are to Queen only.
\r
245 void GenPseudoLegal(board, flags, epfile, callback, closure)
\r
249 MoveCallback callback;
\r
253 int i, j, d, s, fs, rs, rt, ft, m;
\r
255 for (rf = 0; rf < BOARD_HEIGHT; rf++)
\r
256 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
\r
259 if (flags & F_WHITE_ON_MOVE) {
\r
260 if (!WhitePiece(board[rf][ff])) continue;
\r
262 if (!BlackPiece(board[rf][ff])) continue;
\r
264 m = 0; piece = board[rf][ff];
\r
265 if(PieceToChar(piece) == '~')
\r
266 piece = (ChessSquare) ( DEMOTED piece );
\r
267 if(gameInfo.variant == VariantShogi)
\r
268 piece = (ChessSquare) ( SHOGI piece );
\r
271 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
\r
273 /* can't happen ([HGM] except for faries...) */
\r
277 if(gameInfo.variant == VariantXiangqi) {
\r
278 /* [HGM] capture and move straight ahead in Xiangqi */
\r
279 if (rf < BOARD_HEIGHT-1 &&
\r
280 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
\r
281 callback(board, flags, NormalMove,
\r
282 rf, ff, rf + 1, ff, closure);
\r
284 /* and move sideways when across the river */
\r
285 for (s = -1; s <= 1; s += 2) {
\r
286 if (rf >= BOARD_HEIGHT>>1 &&
\r
287 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
288 !WhitePiece(board[rf][ff+s]) ) {
\r
289 callback(board, flags, NormalMove,
\r
290 rf, ff, rf, ff+s, closure);
\r
295 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
\r
296 callback(board, flags,
\r
297 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
298 rf, ff, rf + 1, ff, closure);
\r
300 if (rf == 1 && board[2][ff] == EmptySquare &&
\r
301 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
302 gameInfo.variant != VariantCourier && /* [HGM] */
\r
303 board[3][ff] == EmptySquare ) {
\r
304 callback(board, flags, NormalMove,
\r
305 rf, ff, 3, ff, closure);
\r
307 for (s = -1; s <= 1; s += 2) {
\r
308 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
309 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
310 BlackPiece(board[rf + 1][ff + s]))) {
\r
311 callback(board, flags,
\r
312 rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
\r
313 rf, ff, rf + 1, ff + s, closure);
\r
315 if (rf == BOARD_HEIGHT-4) {
\r
316 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
317 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
318 board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
\r
319 board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
\r
320 callback(board, flags, WhiteCapturesEnPassant,
\r
321 rf, ff, 5, ff + s, closure);
\r
328 if(gameInfo.variant == VariantXiangqi) {
\r
329 /* [HGM] capture straight ahead in Xiangqi */
\r
330 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
\r
331 callback(board, flags, NormalMove,
\r
332 rf, ff, rf - 1, ff, closure);
\r
334 /* and move sideways when across the river */
\r
335 for (s = -1; s <= 1; s += 2) {
\r
336 if (rf < BOARD_HEIGHT>>1 &&
\r
337 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
338 !BlackPiece(board[rf][ff+s]) ) {
\r
339 callback(board, flags, NormalMove,
\r
340 rf, ff, rf, ff+s, closure);
\r
345 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
\r
346 callback(board, flags,
\r
347 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
348 rf, ff, rf - 1, ff, closure);
\r
350 if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
\r
351 gameInfo.variant != VariantShatranj && /* [HGM] */
\r
352 gameInfo.variant != VariantCourier && /* [HGM] */
\r
353 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
\r
354 callback(board, flags, NormalMove,
\r
355 rf, ff, BOARD_HEIGHT-4, ff, closure);
\r
357 for (s = -1; s <= 1; s += 2) {
\r
358 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
359 ((flags & F_KRIEGSPIEL_CAPTURE) ||
\r
360 WhitePiece(board[rf - 1][ff + s]))) {
\r
361 callback(board, flags,
\r
362 rf == 1 ? BlackPromotionQueen : NormalMove,
\r
363 rf, ff, rf - 1, ff + s, closure);
\r
366 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
367 (epfile == ff + s || epfile == EP_UNKNOWN) &&
\r
368 board[3][ff + s] == WhitePawn &&
\r
369 board[2][ff + s] == EmptySquare) {
\r
370 callback(board, flags, BlackCapturesEnPassant,
\r
371 rf, ff, 2, ff + s, closure);
\r
382 for (i = -1; i <= 1; i += 2)
\r
383 for (j = -1; j <= 1; j += 2)
\r
384 for (s = 1; s <= 2; s++) {
\r
387 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
388 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
\r
389 && !SameColor(board[rf][ff], board[rt][ft]))
\r
390 callback(board, flags, NormalMove,
\r
391 rf, ff, rt, ft, closure);
\r
395 case SHOGI WhiteKnight:
\r
396 for (s = -1; s <= 1; s += 2) {
\r
397 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
398 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
\r
399 callback(board, flags, NormalMove,
\r
400 rf, ff, rf + 2, ff + s, closure);
\r
405 case SHOGI BlackKnight:
\r
406 for (s = -1; s <= 1; s += 2) {
\r
407 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
408 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
\r
409 callback(board, flags, NormalMove,
\r
410 rf, ff, rf - 2, ff + s, closure);
\r
417 for (d = 0; d <= 1; d++)
\r
418 for (s = -1; s <= 1; s += 2) {
\r
420 for (i = 1;; i++) {
\r
421 rt = rf + (i * s) * d;
\r
422 ft = ff + (i * s) * (1 - d);
\r
423 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
424 if (m == 0 && board[rt][ft] == EmptySquare)
\r
425 callback(board, flags, NormalMove,
\r
426 rf, ff, rt, ft, closure);
\r
427 if (m == 1 && board[rt][ft] != EmptySquare &&
\r
428 !SameColor(board[rf][ff], board[rt][ft]) )
\r
429 callback(board, flags, NormalMove,
\r
430 rf, ff, rt, ft, closure);
\r
431 if (board[rt][ft] != EmptySquare && m++) break;
\r
436 /* Gold General (and all its promoted versions) . First do the */
\r
437 /* diagonal forward steps, then proceed as normal Wazir */
\r
438 case SHOGI WhiteWazir:
\r
439 case SHOGI (PROMOTED WhitePawn):
\r
440 case SHOGI (PROMOTED WhiteKnight):
\r
441 case SHOGI (PROMOTED WhiteQueen):
\r
442 case SHOGI (PROMOTED WhiteFerz):
\r
443 for (s = -1; s <= 1; s += 2) {
\r
444 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
445 !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
\r
446 callback(board, flags, NormalMove,
\r
447 rf, ff, rf + 1, ff + s, closure);
\r
452 case SHOGI BlackWazir:
\r
453 case SHOGI (PROMOTED BlackPawn):
\r
454 case SHOGI (PROMOTED BlackKnight):
\r
455 case SHOGI (PROMOTED BlackQueen):
\r
456 case SHOGI (PROMOTED BlackFerz):
\r
457 for (s = -1; s <= 1; s += 2) {
\r
458 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
\r
459 !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
\r
460 callback(board, flags, NormalMove,
\r
461 rf, ff, rf - 1, ff + s, closure);
\r
468 for (d = 0; d <= 1; d++)
\r
469 for (s = -1; s <= 1; s += 2) {
\r
471 ft = ff + s * (1 - d);
\r
472 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
473 && !SameColor(board[rf][ff], board[rt][ft]) &&
\r
474 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
475 callback(board, flags, NormalMove,
\r
476 rf, ff, rt, ft, closure);
\r
482 /* [HGM] support Shatranj pieces */
\r
483 for (rs = -1; rs <= 1; rs += 2)
\r
484 for (fs = -1; fs <= 1; fs += 2) {
\r
487 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
\r
488 && ( gameInfo.variant != VariantXiangqi ||
\r
489 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
\r
491 && !SameColor(board[rf][ff], board[rt][ft]))
\r
492 callback(board, flags, NormalMove,
\r
493 rf, ff, rt, ft, closure);
\r
497 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
\r
498 case SHOGI WhiteCardinal:
\r
499 case SHOGI BlackCardinal:
\r
502 /* Capablanca Archbishop continues as Knight */
\r
507 /* Shogi Bishops are ordinary Bishops */
\r
508 case SHOGI WhiteBishop:
\r
509 case SHOGI BlackBishop:
\r
512 for (rs = -1; rs <= 1; rs += 2)
\r
513 for (fs = -1; fs <= 1; fs += 2)
\r
514 for (i = 1;; i++) {
\r
515 rt = rf + (i * rs);
\r
516 ft = ff + (i * fs);
\r
517 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
518 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
519 callback(board, flags, NormalMove,
\r
520 rf, ff, rt, ft, closure);
\r
521 if (board[rt][ft] != EmptySquare) break;
\r
523 if(m==1) goto mounted;
\r
524 if(m==2) goto finishGold;
\r
525 /* Bishop falls through */
\r
528 /* Shogi Lance is unlike anything, and asymmetric at that */
\r
529 case SHOGI WhiteQueen:
\r
533 if (rt >= BOARD_HEIGHT) break;
\r
534 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
535 callback(board, flags, NormalMove,
\r
536 rf, ff, rt, ft, closure);
\r
537 if (board[rt][ft] != EmptySquare) break;
\r
541 case SHOGI BlackQueen:
\r
546 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
547 callback(board, flags, NormalMove,
\r
548 rf, ff, rt, ft, closure);
\r
549 if (board[rt][ft] != EmptySquare) break;
\r
553 /* Shogi Dragon King has to continue as Ferz after Rook moves */
\r
554 case SHOGI WhiteDragon:
\r
555 case SHOGI BlackDragon:
\r
558 /* Capablanca Chancellor sets flag to continue as Knight */
\r
559 case WhiteMarshall:
\r
560 case BlackMarshall:
\r
563 /* Shogi Rooks are ordinary Rooks */
\r
564 case SHOGI WhiteRook:
\r
565 case SHOGI BlackRook:
\r
568 for (d = 0; d <= 1; d++)
\r
569 for (s = -1; s <= 1; s += 2)
\r
570 for (i = 1;; i++) {
\r
571 rt = rf + (i * s) * d;
\r
572 ft = ff + (i * s) * (1 - d);
\r
573 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
574 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
575 callback(board, flags, NormalMove,
\r
576 rf, ff, rt, ft, closure);
\r
577 if (board[rt][ft] != EmptySquare) break;
\r
579 if(m==1) goto mounted;
\r
580 if(m==2) goto walking;
\r
585 for (rs = -1; rs <= 1; rs++)
\r
586 for (fs = -1; fs <= 1; fs++) {
\r
587 if (rs == 0 && fs == 0) continue;
\r
588 for (i = 1;; i++) {
\r
589 rt = rf + (i * rs);
\r
590 ft = ff + (i * fs);
\r
591 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
592 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
593 callback(board, flags, NormalMove,
\r
594 rf, ff, rt, ft, closure);
\r
595 if (board[rt][ft] != EmptySquare) break;
\r
600 /* Shogi Pawn and Silver General: first the Pawn move, */
\r
601 /* then the General continues like a Ferz */
\r
602 case SHOGI WhitePawn:
\r
603 case SHOGI WhiteFerz:
\r
604 if (rf < BOARD_HEIGHT-1 &&
\r
605 !SameColor(board[rf][ff], board[rf + 1][ff]) )
\r
606 callback(board, flags, NormalMove,
\r
607 rf, ff, rf + 1, ff, closure);
\r
608 if(piece != SHOGI WhitePawn) goto finishSilver;
\r
611 case SHOGI BlackPawn:
\r
612 case SHOGI BlackFerz:
\r
614 !SameColor(board[rf][ff], board[rf - 1][ff]) )
\r
615 callback(board, flags, NormalMove,
\r
616 rf, ff, rf - 1, ff, closure);
\r
617 if(piece == SHOGI BlackPawn) break;
\r
622 /* [HGM] support Shatranj pieces */
\r
623 for (rs = -1; rs <= 1; rs += 2)
\r
624 for (fs = -1; fs <= 1; fs += 2) {
\r
627 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
628 if (!SameColor(board[rf][ff], board[rt][ft]) &&
\r
629 (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
\r
630 callback(board, flags, NormalMove,
\r
631 rf, ff, rt, ft, closure);
\r
637 m++; // [HGM] superchess: use for Centaur
\r
640 case SHOGI WhiteKing:
\r
641 case SHOGI BlackKing:
\r
645 for (i = -1; i <= 1; i++)
\r
646 for (j = -1; j <= 1; j++) {
\r
647 if (i == 0 && j == 0) continue;
\r
650 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
\r
651 if (SameColor(board[rf][ff], board[rt][ft])) continue;
\r
652 callback(board, flags, NormalMove,
\r
653 rf, ff, rt, ft, closure);
\r
655 if(m==1) goto mounted;
\r
658 case WhiteNightrider:
\r
659 case BlackNightrider:
\r
660 for (i = -1; i <= 1; i += 2)
\r
661 for (j = -1; j <= 1; j += 2)
\r
662 for (s = 1; s <= 2; s++) { int k;
\r
665 ft = ff + k*j*(3-s);
\r
666 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
\r
667 if (SameColor(board[rf][ff], board[rt][ft])) break;
\r
668 callback(board, flags, NormalMove,
\r
669 rf, ff, rt, ft, closure);
\r
670 if (board[rt][ft] != EmptySquare) break;
\r
685 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
\r
686 int rf, int ff, int rt, int ft,
\r
687 VOIDSTAR closure));
\r
689 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
693 int rf, ff, rt, ft;
\r
696 register GenLegalClosure *cl = (GenLegalClosure *) closure;
\r
698 if (!(flags & F_IGNORE_CHECK) &&
\r
699 CheckTest(board, flags, rf, ff, rt, ft,
\r
700 kind == WhiteCapturesEnPassant ||
\r
701 kind == BlackCapturesEnPassant)) return;
\r
702 if (flags & F_ATOMIC_CAPTURE) {
\r
703 if (board[rt][ft] != EmptySquare ||
\r
704 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
\r
706 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
\r
707 if (board[rf][ff] == king) return;
\r
708 for (r = rt-1; r <= rt+1; r++) {
\r
709 for (f = ft-1; f <= ft+1; f++) {
\r
710 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
\r
711 board[r][f] == king) return;
\r
716 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
\r
721 int rf, ff, rt, ft;
\r
723 } LegalityTestClosure;
\r
726 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
\r
727 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
\r
728 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
\r
729 moves that would destroy your own king. The CASTLE_OK flags are
\r
730 true if castling is not yet ruled out by a move of the king or
\r
731 rook. Return TRUE if the player on move is currently in check and
\r
732 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
\r
733 int GenLegal(board, flags, epfile, castlingRights, callback, closure)
\r
737 char castlingRights[];
\r
738 MoveCallback callback;
\r
741 GenLegalClosure cl;
\r
742 int ff, ft, k, left, right;
\r
743 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
\r
744 ChessSquare wKing = WhiteKing, bKing = BlackKing;
\r
748 GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
\r
750 if (!ignoreCheck &&
\r
751 CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
\r
753 /* Generate castling moves */
\r
754 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
\r
755 wKing = WhiteUnicorn; bKing = BlackUnicorn;
\r
758 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
\r
759 if ((flags & F_WHITE_ON_MOVE) &&
\r
760 (flags & F_WHITE_KCASTLE_OK) &&
\r
761 board[0][ff] == wKing &&
\r
762 board[0][ff + 1] == EmptySquare &&
\r
763 board[0][ff + 2] == EmptySquare &&
\r
764 board[0][BOARD_RGHT-3] == EmptySquare &&
\r
765 board[0][BOARD_RGHT-2] == EmptySquare &&
\r
766 board[0][BOARD_RGHT-1] == WhiteRook &&
\r
767 castlingRights[0] >= 0 && /* [HGM] check rights */
\r
768 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
770 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
\r
771 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
\r
772 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
\r
773 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
\r
775 callback(board, flags,
\r
776 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
\r
777 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
\r
779 if ((flags & F_WHITE_ON_MOVE) &&
\r
780 (flags & F_WHITE_QCASTLE_OK) &&
\r
781 board[0][ff] == wKing &&
\r
782 board[0][ff - 1] == EmptySquare &&
\r
783 board[0][ff - 2] == EmptySquare &&
\r
784 board[0][BOARD_LEFT+2] == EmptySquare &&
\r
785 board[0][BOARD_LEFT+1] == EmptySquare &&
\r
786 board[0][BOARD_LEFT+0] == WhiteRook &&
\r
787 castlingRights[1] >= 0 && /* [HGM] check rights */
\r
788 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
\r
790 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
\r
791 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
\r
792 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
\r
794 callback(board, flags,
\r
795 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
\r
796 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
798 if (!(flags & F_WHITE_ON_MOVE) &&
\r
799 (flags & F_BLACK_KCASTLE_OK) &&
\r
800 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
801 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
\r
802 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
\r
803 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
\r
804 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
\r
805 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
\r
806 castlingRights[3] >= 0 && /* [HGM] check rights */
\r
807 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
809 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
\r
810 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
\r
811 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
\r
812 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
\r
814 callback(board, flags,
\r
815 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
\r
816 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
\r
818 if (!(flags & F_WHITE_ON_MOVE) &&
\r
819 (flags & F_BLACK_QCASTLE_OK) &&
\r
820 board[BOARD_HEIGHT-1][ff] == bKing &&
\r
821 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
\r
822 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
\r
823 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
\r
824 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
\r
825 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
\r
826 castlingRights[4] >= 0 && /* [HGM] check rights */
\r
827 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
\r
829 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
\r
830 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
\r
831 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
\r
833 callback(board, flags,
\r
834 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
\r
835 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
\r
839 if(gameInfo.variant == VariantFischeRandom) {
\r
841 /* generate all potential FRC castling moves (KxR), ignoring flags */
\r
842 /* [HGM] test if the Rooks we find have castling rights */
\r
845 if ((flags & F_WHITE_ON_MOVE) != 0) {
\r
846 ff = castlingRights[2]; /* King file if we have any rights */
\r
847 if(ff > 0 && board[0][ff] == WhiteKing) {
\r
848 if (appData.debugMode) {
\r
849 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
\r
850 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
\r
852 ft = castlingRights[0]; /* Rook file if we have H-side rights */
\r
854 right = BOARD_RGHT-2;
\r
855 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
856 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
857 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
858 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
859 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
860 if(ft >= 0 && board[0][ft] == WhiteRook)
\r
861 callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
\r
863 ft = castlingRights[1]; /* Rook file if we have A-side rights */
\r
864 left = BOARD_LEFT+2;
\r
866 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
867 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
868 if(k != ft && board[0][k] != EmptySquare) ft = -1;
\r
869 if(ff > BOARD_LEFT+2)
\r
870 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
871 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
\r
872 if(ft >= 0 && board[0][ft] == WhiteRook)
\r
873 callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
\r
876 ff = castlingRights[5]; /* King file if we have any rights */
\r
877 if(ff > 0 && board[BOARD_HEIGHT-1][ff] == BlackKing) {
\r
878 ft = castlingRights[3]; /* Rook file if we have H-side rights */
\r
880 right = BOARD_RGHT-2;
\r
881 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
\r
882 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
883 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
884 for(k=left; k<right && ft >= 0; k++) /* then if not checked */
\r
885 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
886 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
\r
887 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
889 ft = castlingRights[4]; /* Rook file if we have A-side rights */
\r
890 left = BOARD_LEFT+2;
\r
892 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
\r
893 for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
\r
894 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
\r
895 if(ff > BOARD_LEFT+2)
\r
896 for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
\r
897 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
\r
898 if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
\r
899 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
\r
912 } CheckTestClosure;
\r
915 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
\r
916 int rf, int ff, int rt, int ft,
\r
917 VOIDSTAR closure));
\r
920 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
924 int rf, ff, rt, ft;
\r
927 register CheckTestClosure *cl = (CheckTestClosure *) closure;
\r
929 if (rt == cl->rking && ft == cl->fking) cl->check++;
\r
933 /* If the player on move were to move from (rf, ff) to (rt, ft), would
\r
934 he leave himself in check? Or if rf == -1, is the player on move
\r
935 in check now? enPassant must be TRUE if the indicated move is an
\r
936 e.p. capture. The possibility of castling out of a check along the
\r
937 back rank is not accounted for (i.e., we still return nonzero), as
\r
938 this is illegal anyway. Return value is the number of times the
\r
939 king is in check. */
\r
940 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
\r
943 int rf, ff, rt, ft, enPassant;
\r
945 CheckTestClosure cl;
\r
946 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
\r
947 ChessSquare captured = EmptySquare;
\r
948 /* Suppress warnings on uninitialized variables */
\r
950 if(gameInfo.variant == VariantXiangqi)
\r
951 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
\r
952 if(gameInfo.variant == VariantKnightmate)
\r
953 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
\r
957 captured = board[rf][ft];
\r
958 board[rf][ft] = EmptySquare;
\r
960 captured = board[rt][ft];
\r
962 board[rt][ft] = board[rf][ff];
\r
963 board[rf][ff] = EmptySquare;
\r
966 /* For compatibility with ICS wild 9, we scan the board in the
\r
967 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
\r
968 and we test only whether that one is in check. */
\r
970 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
\r
971 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
\r
972 if (board[cl.rking][cl.fking] == king) {
\r
973 if(gameInfo.variant == VariantXiangqi) {
\r
974 /* [HGM] In Xiangqi opposing Kings means check as well */
\r
976 dir = (king >= BlackPawn) ? -1 : 1;
\r
977 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
\r
978 board[i][cl.fking] == EmptySquare; i+=dir );
\r
979 if(i>=0 && i<BOARD_HEIGHT &&
\r
980 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
\r
984 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
\r
985 CheckTestCallback, (VOIDSTAR) &cl);
\r
986 goto undo_move; /* 2-level break */
\r
993 board[rf][ff] = board[rt][ft];
\r
995 board[rf][ft] = captured;
\r
996 board[rt][ft] = EmptySquare;
\r
998 board[rt][ft] = captured;
\r
1006 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
\r
1007 int rf, int ff, int rt, int ft,
\r
1008 VOIDSTAR closure));
\r
1010 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1014 int rf, ff, rt, ft;
\r
1017 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
\r
1019 // if (appData.debugMode) {
\r
1020 // fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
1022 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
\r
1026 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
\r
1028 int flags, epfile;
\r
1029 int rf, ff, rt, ft, promoChar;
\r
1030 char castlingRights[];
\r
1032 LegalityTestClosure cl; ChessSquare piece = board[rf][ff];
\r
1034 if (appData.debugMode) {
\r
1036 for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
\r
1037 fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
\r
1039 /* [HGM] Lance, Cobra and Falcon are wildcard pieces; consider all their moves legal */
\r
1040 /* (perhaps we should disallow moves that obviously leave us in check?) */
\r
1041 if(piece == WhiteFalcon || piece == BlackFalcon ||
\r
1042 piece == WhiteCobra || piece == BlackCobra ||
\r
1043 piece == WhiteLance || piece == BlackLance)
\r
1044 return NormalMove;
\r
1050 cl.kind = IllegalMove;
\r
1051 GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
\r
1053 if(gameInfo.variant == VariantShogi) {
\r
1054 /* [HGM] Shogi promotions. '=' means defer */
\r
1055 if(rf != DROP_RANK && cl.kind == NormalMove) {
\r
1056 ChessSquare piece = board[rf][ff];
\r
1058 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
\r
1059 if(promoChar != NULLCHAR && promoChar != 'x' &&
\r
1060 promoChar != '+' && promoChar != '=' &&
\r
1061 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
\r
1062 cl.kind = IllegalMove;
\r
1063 else if(flags & F_WHITE_ON_MOVE) {
\r
1064 if( (int) piece < (int) WhiteWazir &&
\r
1065 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1066 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1067 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1068 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1069 else /* promotion optional, default is promote */
\r
1070 cl.kind = promoChar == '=' ? NormalMove : WhitePromotionQueen;
\r
1072 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1073 NormalMove : IllegalMove;
\r
1075 if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1076 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1077 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1078 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1079 else /* promotion optional, default is promote */
\r
1080 cl.kind = promoChar == '=' ? NormalMove : BlackPromotionQueen;
\r
1082 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1083 NormalMove : IllegalMove;
\r
1087 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1088 if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1090 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
\r
1092 cl.kind = IllegalMove;
\r
1095 /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
\r
1101 } MateTestClosure;
\r
1103 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
\r
1104 int rf, int ff, int rt, int ft,
\r
1105 VOIDSTAR closure));
\r
1107 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1111 int rf, ff, rt, ft;
\r
1114 register MateTestClosure *cl = (MateTestClosure *) closure;
\r
1119 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
\r
1120 int MateTest(board, flags, epfile, castlingRights)
\r
1122 int flags, epfile;
\r
1123 char castlingRights[];
\r
1125 MateTestClosure cl;
\r
1129 inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
\r
1130 if (cl.count > 0) {
\r
1131 return inCheck ? MT_CHECK : MT_NONE;
\r
1133 return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ?
\r
1134 MT_CHECKMATE : MT_STALEMATE;
\r
1139 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
\r
1140 int rf, int ff, int rt, int ft,
\r
1141 VOIDSTAR closure));
\r
1143 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1147 int rf, ff, rt, ft;
\r
1150 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
\r
1152 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
\r
1153 || PieceToChar(board[rf][ff]) == '~'
\r
1154 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
\r
1156 (cl->rfIn == -1 || cl->rfIn == rf) &&
\r
1157 (cl->ffIn == -1 || cl->ffIn == ff) &&
\r
1158 (cl->rtIn == -1 || cl->rtIn == rt) &&
\r
1159 (cl->ftIn == -1 || cl->ftIn == ft)) {
\r
1162 cl->piece = board[rf][ff];
\r
1171 void Disambiguate(board, flags, epfile, closure)
\r
1173 int flags, epfile;
\r
1174 DisambiguateClosure *closure;
\r
1176 int illegal = 0; char c = closure->promoCharIn;
\r
1177 closure->count = 0;
\r
1178 closure->rf = closure->ff = closure->rt = closure->ft = 0;
\r
1179 closure->kind = ImpossibleMove;
\r
1180 if (appData.debugMode) {
\r
1181 fprintf(debugFP, "Disambiguate in: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1182 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1183 closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
\r
1185 GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
\r
1186 if (closure->count == 0) {
\r
1187 /* See if it's an illegal move due to check */
\r
1189 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
\r
1190 (VOIDSTAR) closure);
\r
1191 if (closure->count == 0) {
\r
1192 /* No, it's not even that */
\r
1193 if (appData.debugMode) { int i, j;
\r
1194 for(i=BOARD_HEIGHT-1; i>=0; i--) {
\r
1195 for(j=0; j<BOARD_WIDTH; j++)
\r
1196 fprintf(debugFP, "%3d", (int) board[i][j]);
\r
1197 fprintf(debugFP, "\n");
\r
1204 if(gameInfo.variant == VariantShogi) {
\r
1205 /* [HGM] Shogi promotions. '=' means defer */
\r
1206 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
\r
1207 ChessSquare piece = closure->piece;
\r
1209 if (appData.debugMode) {
\r
1210 fprintf(debugFP, "Disambiguate A: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1211 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1212 closure->promoCharIn,closure->promoCharIn);
\r
1215 if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
\r
1216 ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) )
\r
1217 closure->kind = IllegalMove;
\r
1218 else if(flags & F_WHITE_ON_MOVE) {
\r
1220 if (appData.debugMode) {
\r
1221 fprintf(debugFP, "Disambiguate B: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1222 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1223 closure->promoCharIn,closure->promoCharIn);
\r
1226 if( (int) piece < (int) WhiteWazir &&
\r
1227 (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
\r
1228 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
\r
1229 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1230 closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
\r
1231 else /* promotion optional, default is promote */
\r
1232 closure->kind = c == '=' ? NormalMove : WhitePromotionQueen;
\r
1234 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1235 NormalMove : IllegalMove;
\r
1237 if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {
\r
1238 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
\r
1239 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
\r
1240 closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
\r
1241 else /* promotion optional, default is promote */
\r
1242 closure->kind = c == '=' ? NormalMove : BlackPromotionQueen;
\r
1244 } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
\r
1245 NormalMove : IllegalMove;
\r
1249 if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
\r
1250 if (closure->kind == WhitePromotionQueen
\r
1251 || closure->kind == BlackPromotionQueen) {
\r
1253 PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
\r
1254 closure->promoCharIn);
\r
1256 closure->kind = IllegalMove;
\r
1260 if (appData.debugMode) {
\r
1261 fprintf(debugFP, "Disambiguate C: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1262 closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
\r
1263 closure->promoCharIn,closure->promoCharIn);
\r
1266 /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
\r
1267 if(closure->promoCharIn != '=')
\r
1268 closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));
\r
1269 else closure->promoChar = '=';
\r
1270 if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
\r
1271 if (closure->count > 1) {
\r
1272 closure->kind = AmbiguousMove;
\r
1275 /* Note: If more than one illegal move matches, but no legal
\r
1276 moves, we return IllegalMove, not AmbiguousMove. Caller
\r
1277 can look at closure->count to detect this.
\r
1279 closure->kind = IllegalMove;
\r
1281 if(closure->kind == IllegalMove)
\r
1282 /* [HGM] might be a variant we don't understand, pass on promotion info */
\r
1283 closure->promoChar = closure->promoCharIn;
\r
1284 if (appData.debugMode) {
\r
1285 fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
\r
1286 closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
\r
1287 closure->promoChar >= ' ' ? closure->promoChar:'-');
\r
1294 ChessSquare piece;
\r
1295 int rf, ff, rt, ft;
\r
1301 } CoordsToAlgebraicClosure;
\r
1303 extern void CoordsToAlgebraicCallback P((Board board, int flags,
\r
1304 ChessMove kind, int rf, int ff,
\r
1305 int rt, int ft, VOIDSTAR closure));
\r
1307 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
\r
1311 int rf, ff, rt, ft;
\r
1314 register CoordsToAlgebraicClosure *cl =
\r
1315 (CoordsToAlgebraicClosure *) closure;
\r
1317 if (rt == cl->rt && ft == cl->ft &&
\r
1318 (board[rf][ff] == cl->piece
\r
1319 || PieceToChar(board[rf][ff]) == '~' &&
\r
1320 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
\r
1322 if (rf == cl->rf) {
\r
1323 if (ff == cl->ff) {
\r
1324 cl->kind = kind; /* this is the move we want */
\r
1326 cl->file++; /* need file to rule out this move */
\r
1329 if (ff == cl->ff) {
\r
1330 cl->rank++; /* need rank to rule out this move */
\r
1332 cl->either++; /* rank or file will rule out this move */
\r
1338 /* Convert coordinates to normal algebraic notation.
\r
1339 promoChar must be NULLCHAR or 'x' if not a promotion.
\r
1341 ChessMove CoordsToAlgebraic(board, flags, epfile,
\r
1342 rf, ff, rt, ft, promoChar, out)
\r
1344 int flags, epfile;
\r
1345 int rf, ff, rt, ft;
\r
1347 char out[MOVE_LEN];
\r
1349 ChessSquare piece;
\r
1351 char *outp = out, c;
\r
1352 CoordsToAlgebraicClosure cl;
\r
1354 if (rf == DROP_RANK) {
\r
1355 /* Bughouse piece drop */
\r
1356 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
\r
1358 *outp++ = ft + AAA;
\r
1360 *outp++ = rt + ONE;
\r
1361 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1363 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
\r
1366 if (promoChar == 'x') promoChar = NULLCHAR;
\r
1367 piece = board[rf][ff];
\r
1368 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
\r
1370 if (appData.debugMode)
\r
1371 fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
\r
1375 kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
\r
1376 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1377 /* Keep short notation if move is illegal only because it
\r
1378 leaves the player in check, but still return IllegalMove */
\r
1379 kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1380 rf, ff, rt, ft, promoChar);
\r
1381 if (kind == IllegalMove) break;
\r
1382 kind = IllegalMove;
\r
1385 *outp++ = ff + AAA;
\r
1386 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
\r
1387 /* Non-capture; use style "e5" */
\r
1389 *outp++ = rt + ONE;
\r
1390 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1392 /* Capture; use style "exd5" */
\r
1393 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
\r
1394 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
\r
1395 *outp++ = ft + AAA;
\r
1397 *outp++ = rt + ONE;
\r
1398 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1400 /* Use promotion suffix style "=Q" */
\r
1402 if (appData.debugMode)
\r
1403 fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
\r
1404 if (promoChar != NULLCHAR) {
\r
1405 if(gameInfo.variant == VariantShogi) {
\r
1406 /* [HGM] ... but not in Shogi! */
\r
1407 *outp++ = promoChar == '=' ? '=' : '+';
\r
1410 *outp++ = ToUpper(promoChar);
\r
1419 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
\r
1420 /* Code added by Tord: FRC castling. */
\r
1421 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
\r
1422 (piece == BlackKing && board[rt][ft] == BlackRook)) {
\r
1423 if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
\r
1424 return LegalityTest(board, flags, epfile, initialRights,
\r
1425 rf, ff, rt, ft, promoChar);
\r
1427 /* End of code added by Tord */
\r
1428 /* Test for castling or ICS wild castling */
\r
1429 /* Use style "O-O" (oh-oh) for PGN compatibility */
\r
1430 else if (rf == rt &&
\r
1431 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
\r
1432 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
\r
1433 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
\r
1434 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
\r
1435 strcpy(out, "O-O");
\r
1437 strcpy(out, "O-O-O");
\r
1439 /* This notation is always unambiguous, unless there are
\r
1440 kings on both the d and e files, with "wild castling"
\r
1441 possible for the king on the d file and normal castling
\r
1442 possible for the other. ICS rules for wild 9
\r
1443 effectively make castling illegal for either king in
\r
1444 this situation. So I am not going to worry about it;
\r
1445 I'll just generate an ambiguous O-O in this case.
\r
1447 return LegalityTest(board, flags, epfile, initialRights,
\r
1448 rf, ff, rt, ft, promoChar);
\r
1451 /* else fall through */
\r
1459 cl.kind = IllegalMove;
\r
1460 cl.rank = cl.file = cl.either = 0;
\r
1461 GenLegal(board, flags, epfile, initialRights,
\r
1462 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1464 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
\r
1465 /* Generate pretty moves for moving into check, but
\r
1466 still return IllegalMove.
\r
1468 GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,
\r
1469 CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
\r
1470 if (cl.kind == IllegalMove) break;
\r
1471 cl.kind = IllegalMove;
\r
1474 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
\r
1475 else "Ngf3" or "Ngxf7",
\r
1476 else "N1f3" or "N5xf7",
\r
1477 else "Ng1f3" or "Ng5xf7".
\r
1479 c = PieceToChar(piece) ;
\r
1480 if( c == '~' || c == '+') {
\r
1481 /* [HGM] print nonexistent piece as its demoted version */
\r
1482 piece = (ChessSquare) (DEMOTED piece);
\r
1484 if(c=='+') *outp++ = c;
\r
1485 *outp++ = ToUpper(PieceToChar(piece));
\r
1487 if (cl.file || (cl.either && !cl.rank)) {
\r
1488 *outp++ = ff + AAA;
\r
1492 *outp++ = rf + ONE;
\r
1493 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1496 if(board[rt][ft] != EmptySquare)
\r
1499 *outp++ = ft + AAA;
\r
1501 *outp++ = rt + ONE;
\r
1502 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1504 if (gameInfo.variant == VariantShogi) {
\r
1505 /* [HGM] in Shogi non-pawns can promote */
\r
1506 if(flags & F_WHITE_ON_MOVE) {
\r
1507 if( (int) cl.piece < (int) WhiteWazir &&
\r
1508 (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
\r
1509 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
\r
1510 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
\r
1511 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
\r
1512 else cl.kind = WhitePromotionQueen; /* promotion optional */
\r
1514 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1515 NormalMove : IllegalMove;
\r
1517 if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
\r
1518 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
\r
1519 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
\r
1520 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
\r
1521 else cl.kind = BlackPromotionQueen; /* promotion optional */
\r
1522 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
\r
1523 NormalMove : IllegalMove;
\r
1525 if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
\r
1526 /* for optional promotions append '+' or '=' */
\r
1527 if(promoChar == '=') {
\r
1529 cl.kind = NormalMove;
\r
1530 } else *outp++ = '+';
\r
1532 } else if(cl.kind == IllegalMove) {
\r
1533 /* Illegal move specifies any given promotion */
\r
1534 if(promoChar != NULLCHAR && promoChar != 'x') {
\r
1536 *outp++ = ToUpper(promoChar);
\r
1543 /* [HGM] Always long notation for fairies we don't know */
\r
1548 case WhiteGrasshopper:
\r
1549 case BlackGrasshopper:
\r
1551 /* Moving a nonexistent piece */
\r
1555 /* Not a legal move, even ignoring check.
\r
1556 If there was a piece on the from square,
\r
1557 use style "Ng1g3" or "Ng1xe8";
\r
1558 if there was a pawn or nothing (!),
\r
1559 use style "g1g3" or "g1xe8". Use "x"
\r
1560 if a piece was on the to square, even
\r
1561 a piece of the same color.
\r
1564 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
\r
1565 *outp++ = ToUpper(PieceToChar(piece));
\r
1567 *outp++ = ff + AAA;
\r
1569 *outp++ = rf + ONE;
\r
1570 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
\r
1571 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
\r
1572 *outp++ = ft + AAA;
\r
1574 *outp++ = rt + ONE;
\r
1575 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
\r
1576 /* Use promotion suffix style "=Q" */
\r
1577 if (promoChar != NULLCHAR && promoChar != 'x') {
\r
1579 *outp++ = ToUpper(promoChar);
\r
1583 return IllegalMove;
\r