2 * Program: pgn-extract: a Portable Game Notation (PGN) extractor.
3 * Copyright (C) 1994-2014 David Barnes
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 1, or (at your option)
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * David Barnes may be contacted as D.J.Barnes@kent.ac.uk
19 * http://www.cs.kent.ac.uk/people/staff/djb/
23 /* This file contains functions concerned with decoding
24 * the original text of a move in order to determine
25 * which MoveClass it is in,
26 * any start and end square information.
27 * It extracts this information purely from the move text
28 * rather than analysing the move within the context of
30 * This information is later refined by the semantic analysis
31 * phase of the program as part of the checking of a game score.
45 /* Does the character represent a column of the board? */
49 return (FIRSTCOL <= c) && (c <= LASTCOL);
52 /* Does the character represent a rank of the board? */
56 return (FIRSTRANK <= c) && (c <= LASTRANK);
59 /* What kind of piece is *move likely to represent?
60 * Note, the provision for double-character pieces,
61 * like a Russian King, means we need access to a
62 * string rather than a single char.
65 is_piece(const unsigned char *move)
66 { Piece piece = EMPTY;
73 case 'D': /* Dutch/German. */
78 case 'T': /* Dutch/German. */
83 case 'P': /* Dutch. */
84 case 'S': /* German. */
88 case 'L': /* Dutch/German. */
90 /* Lower case 'b' is most likely to be a pawn reference. */
93 case RUSSIAN_KNIGHT_OR_KING:
94 if(RUSSIAN_PIECE_CHECK(*(move+1)) == RUSSIAN_KING_SECOND_LETTER){
105 /* Is the symbol a capturing one?
106 * In fact, this is used to recognise any general separator
107 * between two parts of a move, e.g.:
114 return (c == 'x') || (c == 'X') || (c == ':') || (c == '-');
118 is_castling_character(char c)
120 return (c == 'O') || (c == '0') || (c == 'o');
126 return (c == '+') || (c == '#');
129 /* Allocate space in which to return the information that
130 * has been gleaned from the move.
133 new_move_structure(void)
134 { Move *move = (Move *)MallocOrDie(sizeof(Move));
136 move->terminating_result = NULL;
139 move->Comment = NULL;
140 move->Variants = NULL;
145 /* Work out whatever can be gleaned from move_string of
146 * the starting and ending points of the given move.
147 * The move may be any legal string.
148 * The scanning here is libertarian, so it relies heavily on
149 * illegal moves having already been filtered out by the process
150 * of lexical analysis.
153 decode_move(const unsigned char *move_string)
154 { /* The four components of the co-ordinates when known. */
155 Rank from_rank = 0, to_rank = 0;
156 Col from_col = 0, to_col = 0;
159 /* Temporary locations until known whether they are from_ or to_. */
162 /* A pointer to move along the move string. */
163 const unsigned char *move = move_string;
164 /* A pointer to the structure containing the details to be returned. */
166 Piece piece_to_move = EMPTY;
168 /* Make an initial distinction between pawn moves and piece moves. */
172 piece_to_move = PAWN;
179 if(is_capture(*move)){
198 if(is_capture(*move)){
203 /* ab, or bg8 for liberal bishop moves. */
210 /* Check the sanity of this. */
211 if((from_col != 'b') &&
212 (from_col != (to_col+1)) && (from_col != (to_col-1))){
217 /* Check the sanity of this. */
218 if((from_col != (to_col+1)) && (from_col != (to_col-1))){
224 print_error_context(GlobalState.logfile);
225 fprintf(GlobalState.logfile,"Unknown pawn move %s.\n",move_string);
230 /* Look for promotions. */
234 if(is_piece(move) != EMPTY){
235 class = PAWN_MOVE_WITH_PROMOTION;
236 /* @@@ Strictly speaking, if the piece is a RUSSIAN_KING
237 * then we should skip two chars.
243 else if((piece_to_move = is_piece(move)) != EMPTY){
245 /* Check for a two-character piece. */
246 if((RUSSIAN_PIECE_CHECK(*move) == RUSSIAN_KNIGHT_OR_KING) &&
247 (piece_to_move == KING)){
252 /* A disambiguating rank.
257 if(is_capture(*move)){
270 print_error_context(GlobalState.logfile);
271 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
275 if(is_capture(*move)){
287 print_error_context(GlobalState.logfile);
288 fprintf(GlobalState.logfile,
289 "Unknown piece move %s.\n",move_string);
294 print_error_context(GlobalState.logfile);
295 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
298 else if(is_col(*move)){
301 if(is_capture(*move)){
305 /* Re1, Re1d1, Re1xd1 */
308 if(is_capture(*move)){
323 print_error_context(GlobalState.logfile);
324 fprintf(GlobalState.logfile,
325 "Unknown piece move %s.\n",move_string);
333 else if(is_col(*move)){
345 print_error_context(GlobalState.logfile);
346 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
351 print_error_context(GlobalState.logfile);
352 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
356 else if(is_castling_character(*move)){
357 /* Some form of castling. */
359 /* Allow separators to be optional. */
363 if(is_castling_character(*move)){
368 if(is_castling_character(*move)){
369 class = QUEENSIDE_CASTLE;
373 class = KINGSIDE_CASTLE;
377 print_error_context(GlobalState.logfile);
378 fprintf(GlobalState.logfile,"Unknown castling move %s.\n",move_string);
382 else if(strcmp((char *) move_string, NULL_MOVE_STRING) == 0) {
386 print_error_context(GlobalState.logfile);
387 fprintf(GlobalState.logfile,"Unknown move %s.\n",move_string);
390 if(Ok && class != NULL_MOVE){
391 /* Allow trailing checks. */
392 while(is_check(*move)){
396 /* Nothing more to check. */
398 else if(((strcmp((const char *) move,"ep") == 0) ||
399 (strcmp((const char *) move,"e.p.") == 0)) &&
400 (class == PAWN_MOVE)){
402 class = ENPASSANT_PAWN_MOVE;
406 print_error_context(GlobalState.logfile);
407 fprintf(GlobalState.logfile,
408 "Unknown text trailing move %s <%s>.\n",move_string,move);
411 /* Store all of the details gathered, even if the move is illegal. */
413 class = UNKNOWN_MOVE;
415 move_details = new_move_structure();
416 strcpy((char *) move_details->move,(const char *) move_string);
417 move_details->class = class;
418 move_details->piece_to_move = piece_to_move;
419 move_details->from_col = from_col;
420 move_details->from_rank = from_rank;
421 move_details->to_col = to_col;
422 move_details->to_rank = to_rank;
423 move_details->captured_piece = EMPTY;
424 move_details->check_status = NOCHECK;
429 decode_algebraic(Move *move_details, Board *board)
430 { short from_r = RankConvert(move_details->from_rank);
431 short from_c = ColConvert(move_details->from_col);
432 Piece piece_to_move = EXTRACT_PIECE(board->board[from_r][from_c]);
434 if(piece_to_move != EMPTY){
435 /* Check for the special case of castling. */
436 if((piece_to_move == KING) && (move_details->from_col == 'e')){
437 if(move_details->to_col == 'g'){
438 move_details->class = KINGSIDE_CASTLE;
440 else if(move_details->to_col == 'c'){
441 move_details->class = QUEENSIDE_CASTLE;
444 move_details->class = PIECE_MOVE;
445 move_details->piece_to_move = piece_to_move;
449 if(piece_to_move == PAWN){
450 move_details->class = PAWN_MOVE;
453 move_details->class = PIECE_MOVE;
455 move_details->piece_to_move = piece_to_move;
457 move_details->captured_piece = EMPTY;
458 move_details->check_status = NOCHECK;
463 /* See if move_string seems to represent the text of a valid move.
464 * Don't print any error messages, just return TRUE or FALSE.
467 move_seems_valid(const unsigned char *move_string)
470 /* A pointer to move along the move string. */
471 unsigned const char *move = move_string;
473 /* Make an initial distinction between pawn moves and piece moves. */
481 if(is_capture(*move)){
494 if(is_capture(*move)){
510 /* Look for promotions. */
514 if(is_piece(move) != EMPTY){
515 class = PAWN_MOVE_WITH_PROMOTION;
516 /* @@@ Strictly speaking, if the piece is a RUSSIAN_KING
517 * then we should skip two chars.
523 else if(is_piece(move) != EMPTY){
525 /* Check for a two-character piece. */
526 if((RUSSIAN_PIECE_CHECK(*move) == RUSSIAN_KNIGHT_OR_KING) &&
527 (is_piece(move) == KING)){
532 /* A disambiguating rank.
536 if(is_capture(*move)){
550 if(is_capture(*move)){
566 else if(is_col(*move)){
568 if(is_capture(*move)){
572 /* Re1, Re1d1, Re1xd1 */
574 if(is_capture(*move)){
588 else if(is_col(*move)){
607 else if(is_castling_character(*move)){
608 /* Some form of castling. */
610 /* Allow separators to be optional. */
614 if(is_castling_character(*move)){
619 if(is_castling_character(*move)){
620 class = QUEENSIDE_CASTLE;
624 class = KINGSIDE_CASTLE;
635 /* Allow trailing checks. */
636 while(is_check(*move)){
640 /* Nothing more to check. */
642 else if(((strcmp((const char *) move,"ep") == 0) ||
643 (strcmp((const char *) move,"e.p.") == 0)) &&
644 (class == PAWN_MOVE)){
646 class = ENPASSANT_PAWN_MOVE;