Implement Spartan Chess
[capablanca.git] / lasker-2.2.3 / src / algcheck.c
1 /*
2    Copyright (c) 1993 Richard V. Nash.
3    Copyright (c) 2000 Dan Papasian
4    Copyright (C) Andrew Tridgell 2002
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21
22 #include "includes.h"
23
24 /* Well, lets see if I can list the possibilities
25  * Piece moves
26  * Ne4
27  * Nxe4
28  * Nce4
29  * Ncxe4
30  * R2f3
31  * R2xf3
32  * Special pawn moves
33  * e4
34  * ed
35  * exd
36  * exd5
37  * ed5
38  * Drop moves (bughouse, board edit)
39  * P@f7 P*f7
40  * #f7 #Nf7
41  * (o-o, o-o-o) Castling is handled earlier, so don't worry about that
42  * Of course any of these can have a + or ++ or = string on the end, just
43  * cut that off.
44  */
45
46 /* f - file
47  * r - rank
48  * p - piece
49  * x - x
50  * @ - drop character (bughouse)
51  */
52 static char *alg_list[] = {
53   "fxfr", "pxfr",               /* These two get confused in case of bishop */
54   "ffr", "pfr",                 /* These two get confused in case of bishop */
55   "pffr",
56   "pfxfr",
57   "prfr",
58   "prxfr",
59   "fr",
60   "ff",
61   "fxf",
62   "p@fr",
63   "#fr",
64   "#pfr",
65   NULL
66 };
67
68 #define ALG_UNKNOWN -1
69
70 static int get_move_info(const char *str, int *piece, int *ff, int *fr, int *tf, int *tr, int *bishconfusion)
71 {
72   char tmp[1024];
73   char *s;
74   int i, j, len;
75   char c;
76   int matchVal = -1;
77   int lpiece, lff, lfr, ltf, ltr;
78
79   *bishconfusion = 0;
80   strlcpy(tmp, str, sizeof(tmp));
81   if ((s = strchr(tmp, '+'))) { /* Cut off any check marks */
82     *s = '\0';
83   }
84   if ((s = strchr(tmp, '='))) { /* Cut off any promotion marks */
85     *s = '\0';
86   }
87   if ((s = strchr(tmp, '#'))) { /* Cut off any 'mates' marks */
88     *s = '\0';
89   }
90   *piece = *ff = *fr = *tf = *tr = ALG_UNKNOWN;
91   len = strlen(tmp);
92   for (i = 0; alg_list[i]; i++) {
93     lpiece = lff = lfr = ltf = ltr = ALG_UNKNOWN;
94     if (strlen(alg_list[i]) != len)
95       continue;
96     for (j = len - 1; j >= 0; j--) {
97       switch (alg_list[i][j]) {
98       case 'f':
99         if ((tmp[j] < 'a') || (tmp[j] > 'l')) // [HGM] upto l-file
100           goto nomatch;
101         if (ltf == ALG_UNKNOWN)
102           ltf = tmp[j] - 'a';
103         else
104           lff = tmp[j] - 'a';
105         break;
106       case 'r':
107         if ((tmp[j] < '0') || (tmp[j] > '9')) // [HGM] also match 0- and 9-rank
108           goto nomatch;
109         if (ltr == ALG_UNKNOWN)
110           ltr = tmp[j] - '0'; // [HGM] allow 0-rank for Xiangqi, correct later
111         else
112           lfr = tmp[j] - '0';
113         break;
114       case 'p':
115         if (isupper(tmp[j]))
116           c = tolower(tmp[j]);
117         else
118           c = tmp[j];
119         if (c == 'k')
120           lpiece = KING;
121         else if (c == 'e')   // [HGM] note that som piece indicators are ambiguous,
122           lpiece = ELEPHANT; //       and their true meaning depends on the variant,
123         else if (c == 'v')   //       which we do not know at this point.
124           lpiece = CENTAUR;
125         else if (c == 's')
126           lpiece = SILVER;
127         else if (c == 'g')
128           lpiece = GOLD;
129         else if (c == 'l')
130           lpiece = LANCE;
131         else if (c == 'f')
132           lpiece = FERZ;
133         else if (c == 'h')
134           lpiece = HORSE;
135         else if (c == 'w')
136           lpiece = WAZIR;
137         else if (c == 'o')
138           lpiece = SQUIRREL;
139         else if (c == 'q')
140           lpiece = QUEEN;
141         else if (c == 'c')
142           lpiece = MARSHALL;
143         else if (c == 'a')
144           lpiece = CARDINAL;
145         else if (c == 'm')
146           lpiece = MAN;
147         else if (c == 'r')
148           lpiece = ROOK;
149         else if (c == 'b')
150           lpiece = BISHOP;
151         else if (c == 'n')
152           lpiece = KNIGHT;
153         else if (c == 'p')
154           lpiece = PAWN;
155         else if (c == 'd')
156           lpiece = DRAGONKING;
157         else
158           goto nomatch;
159         break;
160       case 'x':
161         if ((tmp[j] != 'x') && (tmp[j] != 'X'))
162           goto nomatch;
163         break;
164       case '@':
165         if (tmp[j] != '@' && tmp[j] != '*')
166           goto nomatch;
167         lff = lfr = ALG_DROP;
168         break;
169       case '#':
170         if (tmp[j] != '#')
171           goto nomatch;
172         lff = lfr = ALG_DROP;
173         break;
174       default:
175         d_printf( "Unknown character in algebraic parsing\n");
176         break;
177       }
178     }
179     if (lpiece == ALG_UNKNOWN)
180       lpiece = PAWN;
181     if (lpiece == PAWN && (lfr == ALG_UNKNOWN)) {       /* ffr or ff */
182       if (lff != ALG_UNKNOWN) {
183         if (lff == ltf)
184           goto nomatch;
185         if ((lff - ltf != 1) && (ltf - lff != 1))
186           goto nomatch;
187       }
188     }
189     *piece = lpiece;            /* We have a match */
190     *tf = ltf;
191     *tr = ltr;
192     *ff = lff;
193     *fr = lfr;
194     if (matchVal != -1) {
195       /* We have two matches, it must be that Bxc4 vs. bxc4 problem */
196       /* Or it could be the Bc4 vs bc4 problem */
197       *bishconfusion = 1;
198     }
199     matchVal = i;
200 nomatch:;
201   }
202
203   if (matchVal != -1)
204     return MS_ALG;
205   else
206     return MS_NOTMOVE;
207 }
208
209 int alg_is_move(const char *mstr)
210 {
211         int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
212
213         return get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc);
214 }
215
216 /* add any promotion qualifier from a move string */
217 static void add_promotion(struct game_state_t *gs, const char *mstr, struct move_t * mt)
218 {
219         char *s;
220         int piece, i;
221         s = strchr(mstr, '=');
222         if (s == NULL) {
223                 return;
224         }
225         if(gs->promoType == 3) { // handle Shogi promotions
226                 piece = gs->board[mt->fromFile][mt->fromRank];
227                 if(colorval(piece) == WHITE && mt->fromRank < gs->ranks - gs->ranks/3
228                                             && mt->toRank   < gs->ranks - gs->ranks/3 ) return;
229                 if(colorval(piece) == BLACK && mt->fromRank >= gs->ranks/3
230                                             && mt->toRank   >= gs->ranks/3 ) return;
231                 switch(piecetype(piece)) {
232                     case PAWN:
233                     case LANCE:
234                     case HONORABLEHORSE:
235                     case SILVER:
236                         if(s[1] != '+' && s[1] != '^' && s[1] != 'G' && s[1] != 'g') return;
237                         piece = GOLD; break;
238                     case BISHOP:
239                         if(s[1] != '+' && s[1] != '^' && s[1] != 'H' && s[1] != 'h') return;
240                         piece = DRAGONHORSE; break;
241                     case ROOK:
242                         if(s[1] != '+' && s[1] != '^' && s[1] != 'D' && s[1] != 'd') return;
243                         piece = DRAGONKING; break;
244                     default: return; // others do not promote, so ignore
245                 }
246                 mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
247                 return;
248         }
249
250       if(gs->drops != 2 || (gs->onMove == WHITE ? 0 : gs->ranks-1) != mt->fromRank) { // [HGM] always accept if backrank mover in Seirawan
251         if (piecetype(gs->board[mt->fromFile][mt->fromRank]) != PAWN &&
252             piecetype(gs->board[mt->fromFile][mt->fromRank]) != HOPLITE) {
253                 return;
254         }
255         if (mt->toRank < gs->ranks - gs->promoZone && mt->toRank >= gs->promoZone) {
256                 return;
257         }
258       }
259
260         switch (tolower(s[1])) {
261         case 'f':
262                 piece = FERZ2;
263                 break;
264         case 'q':
265                 piece = QUEEN;
266                 break;
267         case 'c':
268                 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) == HOPLITE) piece = CAPTAIN; else
269                 if(!gs->capablancaPieces) return; // [HGM] should make variant-dependent piece mask
270                 piece = MARSHALL;
271                 break;
272         case 'a':
273                 if(!gs->capablancaPieces) return;
274                 piece = CARDINAL;
275                 break;
276         case 'm':
277                 if(!gs->royalKnight) return; // [HGM] only in knightmate
278                 piece = MAN;
279                 break;
280         case 'r':
281                 piece = ROOK;
282                 break;
283         case 'b':
284                 piece = BISHOP;
285                 break;
286         case 'n':
287                 if(gs->royalKnight) return; // [HGM] not in knightmate
288                 piece = KNIGHT;
289                 break;
290         // Superchess promotons: filtered out later by promoType
291         case 'g':
292                 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) == HOPLITE) piece = GENERAL; else
293                 piece = MASTODON;
294                 break;
295         case 'o':
296                 piece = SQUIRREL;
297                 break;
298         case 'w':
299                 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) == HOPLITE) piece = WARLORD; else
300                 piece = WOODY;
301                 break;
302         case 'k':
303                 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) != HOPLITE) return;
304                 piece = KING;
305                 break;
306         case 'l':
307                 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) != HOPLITE) return;
308                 piece = LIEUTENANT;
309                 break;
310         case 'v':
311                 piece = CENTAUR;
312                 break;
313         case 'e':
314                 piece = gs->drops == 2 ? SELEPHANT : EMPRESS; // for Seirawan
315                 break;
316         case 's':
317                 piece = PRINCESS;
318                 break;
319         case 'h':
320                 if(gs->drops != 2) return;
321                 piece = HAWK;
322                 break;
323         default:
324                 return;
325         }
326         i = colorval(gs->board[mt->fromFile][mt->fromRank]) == WHITE ? 0 : 1;
327         if(gs->promoType == 2 && gs->holding[i][piece-1] == 0) return; // only if piece was captured
328         if(piece >= WOODY && piece < KING && (gs->promoType != 2 || gs->promoZone == 3)) return; // reserved for Superchess
329
330         mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
331 }
332
333 /* We already know it is algebraic, get the move squares */
334 int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
335 {
336         int f=0, r=0, tmpr=0, posf=0, posr=0, posr2=0;
337         int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
338
339   if (get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc) != MS_ALG) {
340     d_printf( "CHESSD: Shouldn't try to algebraicly parse non-algabraic move string.\n");
341     return MOVE_ILLEGAL;
342   }
343   // [HGM] check if move does not stray off board
344   if(gs->ranks < 10) { 
345     if(tr == 0 || fr == 0) return MOVE_ILLEGAL; // used nonexistent 0-rank
346     if(tr != ALG_UNKNOWN) tr--; if(fr != ALG_UNKNOWN) fr--; // shift to lowest rank = 1
347   }
348   if(tr >= gs->ranks || fr >= gs->ranks || tf >= gs->files || ff >= gs->files)
349     return MOVE_ILLEGAL;
350
351   // [HGM] resolve ambiguity in piece, type based on variant
352   switch(piece) {
353     case ELEPHANT:
354       if(strstr(gs->variant, "super"))   piece = EMPRESS; else
355       if(strstr(gs->variant, "seirawan"))piece = SELEPHANT; else
356       if(strstr(gs->variant, "great"))   piece = MODERNELEPHANT; else
357       if(strstr(gs->variant, "courier")) piece = ALFIL2;
358       break;
359     case CARDINAL:
360       if(strstr(gs->variant, "super")) piece = AMAZON; else
361       if(strstr(gs->variant, "xiangqi")) piece = MANDARIN;
362       break;
363     case MARSHALL:
364       if(strstr(gs->variant, "xiangqi")) piece = CANNON;
365       break;
366     case SILVER:
367       if(strstr(gs->variant, "super")) piece = PRINCESS;
368       if(strstr(gs->variant, "great")) piece = MAN2;
369       break;
370     case BISHOP:
371       if(strstr(gs->variant, "shatranj")) piece = ALFIL;
372       break;
373     case QUEEN:
374       if(strstr(gs->variant, "shatranj")) piece = FERZ;
375       break;
376     case WAZIR:
377       if(strstr(gs->variant, "super")) piece = WOODY;
378       break;
379     case KNIGHT:
380       if(strstr(gs->variant, "shogi")) piece = HONORABLEHORSE;
381       break;
382     case MAN:
383       if(strstr(gs->variant, "great")) piece = MINISTER;
384       break;
385     case HORSE:
386       if(strstr(gs->variant, "shogi")) piece = DRAGONHORSE; else
387       if(strstr(gs->variant, "seirawan")) piece = HAWK; else
388       if(strstr(gs->variant, "great")) piece = PRIESTESS;
389       break;
390     case GOLD:
391       if(strstr(gs->variant, "great")) piece = MASTODON;
392       break;
393   }
394
395   /* Resolve ambiguities in to-ness */
396   if (tf == ALG_UNKNOWN) {
397           d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
398           return MOVE_AMBIGUOUS;        /* Must always know to file */
399   }
400   if (tr == ALG_UNKNOWN) {
401     posr = posr2 = ALG_UNKNOWN;
402     if (piece != PAWN) {
403             d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
404             return MOVE_AMBIGUOUS;
405     }
406     if (ff == ALG_UNKNOWN) {
407             d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
408             return MOVE_AMBIGUOUS;
409     }
410     /* Need to find pawn on ff that can take to tf and fill in ranks */
411     for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
412          NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
413       if ((ff != ALG_UNKNOWN) && (ff != f))
414         continue;
415       if (piecetype(gs->board[f][r]) != piece)
416         continue;
417       if (gs->onMove == WHITE) {
418         tmpr = r + 1;
419       } else {
420         tmpr = r - 1;
421       }
422 /*      if ((gs->board[tf][tmpr] == NOPIECE) ||
423           (iscolor(gs->board[tf][tmpr], gs->onMove))) continue;*/
424 /* patch from Soso, added by Sparky 3/16/95                    */
425       if (gs->board[tf][tmpr] == NOPIECE) {
426         if ((gs->ep_possible[((gs->onMove == WHITE) ? 0 : 1)][ff]) != (tf - ff))
427           continue;
428       } else {
429         if (iscolor(gs->board[tf][tmpr], gs->onMove))
430           continue;
431       }
432
433       if (legal_andcheck_move(gs, f, r, tf, tmpr)) {
434               if ((posr != ALG_UNKNOWN) && (posr2 != ALG_UNKNOWN)) {
435                       d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
436                       return MOVE_AMBIGUOUS;
437               }
438         posr = tmpr;
439         posr2 = r;
440       }
441     }
442     tr = posr;
443     fr = posr2;
444   } else if (bc) {              /* Could be bxc4 or Bxc4, tr is known */
445     ff = ALG_UNKNOWN;
446     fr = ALG_UNKNOWN;
447     for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
448          NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
449             if ((piecetype(gs->board[f][r]) != PAWN) && (piecetype(gs->board[f][r]) != piece)) {
450                     // note that the interpretation Bxc4 is matched last, and has set piece to BISHOP
451                     continue;
452             }
453             if (legal_andcheck_move(gs, f, r, tf, tr)) {
454                     if ((piecetype(gs->board[f][r]) == PAWN) && (f != tolower(mstr[0]) - 'a')) {
455                             continue;
456                     }
457
458                     /* if its a lowercase 'b' then prefer the pawn move if there is one */
459                     if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
460                         piecetype(gs->board[f][r]) == PAWN && mstr[0] >= 'a') {
461                             ff = f;
462                             fr = r;
463                             continue;
464                     }
465
466                     if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
467                         piecetype(gs->board[ff][fr]) == PAWN && mstr[0] >= 'a') {
468                             continue;
469                     }
470
471                     if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN)) {
472                             d_printf("Ambiguous %s(%d) mstr=%s\n", __FUNCTION__, __LINE__, mstr);
473                             return (MOVE_AMBIGUOUS);
474                     }
475                     ff = f;
476                     fr = r;
477             }
478     }
479   } else {                      /* The from position is unknown */
480     posf = ALG_UNKNOWN;
481     posr = ALG_UNKNOWN;
482     if ((ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) {
483       /* Need to find a piece that can go to tf, tr */
484       for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
485            NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
486         if ((ff != ALG_UNKNOWN) && (ff != f))
487           continue;
488         if ((fr != ALG_UNKNOWN) && (fr != r))
489           continue;
490         if (piecetype(gs->board[f][r]) != piece)
491           continue;
492         if (legal_andcheck_move(gs, f, r, tf, tr)) {
493                 if ((posf != ALG_UNKNOWN) && (posr != ALG_UNKNOWN)) {
494                         d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
495                         return MOVE_AMBIGUOUS;
496                 }
497           posf = f;
498           posr = r;
499         }
500       }
501     } else if (ff == ALG_DROP) {
502       if (legal_andcheck_move(gs, ALG_DROP, piece, tf, tr)) {
503         posf = ALG_DROP;
504         posr = piece;
505       }
506     }
507     ff = posf;
508     fr = posr;
509   }
510   if ((tf == ALG_UNKNOWN) || (tr == ALG_UNKNOWN) ||
511       (ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN))
512     return MOVE_ILLEGAL;
513   mt->fromFile = ff;
514   mt->fromRank = fr;
515   mt->toFile = tf;
516   mt->toRank = tr;
517
518   add_promotion(gs, mstr, mt);
519
520   return MOVE_OK;
521 }
522
523 /* A assumes the move has yet to be made on the board */
524
525 /* Soso: rewrote alg_unparse function.
526  * Algebraic deparser - sets the mStr variable with move description
527  * in short notation. Used in last move report and in 'moves' command.
528  */
529
530 char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
531 {
532   static char mStr[20];
533   char tmp[20];
534   int piece, f, r;
535   int ambig, r_ambig, f_ambig;
536   struct game_state_t fakeMove;
537
538   if (mt->fromFile == ALG_DROP) {
539     piece = mt->fromRank;
540   } else {
541     piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
542   }
543
544   if (mt->fromFile == ALG_CASTLE) {
545     int r = gs->onMove == WHITE ? 1 : gs->ranks;
546     if(mt->toFile > mt->toRank) { // [HGM] castle: K ends right of R
547       strcpy(mStr, "O-O");
548     }
549     if (mt->toFile < mt->toRank) { // [HGM] castle: K ends left of R
550       strcpy(mStr, "O-O-O");
551     }
552     if(gs->drops == 2) {
553         if(mt->piecePromotionTo < 0) snprintf(mStr, 20, "%c%de%d", mt->fromRank + 'a', r, r);
554         goto suffix; // [HGM] in Seirawan castling can have gating suffix
555     }
556     goto check;
557   }
558   strcpy(mStr, "");
559   switch (piece) {
560   case PAWN:
561     if (mt->fromFile == ALG_DROP) {
562       strcpy(mStr,"P");
563     } else if (mt->fromFile != mt->toFile 
564             || gs->board[mt->toFile][mt->toRank] != NOPIECE) { // [HGM] XQ: forward captures as "axa6"
565       sprintf(tmp, "%c", mt->fromFile + 'a');
566       strcpy(mStr, tmp);
567     }
568     break;
569   case KNIGHT:
570     strcpy(mStr, "N");
571     break;
572   case BISHOP:
573     strcpy(mStr, "B");
574     break;
575   case ROOK:
576     strcpy(mStr, "R");
577     break;
578   case ALFIL2:
579   case AMAZON:
580   case CARDINAL:
581     strcpy(mStr, "A");
582     break;
583   case CAPTAIN:
584   case CANNON:
585   case MARSHALL:
586     strcpy(mStr, "C");
587     break;
588   case MAN:
589     strcpy(mStr, "M");
590     break;
591   case FERZ:
592   case QUEEN:
593     strcpy(mStr, "Q");
594     break;
595   case EMPRESS:
596   case ELEPHANT:
597   case SELEPHANT:
598     strcpy(mStr, "E");
599     break;
600   case ALFIL:
601     strcpy(mStr, "B");
602     break;
603   case FERZ2:
604     strcpy(mStr, "F");
605     break;
606   case WARLORD:
607   case WOODY:
608   case WAZIR:
609     strcpy(mStr, "W");
610     break;
611   case SQUIRREL:
612     strcpy(mStr, "O");
613     break;
614   case CENTAUR:
615     strcpy(mStr, "V");
616     break;
617   case HORSE:
618   case DRAGONHORSE:
619   case HAWK:
620   case HOPLITE:
621     strcpy(mStr, "H");
622     break;
623   case HONORABLEHORSE:
624     strcpy(mStr, "N");
625     break;
626   case DRAGONKING:
627     strcpy(mStr, "D");
628     break;
629   case LIEUTENANT:
630   case LANCE:
631     strcpy(mStr, "L");
632     break;
633   case PRINCESS:
634   case SILVER:
635     strcpy(mStr, "S");
636     break;
637   case MASTODON:
638   case GENERAL:
639   case GOLD:
640     strcpy(mStr, "G");
641     break;
642   case MANDARIN:
643     strcpy(mStr, "A");
644     break;
645   case KING:
646     strcpy(mStr, "K");
647     break;
648   default:
649     strcpy(mStr, "");
650     break;
651   }
652
653   if (mt->fromFile == ALG_DROP) {
654     strcat(mStr, "@");
655   } else {
656   /* Checks for ambiguity in short notation ( Ncb3, R8e8 or so) */
657   if (piece != PAWN) {
658     ambig = r_ambig = f_ambig = 0;
659     for (r = 0; r < gs->ranks; r++)
660       for (f = 0; f < gs->files; f++) {
661         if ((gs->board[f][r] != NOPIECE) && iscolor(gs->board[f][r], gs->onMove)
662             && (piecetype(gs->board[f][r]) == piece) &&
663             ((f != mt->fromFile) || (r != mt->fromRank))) {
664           if (legal_move(gs, f, r, mt->toFile, mt->toRank)) {
665             fakeMove = *gs;
666             fakeMove.board[f][r] = NOPIECE;
667             fakeMove.board[mt->toFile][mt->toRank] = piece | gs->onMove;
668             fakeMove.onMove = CToggle(fakeMove.onMove);
669             gs->onMove = CToggle(gs->onMove);
670
671 #if 0
672             d_printf("possible move %c%d%c%d against %c%d%c%d\n",
673                      'a' + f, r+1,
674                      'a' + mt->toFile, mt->toRank+1,
675                      'a' + mt->fromFile, mt->fromRank+1,
676                      'a' + mt->toFile, mt->toRank+1);
677 #endif
678
679             if (!in_check(&fakeMove)) {
680                     ambig++;
681                     if (f == mt->fromFile) {
682                             ambig++;
683                             f_ambig++;
684                     }
685                     if (r == mt->fromRank) {
686                             ambig++;
687                             r_ambig++;
688                     }
689             }
690             gs->onMove = CToggle(gs->onMove);
691           }
692         }
693       }
694     if (ambig > 0) {
695       /* Ambiguity in short notation, need to add file,rank or _both_ in
696          notation */
697       if (f_ambig == 0) {
698         sprintf(tmp, "%c", mt->fromFile + 'a');
699         strcat(mStr, tmp);
700       } else if (r_ambig == 0) {
701         sprintf(tmp, "%d", mt->fromRank + 1 - (gs->ranks > 9));
702         strcat(mStr, tmp);
703       } else {
704         sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1 - (gs->ranks > 9));
705         strcat(mStr, tmp);
706       }
707     }
708   }
709   if ((gs->board[mt->toFile][mt->toRank] != NOPIECE) ||
710       ((piece == PAWN) && (mt->fromFile != mt->toFile))) {
711     strcat(mStr, "x");
712   }
713   }
714   sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));
715   strcat(mStr, tmp);
716 suffix:
717   if ((piece == PAWN || piece == HOPLITE || gs->promoType == 3 || gs->drops == 2) && (mt->piecePromotionTo != NOPIECE)) {
718     strcat(mStr, "=");          /* = before promoting piece */
719     switch (piecetype(abs(mt->piecePromotionTo))) {
720     case KING:
721       strcat(mStr, "K");
722       break;
723     case KNIGHT:
724       strcat(mStr, "N");
725       break;
726     case BISHOP:
727       strcat(mStr, "B");
728       break;
729     case ROOK:
730       strcat(mStr, "R");
731       break;
732     case CARDINAL:
733       strcat(mStr, "A");
734       break;
735     case MARSHALL:
736     case CAPTAIN:
737       strcat(mStr, "C");
738       break;
739     case MAN:
740       strcat(mStr, "M");
741       break;
742     case QUEEN:
743       strcat(mStr, "Q");
744       break;
745     case FERZ2:
746       strcat(mStr, "F");
747       break;
748     case WARLORD:
749     case WOODY:
750       strcat(mStr, "W");
751       break;
752     case SELEPHANT:
753     case EMPRESS:
754       strcat(mStr, "E");
755       break;
756     case CENTAUR:
757       strcat(mStr, "V");
758       break;
759     case PRINCESS:
760       strcat(mStr, "S");
761       break;
762     case SQUIRREL:
763       strcat(mStr, "O");
764       break;
765     case MASTODON:
766     case GENERAL:
767     case GOLD: // [HGM] Shogi promotions: avoid use of '+'
768       strcat(mStr, "G");
769       break;
770     case HAWK:
771     case DRAGONHORSE:
772       strcat(mStr, "H");
773       break;
774     case DRAGONKING:
775       strcat(mStr, "D");
776       break;
777     case LIEUTENANT:
778       strcat(mStr, "L");
779       break;
780     default:
781       break;
782     }
783   }
784 check:;
785   fakeMove = *gs;
786   execute_move(&fakeMove, mt, 0);
787   fakeMove.onMove = CToggle(fakeMove.onMove);
788   if (in_check(&fakeMove)) {
789     strcat(mStr, "+");
790   }
791   return mStr;
792 }