32bdba100a1f380d4e7ce0cf199e4a1eaf208c7c
[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] > 'h'))
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] < '1') || (tmp[j] > '8'))
108           goto nomatch;
109         if (ltr == ALG_UNKNOWN)
110           ltr = tmp[j] - '1';
111         else
112           lfr = tmp[j] - '1';
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 == 'q')
122           lpiece = QUEEN;
123         else if (c == 'r')
124           lpiece = ROOK;
125         else if (c == 'b')
126           lpiece = BISHOP;
127         else if (c == 'n')
128           lpiece = KNIGHT;
129         else if (c == 'p')
130           lpiece = PAWN;
131         else
132           goto nomatch;
133         break;
134       case 'x':
135         if ((tmp[j] != 'x') && (tmp[j] != 'X'))
136           goto nomatch;
137         break;
138       case '@':
139         if (tmp[j] != '@' && tmp[j] != '*')
140           goto nomatch;
141         lff = lfr = ALG_DROP;
142         break;
143       case '#':
144         if (tmp[j] != '#')
145           goto nomatch;
146         lff = lfr = ALG_DROP;
147         break;
148       default:
149         d_printf( "Unknown character in algebraic parsing\n");
150         break;
151       }
152     }
153     if (lpiece == ALG_UNKNOWN)
154       lpiece = PAWN;
155     if (lpiece == PAWN && (lfr == ALG_UNKNOWN)) {       /* ffr or ff */
156       if (lff != ALG_UNKNOWN) {
157         if (lff == ltf)
158           goto nomatch;
159         if ((lff - ltf != 1) && (ltf - lff != 1))
160           goto nomatch;
161       }
162     }
163     *piece = lpiece;            /* We have a match */
164     *tf = ltf;
165     *tr = ltr;
166     *ff = lff;
167     *fr = lfr;
168     if (matchVal != -1) {
169       /* We have two matches, it must be that Bxc4 vs. bxc4 problem */
170       /* Or it could be the Bc4 vs bc4 problem */
171       *bishconfusion = 1;
172     }
173     matchVal = i;
174 nomatch:;
175   }
176   if (matchVal != -1)
177     return MS_ALG;
178   else
179     return MS_NOTMOVE;
180 }
181
182 int alg_is_move(const char *mstr)
183 {
184         int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
185
186         return get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc);
187 }
188
189 /* add any promotion qualifier from a move string */
190 static void add_promotion(struct game_state_t *gs, const char *mstr, struct move_t * mt)
191 {
192         char *s;
193         int piece;
194         s = strchr(mstr, '=');
195         if (s == NULL) {
196                 return;
197         }
198         
199         if (piecetype(gs->board[mt->fromFile][mt->fromRank]) != PAWN) {
200                 return;
201         }
202         if (mt->toRank != 7 && mt->toRank != 0) {
203                 return;
204         }
205
206         switch (tolower(s[1])) {
207         case 'q':
208                 piece = QUEEN;
209                 break;
210         case 'r':
211                 piece = ROOK;
212                 break;
213         case 'b':
214                 piece = BISHOP;
215                 break;
216         case 'n':
217                 piece = KNIGHT;
218                 break;
219         default:
220                 return;
221         }
222
223         mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
224 }
225
226 /* We already know it is algebraic, get the move squares */
227 int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
228 {
229         int f=0, r=0, tmpr=0, posf=0, posr=0, posr2=0;
230         int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
231
232   if (get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc) != MS_ALG) {
233     d_printf( "CHESSD: Shouldn't try to algebraicly parse non-algabraic move string.\n");
234     return MOVE_ILLEGAL;
235   }
236   /* Resolve ambiguities in to-ness */
237   if (tf == ALG_UNKNOWN) {
238           d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
239           return MOVE_AMBIGUOUS;        /* Must always know to file */
240   }
241   if (tr == ALG_UNKNOWN) {
242     posr = posr2 = ALG_UNKNOWN;
243     if (piece != PAWN) {
244             d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
245             return MOVE_AMBIGUOUS;
246     }
247     if (ff == ALG_UNKNOWN) {
248             d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
249             return MOVE_AMBIGUOUS;
250     }
251     /* Need to find pawn on ff that can take to tf and fill in ranks */
252     for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
253          NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
254       if ((ff != ALG_UNKNOWN) && (ff != f))
255         continue;
256       if (piecetype(gs->board[f][r]) != piece)
257         continue;
258       if (gs->onMove == WHITE) {
259         tmpr = r + 1;
260       } else {
261         tmpr = r - 1;
262       }
263 /*      if ((gs->board[tf][tmpr] == NOPIECE) ||
264           (iscolor(gs->board[tf][tmpr], gs->onMove))) continue;*/
265 /* patch from Soso, added by Sparky 3/16/95                    */
266       if (gs->board[tf][tmpr] == NOPIECE) {
267         if ((gs->ep_possible[((gs->onMove == WHITE) ? 0 : 1)][ff]) != (tf - ff))
268           continue;
269       } else {
270         if (iscolor(gs->board[tf][tmpr], gs->onMove))
271           continue;
272       }
273
274       if (legal_andcheck_move(gs, f, r, tf, tmpr)) {
275               if ((posr != ALG_UNKNOWN) && (posr2 != ALG_UNKNOWN)) {
276                       d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
277                       return MOVE_AMBIGUOUS;
278               }
279         posr = tmpr;
280         posr2 = r;
281       }
282     }
283     tr = posr;
284     fr = posr2;
285   } else if (bc) {              /* Could be bxc4 or Bxc4, tr is known */
286     ff = ALG_UNKNOWN;
287     fr = ALG_UNKNOWN;
288     for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
289          NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
290             if ((piecetype(gs->board[f][r]) != PAWN) && (piecetype(gs->board[f][r]) != BISHOP)) {
291                     continue;
292             }
293             if (legal_andcheck_move(gs, f, r, tf, tr)) {
294                     if ((piecetype(gs->board[f][r]) == PAWN) && (f != 1)) {
295                             continue;
296                     }
297
298                     /* if its a lowercase 'b' then prefer the pawn move if there is one */
299                     if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
300                         piecetype(gs->board[f][r]) == PAWN && mstr[0] == 'b') {
301                             ff = f;
302                             fr = r;
303                             continue;
304                     }
305
306                     if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
307                         piecetype(gs->board[ff][fr]) == PAWN && mstr[0] == 'b') {
308                             continue;
309                     }
310
311                     if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN)) {
312                             d_printf("Ambiguous %s(%d) mstr=%s\n", __FUNCTION__, __LINE__, mstr);
313                             return (MOVE_AMBIGUOUS);
314                     }
315                     ff = f;
316                     fr = r;
317             }
318     }
319   } else {                      /* The from position is unknown */
320     posf = ALG_UNKNOWN;
321     posr = ALG_UNKNOWN;
322     if ((ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) {
323       /* Need to find a piece that can go to tf, tr */
324       for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
325            NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
326         if ((ff != ALG_UNKNOWN) && (ff != f))
327           continue;
328         if ((fr != ALG_UNKNOWN) && (fr != r))
329           continue;
330         if (piecetype(gs->board[f][r]) != piece)
331           continue;
332         if (legal_andcheck_move(gs, f, r, tf, tr)) {
333                 if ((posf != ALG_UNKNOWN) && (posr != ALG_UNKNOWN)) {
334                         d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
335                         return MOVE_AMBIGUOUS;
336                 }
337           posf = f;
338           posr = r;
339         }
340       }
341     } else if (ff == ALG_DROP) {
342       if (legal_andcheck_move(gs, ALG_DROP, piece, tf, tr)) {
343         posf = ALG_DROP;
344         posr = piece;
345       }
346     }
347     ff = posf;
348     fr = posr;
349   }
350   if ((tf == ALG_UNKNOWN) || (tr == ALG_UNKNOWN) ||
351       (ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN))
352     return MOVE_ILLEGAL;
353   mt->fromFile = ff;
354   mt->fromRank = fr;
355   mt->toFile = tf;
356   mt->toRank = tr;
357
358   add_promotion(gs, mstr, mt);
359
360   return MOVE_OK;
361 }
362
363 /* A assumes the move has yet to be made on the board */
364
365 /* Soso: rewrote alg_unparse function.
366  * Algebraic deparser - sets the mStr variable with move description
367  * in short notation. Used in last move report and in 'moves' command.
368  */
369
370 char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
371 {
372   static char mStr[20];
373   char tmp[20];
374   int piece, f, r;
375   int ambig, r_ambig, f_ambig;
376   struct game_state_t fakeMove;
377
378   if (mt->fromFile == ALG_DROP) {
379     piece = mt->fromRank;
380   } else {
381     piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
382   }
383
384   if ((piece == KING) && ((mt->fromFile == 4) && (mt->toFile == 6))) {
385     strcpy(mStr, "O-O");
386     goto check;
387   }
388   if ((piece == KING) && ((mt->fromFile == 4) && (mt->toFile == 2))) {
389     strcpy(mStr, "O-O-O");
390     goto check;
391   }
392   strcpy(mStr, "");
393   switch (piece) {
394   case PAWN:
395     if (mt->fromFile == ALG_DROP) {
396       strcpy(mStr,"P");
397     } else if (mt->fromFile != mt->toFile) {
398       sprintf(tmp, "%c", mt->fromFile + 'a');
399       strcpy(mStr, tmp);
400     }
401     break;
402   case KNIGHT:
403     strcpy(mStr, "N");
404     break;
405   case BISHOP:
406     strcpy(mStr, "B");
407     break;
408   case ROOK:
409     strcpy(mStr, "R");
410     break;
411   case QUEEN:
412     strcpy(mStr, "Q");
413     break;
414   case KING:
415     strcpy(mStr, "K");
416     break;
417   default:
418     strcpy(mStr, "");
419     break;
420   }
421
422   if (mt->fromFile == ALG_DROP) {
423     strcat(mStr, "@");
424   } else {
425   /* Checks for ambiguity in short notation ( Ncb3, R8e8 or so) */
426   if (piece != PAWN) {
427     ambig = r_ambig = f_ambig = 0;
428     for (r = 0; r < 8; r++)
429       for (f = 0; f < 8; f++) {
430         if ((gs->board[f][r] != NOPIECE) && iscolor(gs->board[f][r], gs->onMove)
431             && (piecetype(gs->board[f][r]) == piece) &&
432             ((f != mt->fromFile) || (r != mt->fromRank))) {
433           if (legal_move(gs, f, r, mt->toFile, mt->toRank)) {
434             fakeMove = *gs;
435             fakeMove.board[f][r] = NOPIECE;
436             fakeMove.board[mt->toFile][mt->toRank] = piece | gs->onMove;
437             fakeMove.onMove = CToggle(fakeMove.onMove);
438             gs->onMove = CToggle(gs->onMove);
439
440 #if 0
441             d_printf("possible move %c%d%c%d against %c%d%c%d\n",
442                      'a' + f, r+1,
443                      'a' + mt->toFile, mt->toRank+1,
444                      'a' + mt->fromFile, mt->fromRank+1,
445                      'a' + mt->toFile, mt->toRank+1);
446 #endif
447
448             if (!in_check(&fakeMove)) {
449                     ambig++;
450                     if (f == mt->fromFile) {
451                             ambig++;
452                             f_ambig++;
453                     }
454                     if (r == mt->fromRank) {
455                             ambig++;
456                             r_ambig++;
457                     }
458             }
459             gs->onMove = CToggle(gs->onMove);
460           }
461         }
462       }
463     if (ambig > 0) {
464       /* Ambiguity in short notation, need to add file,rank or _both_ in
465          notation */
466       if (f_ambig == 0) {
467         sprintf(tmp, "%c", mt->fromFile + 'a');
468         strcat(mStr, tmp);
469       } else if (r_ambig == 0) {
470         sprintf(tmp, "%d", mt->fromRank + 1);
471         strcat(mStr, tmp);
472       } else {
473         sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1);
474         strcat(mStr, tmp);
475       }
476     }
477   }
478   if ((gs->board[mt->toFile][mt->toRank] != NOPIECE) ||
479       ((piece == PAWN) && (mt->fromFile != mt->toFile))) {
480     strcat(mStr, "x");
481   }
482   }
483   sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1);
484   strcat(mStr, tmp);
485
486   if ((piece == PAWN) && (mt->piecePromotionTo != NOPIECE)) {
487     strcat(mStr, "=");          /* = before promoting piece */
488     switch (piecetype(mt->piecePromotionTo)) {
489     case KNIGHT:
490       strcat(mStr, "N");
491       break;
492     case BISHOP:
493       strcat(mStr, "B");
494       break;
495     case ROOK:
496       strcat(mStr, "R");
497       break;
498     case QUEEN:
499       strcat(mStr, "Q");
500       break;
501     default:
502       break;
503     }
504   }
505 check:;
506   fakeMove = *gs;
507   execute_move(&fakeMove, mt, 0);
508   fakeMove.onMove = CToggle(fakeMove.onMove);
509   if (in_check(&fakeMove)) {
510     strcat(mStr, "+");
511   }
512   return mStr;
513 }