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