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