changes by H.G. Muller; version 4.3.4
[xboard.git] / moves.c
1 /*\r
2  * moves.c - Move generation and checking\r
3  * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
6  * Enhancements Copyright 1992-95 Free Software Foundation, Inc.\r
7  *\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
12  *\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
20  *\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
27  * SOFTWARE.\r
28  * ------------------------------------------------------------------------\r
29  *\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
37  *\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
42  *\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
47  */\r
48 \r
49 #include "config.h"\r
50 \r
51 #include <stdio.h>\r
52 #if HAVE_STRING_H\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
57 #include "common.h"\r
58 #include "backend.h" \r
59 #include "moves.h"\r
60 #include "parser.h"\r
61 \r
62 int WhitePiece P((ChessSquare));\r
63 int BlackPiece P((ChessSquare));\r
64 int SameColor P((ChessSquare, ChessSquare));\r
65 \r
66 extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */\r
67 \r
68 \r
69 int WhitePiece(piece)\r
70      ChessSquare piece;\r
71 {\r
72     return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;\r
73 }\r
74 \r
75 int BlackPiece(piece)\r
76      ChessSquare piece;\r
77 {\r
78     return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;\r
79 }\r
80 \r
81 int SameColor(piece1, piece2)\r
82      ChessSquare piece1, piece2;\r
83 {\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
92 }\r
93 \r
94 ChessSquare PromoPiece(moveType)\r
95      ChessMove moveType;\r
96 {\r
97     switch (moveType) {\r
98       default:\r
99         return EmptySquare;\r
100       case WhitePromotionQueen:\r
101         return WhiteQueen;\r
102       case BlackPromotionQueen:\r
103         return BlackQueen;\r
104       case WhitePromotionRook:\r
105         return WhiteRook;\r
106       case BlackPromotionRook:\r
107         return BlackRook;\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
117         return WhiteKing;\r
118       case BlackPromotionKing:\r
119         return BlackKing;\r
120 #ifdef FAIRY\r
121       case WhitePromotionChancellor:\r
122         return WhiteMarshall;\r
123       case BlackPromotionChancellor:\r
124         return BlackMarshall;\r
125       case WhitePromotionArchbishop:\r
126         return WhiteCardinal;\r
127       case BlackPromotionArchbishop:\r
128         return BlackCardinal;\r
129 #endif\r
130     }\r
131 }\r
132 \r
133 ChessMove PromoCharToMoveType(whiteOnMove, promoChar)\r
134      int whiteOnMove;\r
135      int promoChar;\r
136 {\r
137     if (whiteOnMove) {\r
138         switch (promoChar) {\r
139           case 'n':\r
140           case 'N':\r
141             return WhitePromotionKnight;\r
142           case 'b':\r
143           case 'B':\r
144             return WhitePromotionBishop;\r
145           case 'r':\r
146           case 'R':\r
147             return WhitePromotionRook;\r
148 #ifdef FAIRY\r
149           case 'a':\r
150           case 'A':\r
151             return WhitePromotionArchbishop;\r
152           case 'c':\r
153           case 'C':\r
154             return WhitePromotionChancellor;\r
155 #endif\r
156           case 'q':\r
157           case 'Q':\r
158             return WhitePromotionQueen;\r
159           case 'k':\r
160           case 'K':\r
161             return WhitePromotionKing;\r
162           case NULLCHAR:\r
163           default:\r
164             return NormalMove;\r
165         }\r
166     } else {\r
167         switch (promoChar) {\r
168           case 'n':\r
169           case 'N':\r
170             return BlackPromotionKnight;\r
171           case 'b':\r
172           case 'B':\r
173             return BlackPromotionBishop;\r
174           case 'r':\r
175           case 'R':\r
176             return BlackPromotionRook;\r
177 #ifdef FAIRY\r
178           case 'a':\r
179           case 'A':\r
180             return BlackPromotionArchbishop;\r
181           case 'c':\r
182           case 'C':\r
183             return BlackPromotionChancellor;\r
184 #endif\r
185           case 'q':\r
186           case 'Q':\r
187             return BlackPromotionQueen;\r
188           case 'k':\r
189           case 'K':\r
190             return BlackPromotionKing;\r
191           case NULLCHAR:\r
192           default:\r
193             return NormalMove;\r
194         }\r
195     }\r
196 }\r
197 \r
198 char pieceToChar[] = {\r
199               'P', 'N', 'B', 'R', 'Q', \r
200 #ifdef FAIRY\r
201     'F', 'W', 'E', 'H', 'A', 'C', 'G', 'O', 'M', 'U', \r
202 #endif\r
203     'K',      'p', 'n', 'b', 'r', 'q', \r
204 #ifdef FAIRY            \r
205     'f', 'w', 'e', 'h', 'a', 'c', 'g', 'o', 'm', 'u',\r
206 #endif\r
207     'k', 'x'\r
208   };\r
209 \r
210 char PieceToChar(p)\r
211      ChessSquare p;\r
212 {\r
213     return pieceToChar[(int) p];\r
214 }\r
215 \r
216 ChessSquare CharToPiece(c)\r
217      int c;\r
218 {\r
219      int i;\r
220      for(i=0; i< (int) EmptySquare; i++)\r
221           if(pieceToChar[i] == c) return (ChessSquare) i;\r
222      return EmptySquare;\r
223 /* [HGM] code marked for deletion\r
224      switch (c) {\r
225       default:\r
226       case 'x': return EmptySquare;\r
227       case 'P': return WhitePawn;\r
228       case 'R': return WhiteRook;\r
229       case 'N': return WhiteKnight;\r
230       case 'B': return WhiteBishop;\r
231       case 'Q': return WhiteQueen;\r
232       case 'K': return WhiteKing;\r
233       case 'p': return BlackPawn;\r
234       case 'r': return BlackRook;\r
235       case 'n': return BlackKnight;\r
236       case 'b': return BlackBishop;\r
237       case 'q': return BlackQueen;\r
238       case 'k': return BlackKing;\r
239 #ifdef FAIRY\r
240       case 'A': return WhiteCardinal;\r
241       case 'C': return WhiteMarshall;\r
242       case 'F': return WhiteFerz;\r
243       case 'H': return WhiteNightrider;\r
244       case 'E': return WhiteAlfil;\r
245       case 'W': return WhiteWazir;\r
246       case 'U': return WhiteUnicorn;\r
247       case 'O': return WhiteCannon;\r
248       case 'G': return WhiteGrasshopper;\r
249       case 'M': return WhiteMan;\r
250                 \r
251       case 'a': return BlackCardinal;\r
252       case 'c': return BlackMarshall;\r
253       case 'f': return BlackFerz;\r
254       case 'h': return BlackNightrider;\r
255       case 'e': return BlackAlfil;\r
256       case 'w': return BlackWazir;\r
257       case 'u': return BlackUnicorn;\r
258       case 'o': return BlackCannon;\r
259       case 'g': return BlackGrasshopper;\r
260       case 'm': return BlackMan;\r
261                 \r
262 #endif\r
263     }\r
264 */\r
265 }\r
266 \r
267 void CopyBoard(to, from)\r
268      Board to, from;\r
269 {\r
270     int i, j;\r
271     \r
272     for (i = 0; i < BOARD_HEIGHT; i++)\r
273       for (j = 0; j < BOARD_WIDTH; j++)\r
274         to[i][j] = from[i][j];\r
275 }\r
276 \r
277 int CompareBoards(board1, board2)\r
278      Board board1, board2;\r
279 {\r
280     int i, j;\r
281     \r
282     for (i = 0; i < BOARD_HEIGHT; i++)\r
283       for (j = 0; j < BOARD_WIDTH; j++) {\r
284           if (board1[i][j] != board2[i][j])\r
285             return FALSE;\r
286     }\r
287     return TRUE;\r
288 }\r
289 \r
290 \r
291 /* Call callback once for each pseudo-legal move in the given\r
292    position, except castling moves. A move is pseudo-legal if it is\r
293    legal, or if it would be legal except that it leaves the king in\r
294    check.  In the arguments, epfile is EP_NONE if the previous move\r
295    was not a double pawn push, or the file 0..7 if it was, or\r
296    EP_UNKNOWN if we don't know and want to allow all e.p. captures.\r
297    Promotion moves generated are to Queen only.\r
298 */\r
299 void GenPseudoLegal(board, flags, epfile, callback, closure)\r
300      Board board;\r
301      int flags;\r
302      int epfile;\r
303      MoveCallback callback;\r
304      VOIDSTAR closure;\r
305 {\r
306     int rf, ff;\r
307     int i, j, d, s, fs, rs, rt, ft, m;\r
308 \r
309     for (rf = 0; rf < BOARD_HEIGHT; rf++) \r
310       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {\r
311           ChessSquare piece;\r
312 \r
313           if (flags & F_WHITE_ON_MOVE) {\r
314               if (!WhitePiece(board[rf][ff])) continue;\r
315           } else {\r
316               if (!BlackPiece(board[rf][ff])) continue;\r
317           }\r
318           m = 0; piece = board[rf][ff];\r
319           if(gameInfo.variant == VariantCrazyhouse &&\r
320               ( (int) piece > (int) WhiteQueen && (int) piece < (int) WhiteKing\r
321              || (int) piece > (int) BlackQueen && (int) piece < (int) BlackKing ))\r
322                  piece = (ChessSquare) ( DEMOTED piece );\r
323           if(gameInfo.variant == VariantShogi)\r
324                  piece = (ChessSquare) ( SHOGI piece );\r
325 \r
326           switch (piece) {\r
327             /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */\r
328             default:\r
329               /* can't happen ([HGM] except for faries...) */\r
330               break;\r
331 \r
332             case WhitePawn:\r
333 #ifdef FAIRY\r
334               if(gameInfo.variant == VariantXiangqi) {\r
335                   /* [HGM] capture and move straight ahead in Xiangqi */\r
336                   if (rf < BOARD_HEIGHT-1 &&\r
337                            !SameColor(board[rf][ff], board[rf + 1][ff]) ) {\r
338                            callback(board, flags, NormalMove,\r
339                                     rf, ff, rf + 1, ff, closure);\r
340                   }\r
341                   /* and move sideways when across the river */\r
342                   for (s = -1; s <= 1; s += 2) {\r
343                       if (rf >= BOARD_HEIGHT>>1 &&\r
344                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
345                           !WhitePiece(board[rf][ff+s]) ) {\r
346                            callback(board, flags, NormalMove,\r
347                                     rf, ff, rf, ff+s, closure);\r
348                       }\r
349                   }\r
350                   break;\r
351               }\r
352 #endif\r
353               if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {\r
354                   callback(board, flags,\r
355                            rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,\r
356                            rf, ff, rf + 1, ff, closure);\r
357               }\r
358               if (rf == 1 && board[2][ff] == EmptySquare &&\r
359                   gameInfo.variant != VariantShatranj && /* [HGM] */\r
360                   gameInfo.variant != VariantCourier  && /* [HGM] */\r
361                   board[3][ff] == EmptySquare ) {\r
362                       callback(board, flags, NormalMove,\r
363                                rf, ff, 3, ff, closure);\r
364               }\r
365               for (s = -1; s <= 1; s += 2) {\r
366                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
367                       ((flags & F_KRIEGSPIEL_CAPTURE) ||\r
368                        BlackPiece(board[rf + 1][ff + s]))) {\r
369                       callback(board, flags, \r
370                                rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,\r
371                                rf, ff, rf + 1, ff + s, closure);\r
372                   }\r
373                   if (rf == BOARD_HEIGHT-4) {\r
374                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
375                           (epfile == ff + s || epfile == EP_UNKNOWN) &&\r
376                           board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&\r
377                           board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {\r
378                           callback(board, flags, WhiteCapturesEnPassant,\r
379                                    rf, ff, 5, ff + s, closure);\r
380                       }\r
381                   }\r
382               }             \r
383               break;\r
384 \r
385             case BlackPawn:\r
386 #ifdef FAIRY\r
387               if(gameInfo.variant == VariantXiangqi) {\r
388                   /* [HGM] capture straight ahead in Xiangqi */\r
389                   if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {\r
390                            callback(board, flags, NormalMove,\r
391                                     rf, ff, rf - 1, ff, closure);\r
392                   }\r
393                   /* and move sideways when across the river */\r
394                   for (s = -1; s <= 1; s += 2) {\r
395                       if (rf < BOARD_HEIGHT>>1 &&\r
396                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
397                           !BlackPiece(board[rf][ff+s]) ) {\r
398                            callback(board, flags, NormalMove,\r
399                                     rf, ff, rf, ff+s, closure);\r
400                       }\r
401                   }\r
402                   break;\r
403               }\r
404 #endif\r
405               if (rf > 0 && board[rf - 1][ff] == EmptySquare) {\r
406                   callback(board, flags, \r
407                            rf == 1 ? BlackPromotionQueen : NormalMove,\r
408                            rf, ff, rf - 1, ff, closure);\r
409               }\r
410               if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&\r
411                   gameInfo.variant != VariantShatranj && /* [HGM] */\r
412                   gameInfo.variant != VariantCourier  && /* [HGM] */\r
413                   board[BOARD_HEIGHT-4][ff] == EmptySquare) {\r
414                   callback(board, flags, NormalMove,\r
415                            rf, ff, BOARD_HEIGHT-4, ff, closure);\r
416               }\r
417               for (s = -1; s <= 1; s += 2) {\r
418                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
419                       ((flags & F_KRIEGSPIEL_CAPTURE) ||\r
420                        WhitePiece(board[rf - 1][ff + s]))) {\r
421                       callback(board, flags, \r
422                                rf == 1 ? BlackPromotionQueen : NormalMove,\r
423                                rf, ff, rf - 1, ff + s, closure);\r
424                   }\r
425                   if (rf == 3) {\r
426                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
427                           (epfile == ff + s || epfile == EP_UNKNOWN) &&\r
428                           board[3][ff + s] == WhitePawn &&\r
429                           board[2][ff + s] == EmptySquare) {\r
430                           callback(board, flags, BlackCapturesEnPassant,\r
431                                    rf, ff, 2, ff + s, closure);\r
432                       }\r
433                   }\r
434               }             \r
435               break;\r
436 \r
437 #ifdef FAIRY\r
438             case WhiteUnicorn:\r
439             case BlackUnicorn:\r
440 #endif\r
441             case WhiteKnight:\r
442             case BlackKnight:\r
443             mounted:\r
444               for (i = -1; i <= 1; i += 2)\r
445                 for (j = -1; j <= 1; j += 2)\r
446                   for (s = 1; s <= 2; s++) {\r
447                       rt = rf + i*s;\r
448                       ft = ff + j*(3-s);\r
449                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)\r
450                           && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)\r
451                           && !SameColor(board[rf][ff], board[rt][ft]))\r
452                       callback(board, flags, NormalMove,\r
453                                rf, ff, rt, ft, closure);\r
454                   }\r
455               break;\r
456 \r
457 #ifdef FAIRY\r
458             case SHOGI WhiteKnight:\r
459               for (s = -1; s <= 1; s += 2) {\r
460                   if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
461                       !SameColor(board[rf][ff], board[rf + 2][ff + s])) {\r
462                       callback(board, flags, \r
463                                rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,\r
464                                rf, ff, rf + 2, ff + s, closure);\r
465                   }\r
466               }\r
467               break;\r
468 \r
469             case SHOGI BlackKnight:\r
470               for (s = -1; s <= 1; s += 2) {\r
471                   if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
472                       !SameColor(board[rf][ff], board[rf - 2][ff + s])) {\r
473                       callback(board, flags, \r
474                                rf == 1 ? BlackPromotionQueen : NormalMove,\r
475                                rf, ff, rf - 2, ff + s, closure);\r
476                   }\r
477               }             \r
478               break;\r
479 \r
480             case WhiteCannon:\r
481             case BlackCannon:\r
482               for (d = 0; d <= 1; d++)\r
483                 for (s = -1; s <= 1; s += 2) {\r
484                   m = 0;\r
485                   for (i = 1;; i++) {\r
486                       rt = rf + (i * s) * d;\r
487                       ft = ff + (i * s) * (1 - d);\r
488                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
489                       if (m == 0 && board[rt][ft] == EmptySquare)\r
490                                  callback(board, flags, NormalMove,\r
491                                           rf, ff, rt, ft, closure);\r
492                       if (m == 1 && board[rt][ft] != EmptySquare &&\r
493                           !SameColor(board[rf][ff], board[rt][ft]) )\r
494                                  callback(board, flags, NormalMove,\r
495                                           rf, ff, rt, ft, closure);\r
496                       if (board[rt][ft] != EmptySquare && m++) break;\r
497                   }\r
498                 }\r
499               break;\r
500 \r
501             /* Gold General (and all its promoted versions) . First do the */\r
502             /* diagonal forward steps, then proceed as normal Wazir        */\r
503             case SHOGI WhiteWazir:\r
504             case SHOGI (PROMOTED WhitePawn):\r
505             case SHOGI (PROMOTED WhiteKnight):\r
506             case SHOGI (PROMOTED WhiteQueen):\r
507             case SHOGI (PROMOTED WhiteFerz):\r
508               for (s = -1; s <= 1; s += 2) {\r
509                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
510                       !SameColor(board[rf][ff], board[rf + 1][ff + s])) {\r
511                       callback(board, flags, \r
512                                rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,\r
513                                rf, ff, rf + 1, ff + s, closure);\r
514                   }\r
515               }\r
516               goto finishGold;\r
517 \r
518             case SHOGI BlackWazir:\r
519             case SHOGI (PROMOTED BlackPawn):\r
520             case SHOGI (PROMOTED BlackKnight):\r
521             case SHOGI (PROMOTED BlackQueen):\r
522             case SHOGI (PROMOTED BlackFerz):\r
523               for (s = -1; s <= 1; s += 2) {\r
524                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
525                       !SameColor(board[rf][ff], board[rf - 1][ff + s])) {\r
526                       callback(board, flags, \r
527                                rf == 1 ? BlackPromotionQueen : NormalMove,\r
528                                rf, ff, rf - 1, ff + s, closure);\r
529                   }\r
530               }             \r
531 \r
532             case WhiteWazir:\r
533             case BlackWazir:\r
534             finishGold:\r
535               for (d = 0; d <= 1; d++)\r
536                 for (s = -1; s <= 1; s += 2) {\r
537                       rt = rf + s * d;\r
538                       ft = ff + s * (1 - d);\r
539                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)\r
540                           && !SameColor(board[rf][ff], board[rt][ft]) &&\r
541                           (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )\r
542                                callback(board, flags, NormalMove,\r
543                                         rf, ff, rt, ft, closure);\r
544                       }\r
545               break;\r
546 \r
547             case WhiteAlfil:\r
548             case BlackAlfil:\r
549                 /* [HGM] support Shatranj pieces */\r
550                 for (rs = -1; rs <= 1; rs += 2) \r
551                   for (fs = -1; fs <= 1; fs += 2) {\r
552                       rt = rf + 2 * rs;\r
553                       ft = ff + 2 * fs;\r
554                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)\r
555                           && ( gameInfo.variant != VariantXiangqi ||\r
556                                board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )\r
557                          \r
558                           && !SameColor(board[rf][ff], board[rt][ft]))\r
559                                callback(board, flags, NormalMove,\r
560                                         rf, ff, rt, ft, closure);\r
561                   }\r
562                 break;\r
563 \r
564             /* Shogi Dragon Horse has to continue with Wazir after Bishop */\r
565             case SHOGI WhiteCardinal:\r
566             case SHOGI BlackCardinal:\r
567               m++;\r
568 \r
569             /* Capablanca Archbishop continues as Knight                  */\r
570             case WhiteCardinal:\r
571             case BlackCardinal:\r
572               m++;\r
573 \r
574             /* Shogi Bishops are ordinary Bishops */\r
575             case SHOGI WhiteBishop:\r
576             case SHOGI BlackBishop:\r
577 #endif\r
578             case WhiteBishop:\r
579             case BlackBishop:\r
580               for (rs = -1; rs <= 1; rs += 2) \r
581                 for (fs = -1; fs <= 1; fs += 2) \r
582                   for (i = 1;; i++) {\r
583                       rt = rf + (i * rs);\r
584                       ft = ff + (i * fs);\r
585                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
586                       if (SameColor(board[rf][ff], board[rt][ft])) break;\r
587                       callback(board, flags, NormalMove,\r
588                                rf, ff, rt, ft, closure);\r
589                       if (board[rt][ft] != EmptySquare) break;\r
590                   }\r
591                 if(m==1) goto mounted;\r
592                 if(m==2) goto finishGold;\r
593                 /* Bishop falls through */\r
594               break;\r
595 \r
596 #ifdef FAIRY\r
597             /* Shogi Lance is unlike anything, and asymmetric at that */\r
598             case SHOGI WhiteQueen:\r
599               for(i = 1;; i++) {\r
600                       rt = rf + i;\r
601                       ft = ff;\r
602                       if (rt >= BOARD_HEIGHT) break;\r
603                       if (SameColor(board[rf][ff], board[rt][ft])) break;\r
604                       callback(board, flags, NormalMove,\r
605                                rf, ff, rt, ft, closure);\r
606                       if (board[rt][ft] != EmptySquare) break;\r
607               }\r
608               break;\r
609 \r
610             case SHOGI BlackQueen:\r
611               for(i = 1;; i++) {\r
612                       rt = rf - i;\r
613                       ft = ff;\r
614                       if (rt < 0) break;\r
615                       if (SameColor(board[rf][ff], board[rt][ft])) break;\r
616                       callback(board, flags, NormalMove,\r
617                                rf, ff, rt, ft, closure);\r
618                       if (board[rt][ft] != EmptySquare) break;\r
619               }\r
620               break;\r
621 \r
622             /* Shogi Dragon King has to continue as Ferz after Rook moves */\r
623             case SHOGI WhiteMarshall:\r
624             case SHOGI BlackMarshall:\r
625               m++;\r
626 \r
627             /* Capablanca Chancellor sets flag to continue as Knight      */\r
628             case WhiteMarshall:\r
629             case BlackMarshall:\r
630               m++;\r
631 \r
632             /* Shogi Rooks are ordinary Rooks */\r
633             case SHOGI WhiteRook:\r
634             case SHOGI BlackRook:\r
635 #endif\r
636             case WhiteRook:\r
637             case BlackRook:\r
638               for (d = 0; d <= 1; d++)\r
639                 for (s = -1; s <= 1; s += 2)\r
640                   for (i = 1;; i++) {\r
641                       rt = rf + (i * s) * d;\r
642                       ft = ff + (i * s) * (1 - d);\r
643                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
644                       if (SameColor(board[rf][ff], board[rt][ft])) break;\r
645                       callback(board, flags, NormalMove,\r
646                                rf, ff, rt, ft, closure);\r
647                       if (board[rt][ft] != EmptySquare) break;\r
648                   }\r
649                 if(m==1) goto mounted;\r
650                 if(m==2) goto walking;\r
651               break;\r
652 \r
653             case WhiteQueen:\r
654             case BlackQueen:\r
655               for (rs = -1; rs <= 1; rs++) \r
656                 for (fs = -1; fs <= 1; fs++) {\r
657                     if (rs == 0 && fs == 0) continue;\r
658                     for (i = 1;; i++) {\r
659                         rt = rf + (i * rs);\r
660                         ft = ff + (i * fs);\r
661                         if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
662                         if (SameColor(board[rf][ff], board[rt][ft])) break;\r
663                         callback(board, flags, NormalMove,\r
664                                  rf, ff, rt, ft, closure);\r
665                         if (board[rt][ft] != EmptySquare) break;\r
666                     }\r
667                 }\r
668               break;\r
669 \r
670 #ifdef FAIRY\r
671             /* Shogi Pawn and Silver General: first the Pawn move,    */\r
672             /* then the General continues like a Ferz                 */\r
673             case SHOGI WhitePawn:\r
674             case SHOGI WhiteFerz:\r
675                   if (rf < BOARD_HEIGHT-1 &&\r
676                            !SameColor(board[rf][ff], board[rf + 1][ff]) ) \r
677                            callback(board, flags, NormalMove,\r
678                                     rf, ff, rf + 1, ff, closure);\r
679               if(piece != SHOGI WhitePawn) goto finishSilver;\r
680               break;\r
681 \r
682             case SHOGI BlackPawn:\r
683             case SHOGI BlackFerz:\r
684                   if (rf > 0 &&\r
685                            !SameColor(board[rf][ff], board[rf - 1][ff]) ) \r
686                            callback(board, flags, NormalMove,\r
687                                     rf, ff, rf - 1, ff, closure);\r
688               if(piece == SHOGI BlackPawn) break;\r
689 \r
690             case WhiteFerz:\r
691             case BlackFerz:\r
692             finishSilver:\r
693                 /* [HGM] support Shatranj pieces */\r
694                 for (rs = -1; rs <= 1; rs += 2) \r
695                   for (fs = -1; fs <= 1; fs += 2) {\r
696                       rt = rf + rs;\r
697                       ft = ff + fs;\r
698                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
699                       if (!SameColor(board[rf][ff], board[rt][ft]) &&\r
700                           (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )\r
701                                callback(board, flags, NormalMove,\r
702                                         rf, ff, rt, ft, closure);\r
703                   }\r
704                 break;\r
705 \r
706             case WhiteMan:\r
707             case BlackMan:\r
708             case SHOGI WhiteKing:\r
709             case SHOGI BlackKing:\r
710 #endif\r
711             case WhiteKing:\r
712             case BlackKing:\r
713             walking:\r
714               for (i = -1; i <= 1; i++)\r
715                 for (j = -1; j <= 1; j++) {\r
716                     if (i == 0 && j == 0) continue;\r
717                     rt = rf + i;\r
718                     ft = ff + j;\r
719                     if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;\r
720                     if (SameColor(board[rf][ff], board[rt][ft])) continue;\r
721                     callback(board, flags, NormalMove,\r
722                              rf, ff, rt, ft, closure);\r
723                 }\r
724               break;\r
725           }\r
726       }\r
727 }\r
728 \r
729 \r
730 typedef struct {\r
731     MoveCallback cb;\r
732     VOIDSTAR cl;\r
733 } GenLegalClosure;\r
734 \r
735 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,\r
736                                 int rf, int ff, int rt, int ft,\r
737                                 VOIDSTAR closure));\r
738 \r
739 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
740      Board board;\r
741      int flags;\r
742      ChessMove kind;\r
743      int rf, ff, rt, ft;\r
744      VOIDSTAR closure;\r
745 {\r
746     register GenLegalClosure *cl = (GenLegalClosure *) closure;\r
747 \r
748     if (!(flags & F_IGNORE_CHECK) &&\r
749         CheckTest(board, flags, rf, ff, rt, ft,\r
750                   kind == WhiteCapturesEnPassant ||\r
751                   kind == BlackCapturesEnPassant)) return;\r
752     if (flags & F_ATOMIC_CAPTURE) {\r
753       if (board[rt][ft] != EmptySquare ||\r
754           kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {\r
755         int r, f;\r
756         ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;\r
757         if (board[rf][ff] == king) return;\r
758         for (r = rt-1; r <= rt+1; r++) {\r
759           for (f = ft-1; f <= ft+1; f++) {\r
760             if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&\r
761                 board[r][f] == king) return;\r
762           }\r
763         }\r
764       }\r
765     }\r
766     cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);\r
767 }\r
768 \r
769 \r
770 typedef struct {\r
771     int rf, ff, rt, ft;\r
772     ChessMove kind;\r
773 } LegalityTestClosure;\r
774 \r
775 \r
776 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless\r
777    F_IGNORE_CHECK is set in the flags, omit moves that would leave the\r
778    king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit\r
779    moves that would destroy your own king.  The CASTLE_OK flags are\r
780    true if castling is not yet ruled out by a move of the king or\r
781    rook.  Return TRUE if the player on move is currently in check and\r
782    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */\r
783 int GenLegal(board, flags, epfile, castlingRights, callback, closure)\r
784      Board board;\r
785      int flags;\r
786      int epfile;\r
787      char castlingRights[];\r
788      MoveCallback callback;\r
789      VOIDSTAR closure;\r
790 {\r
791     GenLegalClosure cl;\r
792     int ff, ft;\r
793     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;\r
794 \r
795     cl.cb = callback;\r
796     cl.cl = closure;\r
797     GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);\r
798 \r
799     if (!ignoreCheck &&\r
800         CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;\r
801 \r
802     /* Generate castling moves */\r
803     for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {\r
804         if ((flags & F_WHITE_ON_MOVE) &&\r
805             (flags & F_WHITE_KCASTLE_OK) &&\r
806             board[0][ff] == WhiteKing &&\r
807             board[0][ff + 1] == EmptySquare &&\r
808             board[0][ff + 2] == EmptySquare &&\r
809             board[0][BOARD_RGHT-3] == EmptySquare &&\r
810             board[0][BOARD_RGHT-2] == EmptySquare &&\r
811             board[0][BOARD_RGHT-1] == WhiteRook &&\r
812             castlingRights[0] >= 0 && /* [HGM] check rights */\r
813             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&\r
814             (ignoreCheck ||                             \r
815              (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&\r
816               !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&\r
817               !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {\r
818 \r
819             callback(board, flags,\r
820                      ff==4 ? WhiteKingSideCastle : WhiteKingSideCastleWild,\r
821                      0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2), closure);\r
822         }\r
823         if ((flags & F_WHITE_ON_MOVE) &&\r
824             (flags & F_WHITE_QCASTLE_OK) &&\r
825             board[0][ff] == WhiteKing &&\r
826             board[0][ff - 1] == EmptySquare &&\r
827             board[0][ff - 2] == EmptySquare &&\r
828             board[0][BOARD_LEFT+2] == EmptySquare &&\r
829             board[0][BOARD_LEFT+1] == EmptySquare &&\r
830             board[0][BOARD_LEFT+0] == WhiteRook &&\r
831             castlingRights[1] >= 0 && /* [HGM] check rights */\r
832             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&\r
833             (ignoreCheck ||\r
834              (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&\r
835               !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3,      FALSE) &&\r
836               !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {\r
837 \r
838             callback(board, flags,\r
839                      ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,\r
840                      0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);\r
841         }\r
842         if (!(flags & F_WHITE_ON_MOVE) &&\r
843             (flags & F_BLACK_KCASTLE_OK) &&\r
844             board[BOARD_HEIGHT-1][ff] == BlackKing &&\r
845             board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&\r
846             board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&\r
847             board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&\r
848             board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&\r
849             board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&\r
850             castlingRights[3] >= 0 && /* [HGM] check rights */\r
851             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&\r
852             (ignoreCheck ||\r
853              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&\r
854               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&\r
855               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {\r
856 \r
857             callback(board, flags,\r
858                      ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,\r
859                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2), closure);\r
860         }\r
861         if (!(flags & F_WHITE_ON_MOVE) &&\r
862             (flags & F_BLACK_QCASTLE_OK) &&\r
863             board[BOARD_HEIGHT-1][ff] == BlackKing &&\r
864             board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&\r
865             board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&\r
866             board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&\r
867             board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&\r
868             board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&\r
869             castlingRights[4] >= 0 && /* [HGM] check rights */\r
870             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&\r
871             (ignoreCheck ||\r
872              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&\r
873               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3,      FALSE) &&\r
874               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE)))) {\r
875 \r
876             callback(board, flags,\r
877                      ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,\r
878                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);\r
879         }\r
880     }\r
881 \r
882     /* PUSH Fabien */\r
883 \r
884     /* generate all potential FRC castling moves (KxR), ignoring flags */\r
885     /* [HGM] Tord! Help requested! */\r
886 \r
887     if ((flags & F_WHITE_ON_MOVE) != 0) {\r
888 \r
889        for (ff = BOARD_LEFT+1; ff < BOARD_RGHT-1; ff++) {\r
890           if (board[0][ff] == WhiteKing) {\r
891              for (ft = BOARD_LEFT+0; ft < BOARD_RGHT; ft++) {\r
892                 if (board[0][ft] == WhiteRook) {\r
893                    callback(board, flags, \r
894                             (ft > ff) ? WhiteHSideCastleFR : WhiteASideCastleFR,\r
895                             0, ff, 0, ft, closure);\r
896                 }\r
897              }\r
898           }\r
899        }\r
900 \r
901     } else {\r
902 \r
903        for (ff = BOARD_LEFT+1; ff < BOARD_RGHT-1; ff++) {\r
904           if (board[BOARD_HEIGHT-1][ff] == BlackKing) {\r
905              for (ft = BOARD_LEFT+0; ft < BOARD_RGHT; ft++) {\r
906                 if (board[BOARD_HEIGHT-1][ft] == BlackRook) {\r
907                    callback(board, flags, \r
908                             (ft > ff) ? BlackHSideCastleFR : BlackASideCastleFR,\r
909                             BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);\r
910                 }\r
911              }\r
912           }\r
913        }\r
914     }\r
915 \r
916     /* POP Fabien */\r
917 \r
918     return FALSE;\r
919 }\r
920 \r
921 \r
922 typedef struct {\r
923     int rking, fking;\r
924     int check;\r
925 } CheckTestClosure;\r
926 \r
927 \r
928 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,\r
929                                  int rf, int ff, int rt, int ft,\r
930                                  VOIDSTAR closure));\r
931 \r
932 \r
933 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
934      Board board;\r
935      int flags;\r
936      ChessMove kind;\r
937      int rf, ff, rt, ft;\r
938      VOIDSTAR closure;\r
939 {\r
940     register CheckTestClosure *cl = (CheckTestClosure *) closure;\r
941 \r
942     if (rt == cl->rking && ft == cl->fking) cl->check++;\r
943 }\r
944 \r
945 \r
946 /* If the player on move were to move from (rf, ff) to (rt, ft), would\r
947    he leave himself in check?  Or if rf == -1, is the player on move\r
948    in check now?  enPassant must be TRUE if the indicated move is an\r
949    e.p. capture.  The possibility of castling out of a check along the\r
950    back rank is not accounted for (i.e., we still return nonzero), as\r
951    this is illegal anyway.  Return value is the number of times the\r
952    king is in check. */ \r
953 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)\r
954      Board board;\r
955      int flags;\r
956      int rf, ff, rt, ft, enPassant;\r
957 {\r
958     CheckTestClosure cl;\r
959     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;\r
960     ChessSquare captured = EmptySquare;\r
961     /*  Suppress warnings on uninitialized variables    */\r
962 \r
963     if(gameInfo.variant == VariantXiangqi)\r
964         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;\r
965     if(gameInfo.variant == VariantKnightmate)\r
966         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;\r
967 \r
968     if (rf >= 0) {\r
969         if (enPassant) {\r
970             captured = board[rf][ft];\r
971             board[rf][ft] = EmptySquare;\r
972         } else {\r
973             captured = board[rt][ft];\r
974         }\r
975         board[rt][ft] = board[rf][ff];\r
976         board[rf][ff] = EmptySquare;\r
977     }\r
978 \r
979     /* For compatibility with ICS wild 9, we scan the board in the\r
980        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,\r
981        and we test only whether that one is in check. */\r
982     cl.check = 0;\r
983     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)\r
984         for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {\r
985           if (board[cl.rking][cl.fking] == king) {\r
986               GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,\r
987                              CheckTestCallback, (VOIDSTAR) &cl);\r
988               goto undo_move;  /* 2-level break */\r
989           }\r
990       }\r
991 \r
992   undo_move:\r
993 \r
994     if (rf >= 0) {\r
995         board[rf][ff] = board[rt][ft];\r
996         if (enPassant) {\r
997             board[rf][ft] = captured;\r
998             board[rt][ft] = EmptySquare;\r
999         } else {\r
1000             board[rt][ft] = captured;\r
1001         }\r
1002     }\r
1003 \r
1004     return cl.check;\r
1005 }\r
1006 \r
1007 \r
1008 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,\r
1009                                     int rf, int ff, int rt, int ft,\r
1010                                     VOIDSTAR closure));\r
1011 \r
1012 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
1013      Board board;\r
1014      int flags;\r
1015      ChessMove kind;\r
1016      int rf, ff, rt, ft;\r
1017      VOIDSTAR closure;\r
1018 {\r
1019     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;\r
1020 \r
1021     if (appData.debugMode) {\r
1022         fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);\r
1023     }\r
1024     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)\r
1025       cl->kind = kind;\r
1026 }\r
1027 \r
1028 ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)\r
1029      Board board;\r
1030      int flags, epfile;\r
1031      int rf, ff, rt, ft, promoChar;\r
1032      char castlingRights[];\r
1033 {\r
1034     LegalityTestClosure cl;\r
1035     \r
1036     cl.rf = rf;\r
1037     cl.ff = ff;\r
1038     cl.rt = rt;\r
1039     cl.ft = ft;\r
1040     cl.kind = IllegalMove;\r
1041     GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);\r
1042     if (promoChar != NULLCHAR && promoChar != 'x') {\r
1043         if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {\r
1044             cl.kind = \r
1045               PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);\r
1046         } else {\r
1047             cl.kind = IllegalMove;\r
1048         }\r
1049     }\r
1050     return cl.kind;\r
1051 }\r
1052 \r
1053 typedef struct {\r
1054     int count;\r
1055 } MateTestClosure;\r
1056 \r
1057 extern void MateTestCallback P((Board board, int flags, ChessMove kind,\r
1058                                 int rf, int ff, int rt, int ft,\r
1059                                 VOIDSTAR closure));\r
1060 \r
1061 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
1062      Board board;\r
1063      int flags;\r
1064      ChessMove kind;\r
1065      int rf, ff, rt, ft;\r
1066      VOIDSTAR closure;\r
1067 {\r
1068     register MateTestClosure *cl = (MateTestClosure *) closure;\r
1069 \r
1070     cl->count++;\r
1071 }\r
1072 \r
1073 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */\r
1074 int MateTest(board, flags, epfile, castlingRights)\r
1075      Board board;\r
1076      int flags, epfile;\r
1077      char castlingRights[];\r
1078 {\r
1079     MateTestClosure cl;\r
1080     int inCheck;\r
1081 \r
1082     cl.count = 0;\r
1083     inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);\r
1084     if (cl.count > 0) {\r
1085         return inCheck ? MT_CHECK : MT_NONE;\r
1086     } else {\r
1087         return inCheck || gameInfo.variant == VariantXiangqi ?\r
1088                          MT_CHECKMATE : MT_STALEMATE;\r
1089     }\r
1090 }\r
1091 \r
1092      \r
1093 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,\r
1094                                     int rf, int ff, int rt, int ft,\r
1095                                     VOIDSTAR closure));\r
1096 \r
1097 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
1098      Board board;\r
1099      int flags;\r
1100      ChessMove kind;\r
1101      int rf, ff, rt, ft;\r
1102      VOIDSTAR closure;\r
1103 {\r
1104     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;\r
1105 \r
1106     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]) &&\r
1107         (cl->rfIn == -1 || cl->rfIn == rf) &&\r
1108         (cl->ffIn == -1 || cl->ffIn == ff) &&\r
1109         (cl->rtIn == -1 || cl->rtIn == rt) &&\r
1110         (cl->ftIn == -1 || cl->ftIn == ft)) {\r
1111 \r
1112         cl->count++;\r
1113         cl->piece = board[rf][ff];\r
1114         cl->rf = rf;\r
1115         cl->ff = ff;\r
1116         cl->rt = rt;\r
1117         cl->ft = ft;\r
1118         cl->kind = kind;\r
1119     }\r
1120 }\r
1121 \r
1122 void Disambiguate(board, flags, epfile, closure)\r
1123      Board board;\r
1124      int flags, epfile;\r
1125      DisambiguateClosure *closure;\r
1126 {\r
1127     int illegal = 0;\r
1128     closure->count = 0;\r
1129     closure->rf = closure->ff = closure->rt = closure->ft = 0;\r
1130     closure->kind = ImpossibleMove;\r
1131     GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);\r
1132     if (closure->count == 0) {\r
1133         /* See if it's an illegal move due to check */\r
1134         illegal = 1;\r
1135         GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,\r
1136                  (VOIDSTAR) closure);   \r
1137         if (closure->count == 0) {\r
1138             /* No, it's not even that */\r
1139             return;\r
1140         }\r
1141     }\r
1142     if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {\r
1143         if (closure->kind == WhitePromotionQueen\r
1144             || closure->kind == BlackPromotionQueen) {\r
1145             closure->kind = \r
1146               PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,\r
1147                                   closure->promoCharIn);\r
1148         } else {\r
1149             closure->kind = IllegalMove;\r
1150         }\r
1151     }\r
1152     closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));\r
1153     if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;\r
1154     if (closure->count > 1) {\r
1155         closure->kind = AmbiguousMove;\r
1156     }\r
1157     if (illegal) {\r
1158         /* Note: If more than one illegal move matches, but no legal\r
1159            moves, we return IllegalMove, not AmbiguousMove.  Caller\r
1160            can look at closure->count to detect this.\r
1161         */\r
1162         closure->kind = IllegalMove;\r
1163     }\r
1164 }\r
1165 \r
1166 \r
1167 typedef struct {\r
1168     /* Input */\r
1169     ChessSquare piece;\r
1170     int rf, ff, rt, ft;\r
1171     /* Output */\r
1172     ChessMove kind;\r
1173     int rank;\r
1174     int file;\r
1175     int either;\r
1176 } CoordsToAlgebraicClosure;\r
1177 \r
1178 extern void CoordsToAlgebraicCallback P((Board board, int flags,\r
1179                                          ChessMove kind, int rf, int ff,\r
1180                                          int rt, int ft, VOIDSTAR closure));\r
1181 \r
1182 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
1183      Board board;\r
1184      int flags;\r
1185      ChessMove kind;\r
1186      int rf, ff, rt, ft;\r
1187      VOIDSTAR closure;\r
1188 {\r
1189     register CoordsToAlgebraicClosure *cl =\r
1190       (CoordsToAlgebraicClosure *) closure;\r
1191 \r
1192     if (rt == cl->rt && ft == cl->ft &&\r
1193         board[rf][ff] == cl->piece) {\r
1194         if (rf == cl->rf) {\r
1195             if (ff == cl->ff) {\r
1196                 cl->kind = kind; /* this is the move we want */\r
1197             } else {\r
1198                 cl->file++; /* need file to rule out this move */\r
1199             }\r
1200         } else {\r
1201             if (ff == cl->ff) {\r
1202                 cl->rank++; /* need rank to rule out this move */\r
1203             } else {\r
1204                 cl->either++; /* rank or file will rule out this move */\r
1205             }\r
1206         }           \r
1207     }\r
1208 }\r
1209 \r
1210 /* Convert coordinates to normal algebraic notation.\r
1211    promoChar must be NULLCHAR or 'x' if not a promotion.\r
1212 */\r
1213 ChessMove CoordsToAlgebraic(board, flags, epfile,\r
1214                             rf, ff, rt, ft, promoChar, out)\r
1215      Board board;\r
1216      int flags, epfile;\r
1217      int rf, ff, rt, ft;\r
1218      int promoChar;\r
1219      char out[MOVE_LEN];\r
1220 {\r
1221     ChessSquare piece;\r
1222     ChessMove kind;\r
1223     char *outp = out;\r
1224     CoordsToAlgebraicClosure cl;\r
1225     \r
1226     if (rf == DROP_RANK) {\r
1227         /* Bughouse piece drop */\r
1228         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));\r
1229         *outp++ = '@';\r
1230         *outp++ = ft + AAA;\r
1231         if(rt+ONE <= '9')\r
1232            *outp++ = rt + ONE;\r
1233         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
1234         *outp = NULLCHAR;\r
1235         AlphaRank(out, 5);\r
1236         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;\r
1237     }\r
1238 \r
1239     if (promoChar == 'x') promoChar = NULLCHAR;\r
1240     piece = board[rf][ff];\r
1241     switch (piece) {\r
1242       case WhitePawn:\r
1243       case BlackPawn:\r
1244         kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);\r
1245         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {\r
1246             /* Keep short notation if move is illegal only because it\r
1247                leaves the player in check, but still return IllegalMove */\r
1248             kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,\r
1249                                rf, ff, rt, ft, promoChar);\r
1250             if (kind == IllegalMove) break;\r
1251             kind = IllegalMove;\r
1252         }\r
1253         /* Pawn move */\r
1254         *outp++ = ff + AAA;\r
1255         if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */\r
1256             /* Non-capture; use style "e5" */\r
1257             if(rt+ONE <= '9')\r
1258                *outp++ = rt + ONE;\r
1259             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
1260         } else {\r
1261             /* Capture; use style "exd5" */\r
1262             if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )\r
1263             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */\r
1264             *outp++ = ft + AAA;\r
1265             if(rt+ONE <= '9')\r
1266                *outp++ = rt + ONE;\r
1267             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
1268         }\r
1269         /* Use promotion suffix style "=Q" */\r
1270         if (promoChar != NULLCHAR && promoChar != 'x') {\r
1271             *outp++ = '=';\r
1272             *outp++ = ToUpper(promoChar);\r
1273         }\r
1274         *outp = NULLCHAR;\r
1275         AlphaRank(out, 10);\r
1276         return kind;\r
1277 \r
1278         \r
1279       case WhiteKing:\r
1280       case BlackKing:\r
1281         /* Fabien moved code: FRC castling first (if KxR), wild castling second */\r
1282         /* Code added by Tord:  FRC castling. */\r
1283         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||\r
1284            (piece == BlackKing && board[rt][ft] == BlackRook)) {\r
1285           if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");\r
1286             return LegalityTest(board, flags, epfile, initialRights,\r
1287                                 rf, ff, rt, ft, promoChar);\r
1288         }\r
1289         /* End of code added by Tord */\r
1290         /* Test for castling or ICS wild castling */\r
1291         /* Use style "O-O" (oh-oh) for PGN compatibility */\r
1292         else if (rf == rt &&\r
1293             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&\r
1294             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||\r
1295              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {\r
1296             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)\r
1297                 strcpy(out, "O-O");\r
1298             else\r
1299                 strcpy(out, "O-O-O");\r
1300 \r
1301             /* This notation is always unambiguous, unless there are\r
1302                kings on both the d and e files, with "wild castling"\r
1303                possible for the king on the d file and normal castling\r
1304                possible for the other.  ICS rules for wild 9\r
1305                effectively make castling illegal for either king in\r
1306                this situation.  So I am not going to worry about it;\r
1307                I'll just generate an ambiguous O-O in this case.\r
1308             */\r
1309             return LegalityTest(board, flags, epfile, initialRights,\r
1310                                 rf, ff, rt, ft, promoChar);\r
1311         }\r
1312 \r
1313         /* else fall through */\r
1314       default:\r
1315         /* Piece move */\r
1316         cl.rf = rf;\r
1317         cl.ff = ff;\r
1318         cl.rt = rt;\r
1319         cl.ft = ft;\r
1320         cl.piece = piece;\r
1321         cl.kind = IllegalMove;\r
1322         cl.rank = cl.file = cl.either = 0;\r
1323         GenLegal(board, flags, epfile, initialRights,\r
1324                  CoordsToAlgebraicCallback, (VOIDSTAR) &cl);\r
1325 \r
1326         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {\r
1327             /* Generate pretty moves for moving into check, but\r
1328                still return IllegalMove.\r
1329             */\r
1330             GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,\r
1331                      CoordsToAlgebraicCallback, (VOIDSTAR) &cl);\r
1332             if (cl.kind == IllegalMove) break;\r
1333             cl.kind = IllegalMove;\r
1334         }\r
1335 \r
1336         /* Style is "Nf3" or "Nxf7" if this is unambiguous,\r
1337            else "Ngf3" or "Ngxf7",\r
1338            else "N1f3" or "N5xf7",\r
1339            else "Ng1f3" or "Ng5xf7".\r
1340         */\r
1341         *outp++ = ToUpper(PieceToChar(piece));\r
1342         \r
1343         if (cl.file || (cl.either && !cl.rank)) {\r
1344             *outp++ = ff + AAA;\r
1345         }\r
1346         if (cl.rank) {\r
1347             if(rf+ONE <= '9')\r
1348                 *outp++ = rf + ONE;\r
1349             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }\r
1350         }\r
1351 \r
1352         if(board[rt][ft] != EmptySquare)\r
1353           *outp++ = 'x';\r
1354 \r
1355         *outp++ = ft + AAA;\r
1356         if(rt+ONE <= '9')\r
1357            *outp++ = rt + ONE;\r
1358         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
1359         *outp = NULLCHAR;\r
1360         AlphaRank(out, 10);\r
1361         return cl.kind;\r
1362         \r
1363 #ifdef FAIRY\r
1364       /* [HGM] Always long notation for fairies, don't know how they move */\r
1365       case WhiteNightrider:\r
1366       case BlackNightrider:\r
1367       case WhiteGrasshopper:\r
1368       case BlackGrasshopper:\r
1369 #endif\r
1370       case EmptySquare:\r
1371         /* Moving a nonexistent piece */\r
1372         break;\r
1373     }\r
1374     \r
1375     /* Not a legal move, even ignoring check.\r
1376        If there was a piece on the from square, \r
1377        use style "Ng1g3" or "Ng1xe8";\r
1378        if there was a pawn or nothing (!),\r
1379        use style "g1g3" or "g1xe8".  Use "x"\r
1380        if a piece was on the to square, even\r
1381        a piece of the same color.\r
1382     */\r
1383     outp = out;\r
1384     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {\r
1385         *outp++ = ToUpper(PieceToChar(piece));\r
1386     }\r
1387     *outp++ = ff + AAA;\r
1388     if(rf+ONE <= '9')\r
1389        *outp++ = rf + ONE;\r
1390     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }\r
1391     if (board[rt][ft] != EmptySquare) *outp++ = 'x';\r
1392     *outp++ = ft + AAA;\r
1393     if(rt+ONE <= '9')\r
1394        *outp++ = rt + ONE;\r
1395     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
1396     /* Use promotion suffix style "=Q" */\r
1397     if (promoChar != NULLCHAR && promoChar != 'x') {\r
1398         *outp++ = '=';\r
1399         *outp++ = ToUpper(promoChar);\r
1400     }\r
1401     *outp = NULLCHAR;\r
1402 \r
1403     AlphaRank(out, 0);\r
1404     return IllegalMove;\r
1405 }\r
1406 \r
1407 \r