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