Rev 33 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
33 | pmbaty | 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 | */ |
||
108 | pmbaty | 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; |
||
33 | pmbaty | 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')) |
||
108 | pmbaty | 60 | return InputMoveICS(tree, ply, wtm, silent, ponder_list, text); |
33 | pmbaty | 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]; |
||
108 | pmbaty | 71 | strcpy(movetext, text); |
33 | pmbaty | 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) { |
||
108 | pmbaty | 301 | MakeMove(tree, MAXPLY, wtm, *mv); |
33 | pmbaty | 302 | if (Check(wtm) || (give_check && !Check(Flip(wtm)))) { |
108 | pmbaty | 303 | UnmakeMove(tree, MAXPLY, wtm, *mv); |
33 | pmbaty | 304 | *mv = 0; |
305 | } else |
||
108 | pmbaty | 306 | UnmakeMove(tree, MAXPLY, wtm, *mv); |
33 | pmbaty | 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 | */ |
||
108 | pmbaty | 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; |
||
33 | pmbaty | 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]; |
||
108 | pmbaty | 372 | strcpy(movetext, text); |
33 | pmbaty | 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) { |
||
108 | pmbaty | 474 | MakeMove(tree, MAXPLY, wtm, *mv); |
33 | pmbaty | 475 | if (Check(wtm)) { |
108 | pmbaty | 476 | UnmakeMove(tree, MAXPLY, wtm, *mv); |
33 | pmbaty | 477 | *mv = 0; |
478 | } else |
||
108 | pmbaty | 479 | UnmakeMove(tree, MAXPLY, wtm, *mv); |
33 | pmbaty | 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 | } |