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