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