Subversion Repositories Games.Chess Giants

Rev

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
}