Go to most recent revision | Details | 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 | */ |
||
| 18 | int InputMove(TREE * RESTRICT tree, char *text, int ply, int wtm, int silent, |
||
| 19 | int ponder_list) { |
||
| 20 | int 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, text, ply, wtm, silent, ponder_list); |
||
| 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_s(movetext, sizeof (movetext), text); // Pierre-Marie Baty -- use safe version |
||
| 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, *mv, wtm); |
||
| 302 | if (Check(wtm) || (give_check && !Check(Flip(wtm)))) { |
||
| 303 | UnmakeMove(tree, MAXPLY, *mv, wtm); |
||
| 304 | *mv = 0; |
||
| 305 | } else |
||
| 306 | UnmakeMove(tree, MAXPLY, *mv, wtm); |
||
| 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 | */ |
||
| 349 | int InputMoveICS(TREE * RESTRICT tree, char *text, int ply, int wtm, |
||
| 350 | int silent, int ponder_list) { |
||
| 351 | int 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_s(movetext, sizeof (movetext), text); // Pierre-Marie Baty -- use safe version |
||
| 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, *mv, wtm); |
||
| 475 | if (Check(wtm)) { |
||
| 476 | UnmakeMove(tree, MAXPLY, *mv, wtm); |
||
| 477 | *mv = 0; |
||
| 478 | } else |
||
| 479 | UnmakeMove(tree, MAXPLY, *mv, wtm); |
||
| 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 | } |