1 #include "chess.h"
2 #include "data.h"
3 /* last modified 02/24/14 */
4 /*
5  *******************************************************************************
6  *                                                                             *
7  *   InputMove() is responsible for converting a move from a text string to    *
8  *   the internal move format.  It allows the so-called "reduced algebraic     *
9  *   move format" which makes the origin square optional unless required for   *
10  *   clarity.  It also accepts as little as required to remove ambiguity from  *
11  *   the move, by using GenerateMoves() to produce a set of legal moves that   *
12  *   the text can be applied against to eliminate those moves not intended.    *
13  *   Hopefully, only one move will remain after the elimination and legality   *
14  *   checks.                                                                   *
15  *                                                                             *
16  *******************************************************************************
17  */
InputMove(TREE * RESTRICT tree,int ply,int wtm,int silent,int ponder_list,char * text)18 int InputMove(TREE * RESTRICT tree, int ply, int wtm, int silent,
19     int ponder_list, char *text) {
20   unsigned moves[220], *mv, *mvp, *goodmove = 0;
21   int piece = -1, capture, promote, give_check;
22   int ffile, frank, tfile, trank;
23   int current, i, nleft;
24   char *goodchar, *tc;
25   char movetext[128];
26   static const char pieces[15] =
27       { ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r',
28     'Q', 'q', 'K', 'k', '\0'
29   };
30   static const char pro_pieces[15] =
31       { ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r', 'Q', 'q',
32     'K', 'k', '\0'
33   };
34 /*
35  ************************************************************
36  *                                                          *
37  *  First, we need to strip off the special characters for  *
38  *  check, mate, bad move, good move, and such that might   *
39  *  come from a PGN input file.                             *
40  *                                                          *
41  ************************************************************
42  */
43   if ((tc = strchr(text, '!')))
44     *tc = 0;
45   if ((tc = strchr(text, '?')))
46     *tc = 0;
47 /*
48  ************************************************************
49  *                                                          *
50  *  Check for full coordinate input (f1e1) and handle that  *
51  *  if needed.                                              *
52  *                                                          *
53  ************************************************************
54  */
55   if (strlen(text) == 0)
56     return 0;
57   if ((text[0] >= 'a') && (text[0] <= 'h') && (text[1] >= '1')
58       && (text[1] <= '8') && (text[2] >= 'a') && (text[2] <= 'h')
59       && (text[3] >= '1') && (text[3] <= '8'))
60     return InputMoveICS(tree, ply, wtm, silent, ponder_list, text);
61 /*
62  ************************************************************
63  *                                                          *
64  *  Initialize move structure.  If we discover a parsing    *
65  *  error, this will cause us to return a move of "0" to    *
66  *  indicate some sort of error was detected.               *
67  *                                                          *
68  ************************************************************
69  */
70   tree->status[MAXPLY] = tree->status[ply];
71   strcpy(movetext, text);
72   moves[0] = 0;
73   piece = 0;
74   capture = 0;
75   promote = 0;
76   give_check = 0;
77   frank = -1;
78   ffile = -1;
79   trank = -1;
80   tfile = -1;
81   goodchar = strchr(movetext, '#');
82   if (goodchar)
83     *goodchar = 0;
84 /*
85  ************************************************************
86  *                                                          *
87  *  First we look for castling moves which are a special    *
88  *  case with an unusual syntax compared to normal moves.   *
89  *                                                          *
90  ************************************************************
91  */
92   if (!strcmp(movetext, "o-o") || !strcmp(movetext, "o-o+")
93       || !strcmp(movetext, "O-O") || !strcmp(movetext, "O-O+")
94       || !strcmp(movetext, "0-0") || !strcmp(movetext, "0-0+")) {
95     piece = king;
96     if (wtm) {
97       ffile = 4;
98       frank = 0;
99       tfile = 6;
100       trank = 0;
101     } else {
102       ffile = 4;
103       frank = 7;
104       tfile = 6;
105       trank = 7;
106     }
107   } else if (!strcmp(movetext, "o-o-o") || !strcmp(movetext, "o-o-o+")
108       || !strcmp(movetext, "O-O-O") || !strcmp(movetext, "O-O-O+")
109       || !strcmp(movetext, "0-0-0") || !strcmp(movetext, "0-0-0+")) {
110     piece = king;
111     if (wtm) {
112       ffile = 4;
113       frank = 0;
114       tfile = 2;
115       trank = 0;
116     } else {
117       ffile = 4;
118       frank = 7;
119       tfile = 2;
120       trank = 7;
121     }
122   } else {
123 /*
124  ************************************************************
125  *                                                          *
126  *  OK, it is not a castling move.  Check for two "b"       *
127  *  characters which might be a piece (bishop) and a file   *
128  *  (b-file).  The first "b" should be "B" but we allow     *
129  *  this to make typing input simpler.                      *
130  *                                                          *
131  ************************************************************
132  */
133     if ((movetext[0] == 'b') && (movetext[1] == 'b'))
134       movetext[0] = 'B';
135 /*
136  ************************************************************
137  *                                                          *
138  *  Check to see if there is a "+" character which means    *
139  *  that this move is a check.  We can use this to later    *
140  *  eliminate all non-checking moves as possibilities.      *
141  *                                                          *
142  ************************************************************
143  */
144     if (strchr(movetext, '+')) {
145       *strchr(movetext, '+') = 0;
146       give_check = 1;
147     }
148 /*
149  ************************************************************
150  *                                                          *
151  *  If this is a promotion, indicated by an "=" in the      *
152  *  text, we can pick up the promote-to piece and save it   *
153  *  to use later when eliminating moves.                    *
154  *                                                          *
155  ************************************************************
156  */
157     if (strchr(movetext, '=')) {
158       goodchar = strchr(movetext, '=');
159       goodchar++;
160       promote = (strchr(pro_pieces, *goodchar) - pro_pieces) >> 1;
161       *strchr(movetext, '=') = 0;
162     }
163 /*
164  ************************************************************
165  *                                                          *
166  *  Now for a kludge.  ChessBase (and others) can't follow  *
167  *  the PGN standard of bxc8=Q for promotion, and instead   *
168  *  will produce "bxc8Q" omitting the PGN-standard "="      *
169  *  character.  We handle that here so that we can read     *
170  *  their non-standard moves.                               *
171  *                                                          *
172  ************************************************************
173  */
174     else {
175       char *prom = strchr(pro_pieces, movetext[strlen(movetext) - 1]);
176 
177       if (prom) {
178         promote = (prom - pro_pieces) >> 1;
179         movetext[strlen(movetext) - 1] = 0;
180       }
181     }
182 /*
183  ************************************************************
184  *                                                          *
185  *  Next we extract the last rank/file designators from the *
186  *  text, since the destination is required for all valid   *
187  *  non-castling moves.  Note that we might not have both a *
188  *  rank and file but we must have at least one.            *
189  *                                                          *
190  ************************************************************
191  */
192     current = strlen(movetext) - 1;
193     trank = movetext[current] - '1';
194     if ((trank >= 0) && (trank <= 7))
195       movetext[current] = 0;
196     else
197       trank = -1;
198     current = strlen(movetext) - 1;
199     tfile = movetext[current] - 'a';
200     if ((tfile >= 0) && (tfile <= 7))
201       movetext[current] = 0;
202     else
203       tfile = -1;
204     if (strlen(movetext)) {
205 /*
206  ************************************************************
207  *                                                          *
208  *  The first character is the moving piece, unless it is a *
209  *  pawn.  In this case, the moving piece is omitted and we *
210  *  know what it has to be.                                 *
211  *                                                          *
212  ************************************************************
213  */
214       if (strchr("  PpNnBBRrQqKk", *movetext)) {
215         piece = (strchr(pieces, movetext[0]) - pieces) >> 1;
216         for (i = 0; i < (int) strlen(movetext); i++)
217           movetext[i] = movetext[i + 1];
218       }
219 /*
220  ************************************************************
221  *                                                          *
222  *  It is also possible that this move is a capture, which  *
223  *  is indicated by a "x" between either the source and     *
224  *  destination squares, or between the moving piece and    *
225  *  the destination.                                        *
226  *                                                          *
227  ************************************************************
228  */
229       if ((strlen(movetext)) && (movetext[strlen(movetext) - 1] == 'x')) {
230         capture = 1;
231         movetext[strlen(movetext) - 1] = 0;
232       } else
233         capture = 0;
234 /*
235  ************************************************************
236  *                                                          *
237  *  It is possible to have no source square, but we could   *
238  *  have a complete algebraic square designation, or just   *
239  *  rank or file, needed to disambiguate the move.          *
240  *                                                          *
241  ************************************************************
242  */
243       if (strlen(movetext)) {
244         ffile = movetext[0] - 'a';
245         if ((ffile < 0) || (ffile > 7)) {
246           ffile = -1;
247           frank = movetext[0] - '1';
248           if ((frank < 0) || (frank > 7))
249             piece = -1;
250         } else {
251           if (strlen(movetext) == 2) {
252             frank = movetext[1] - '1';
253             if ((frank < 0) || (frank > 7))
254               piece = -1;
255           }
256         }
257       }
258     }
259   }
260 /*
261  ************************************************************
262  *                                                          *
263  *  Now for the easy part.  We first generate all moves if  *
264  *  not pondering, or else use a pre-computed list of moves *
265  *  (if pondering) since the board position is not correct  *
266  *  for move input analysis.  We then loop through the list *
267  *  of moves, using the information we extracted previously *
268  *  , and eliminate all moves that are (a) the wrong piece  *
269  *  type;  (b) wrong source or destination square;          *
270  *  (c) wrong promotion type;  (d) should be a capture,     *
271  *  check or promotion but is not, or vice-versa.           *
272  *                                                          *
273  ************************************************************
274  */
275   if (!piece)
276     piece = 1;
277   if (!ponder_list) {
278     mvp = GenerateCaptures(tree, MAXPLY, wtm, moves);
279     mvp = GenerateNoncaptures(tree, MAXPLY, wtm, mvp);
280   } else {
281     for (i = 0; i < num_ponder_moves; i++)
282       moves[i] = ponder_moves[i];
283     mvp = moves + num_ponder_moves;
284   }
285   for (mv = &moves[0]; mv < mvp; mv++) {
286     if (piece && (Piece(*mv) != piece))
287       *mv = 0;
288     if ((ffile >= 0) && (File(From(*mv)) != ffile))
289       *mv = 0;
290     if (capture && (!Captured(*mv)))
291       *mv = 0;
292     if (promote && (Promote(*mv) != promote))
293       *mv = 0;
294     if ((frank >= 0) && (Rank(From(*mv)) != frank))
295       *mv = 0;
296     if ((tfile >= 0) && (File(To(*mv)) != tfile))
297       *mv = 0;
298     if ((trank >= 0) && (Rank(To(*mv)) != trank))
299       *mv = 0;
300     if (!ponder_list && *mv) {
301       MakeMove(tree, MAXPLY, wtm, *mv);
302       if (Check(wtm) || (give_check && !Check(Flip(wtm)))) {
303         UnmakeMove(tree, MAXPLY, wtm, *mv);
304         *mv = 0;
305       } else
306         UnmakeMove(tree, MAXPLY, wtm, *mv);
307     }
308   }
309 /*
310  ************************************************************
311  *                                                          *
312  *  Once we have completed eliminating incorrect moves, we  *
313  *  hope to have exactly one move left.  If none or left,   *
314  *  the entered move is illegal.  If more than one is left, *
315  *  the move entered is ambiguous.  If appropriate, we      *
316  *  output some sort of diagnostic message and then return. *
317  *                                                          *
318  ************************************************************
319  */
320   nleft = 0;
321   for (mv = &moves[0]; mv < mvp; mv++) {
322     if (*mv) {
323       nleft++;
324       goodmove = mv;
325     }
326   }
327   if (nleft == 1)
328     return *goodmove;
329   if (!silent) {
330     if (nleft == 0)
331       Print(4095, "Illegal move: %s\n", text);
332     else if (piece < 0)
333       Print(4095, "Illegal move (unrecognizable): %s\n", text);
334     else
335       Print(4095, "Illegal move (ambiguous): %s\n", text);
336   }
337   return 0;
338 }
339 
340 /* last modified 02/24/14 */
341 /*
342  *******************************************************************************
343  *                                                                             *
344  *   InputMoveICS() is responsible for converting a move from the ics format   *
345  *   [from][to][promote] to the program's internal format.                     *
346  *                                                                             *
347  *******************************************************************************
348  */
InputMoveICS(TREE * RESTRICT tree,int ply,int wtm,int silent,int ponder_list,char * text)349 int InputMoveICS(TREE * RESTRICT tree, int ply, int wtm, int silent,
350     int ponder_list, char *text) {
351   unsigned moves[220], *mv, *mvp, *goodmove = 0;
352   int piece = -1, promote;
353   int ffile, frank, tfile, trank;
354   int i, nleft;
355   char movetext[128];
356   static const char pieces[15] =
357       { ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r',
358     'Q', 'q', 'K', 'k', '\0'
359   };
360 /*
361  ************************************************************
362  *                                                          *
363  *  Initialize move structure.  If we discover a parsing    *
364  *  error, this will cause us to return a move of "0" to    *
365  *  indicate some sort of error was detected.               *
366  *                                                          *
367  ************************************************************
368  */
369   if (strlen(text) == 0)
370     return 0;
371   tree->status[MAXPLY] = tree->status[ply];
372   strcpy(movetext, text);
373   moves[0] = 0;
374   promote = 0;
375 /*
376  ************************************************************
377  *                                                          *
378  *  First we look for castling moves which are a special    *
379  *  case with an unusual syntax compared to normal moves.   *
380  *                                                          *
381  ************************************************************
382  */
383   if (!strcmp(movetext, "o-o") || !strcmp(movetext, "O-O")
384       || !strcmp(movetext, "0-0")) {
385     piece = king;
386     if (wtm) {
387       ffile = 4;
388       frank = 0;
389       tfile = 6;
390       trank = 0;
391     } else {
392       ffile = 4;
393       frank = 7;
394       tfile = 6;
395       trank = 7;
396     }
397   } else if (!strcmp(movetext, "o-o-o") || !strcmp(movetext, "O-O-O")
398       || !strcmp(movetext, "0-0-0")) {
399     piece = king;
400     if (wtm) {
401       ffile = 4;
402       frank = 0;
403       tfile = 2;
404       trank = 0;
405     } else {
406       ffile = 4;
407       frank = 7;
408       tfile = 2;
409       trank = 7;
410     }
411   } else {
412 /*
413  ************************************************************
414  *                                                          *
415  *  Next we extract both rank/file designators from the     *
416  *  text, since the destination is required for all valid   *
417  *  non-castling moves.                                     *
418  *                                                          *
419  ************************************************************
420  */
421     ffile = movetext[0] - 'a';
422     frank = movetext[1] - '1';
423     tfile = movetext[2] - 'a';
424     trank = movetext[3] - '1';
425 /*
426  ************************************************************
427  *                                                          *
428  *  If this is a promotion, indicated by an "=" in the      *
429  *  text, we can pick up the promote-to piece and save it   *
430  *  to use later when eliminating moves.                    *
431  *                                                          *
432  ************************************************************
433  */
434     if (movetext[4] == '=')
435       promote = (strchr(pieces, movetext[5]) - pieces) >> 1;
436     else if ((movetext[4] != 0) && (movetext[4] != ' '))
437       promote = (strchr(pieces, movetext[4]) - pieces) >> 1;
438   }
439 /*
440  ************************************************************
441  *                                                          *
442  *  Now for the easy part.  We first generate all moves if  *
443  *  not pondering, or else use a pre-computed list of moves *
444  *  (if pondering) since the board position is not correct  *
445  *  for move input analysis.  We then loop through the list *
446  *  of moves, using the information we extracted previously *
447  *  and eliminate all moves that are (a) the wrong piece    *
448  *  type;  (b) wrong source or destination square;          *
449  *  (c) wrong promotion type;  (d) should be a capture,     *
450  *  check or promotion but is not or vice-versa.            *
451  *                                                          *
452  ************************************************************
453  */
454   if (!ponder_list) {
455     mvp = GenerateCaptures(tree, MAXPLY, wtm, moves);
456     mvp = GenerateNoncaptures(tree, MAXPLY, wtm, mvp);
457   } else {
458     for (i = 0; i < num_ponder_moves; i++)
459       moves[i] = ponder_moves[i];
460     mvp = moves + num_ponder_moves;
461   }
462   for (mv = &moves[0]; mv < mvp; mv++) {
463     if (Promote(*mv) != promote)
464       *mv = 0;
465     if (Rank(From(*mv)) != frank)
466       *mv = 0;
467     if (File(From(*mv)) != ffile)
468       *mv = 0;
469     if (Rank(To(*mv)) != trank)
470       *mv = 0;
471     if (File(To(*mv)) != tfile)
472       *mv = 0;
473     if (!ponder_list && *mv) {
474       MakeMove(tree, MAXPLY, wtm, *mv);
475       if (Check(wtm)) {
476         UnmakeMove(tree, MAXPLY, wtm, *mv);
477         *mv = 0;
478       } else
479         UnmakeMove(tree, MAXPLY, wtm, *mv);
480     }
481   }
482 /*
483  ************************************************************
484  *                                                          *
485  *  Once we have completed eliminating incorrect moves, we  *
486  *  hope to have exactly one move left.  If none or left,   *
487  *  the entered move is illegal.  If more than one is left, *
488  *  the move entered is ambiguous.  If appropriate, we      *
489  *  output some sort of diagnostic message and then return. *
490  *                                                          *
491  ************************************************************
492  */
493   nleft = 0;
494   for (mv = &moves[0]; mv < mvp; mv++) {
495     if (*mv) {
496       nleft++;
497       goodmove = mv;
498     }
499   }
500   if (nleft == 1)
501     return *goodmove;
502   if (!silent) {
503     if (nleft == 0)
504       Print(4095, "Illegal move: %s\n", text);
505     else if (piece < 0)
506       Print(4095, "Illegal move (unrecognizable): %s\n", text);
507     else
508       Print(4095, "Illegal move (ambiguous): %s\n", text);
509   }
510   return 0;
511 }
512