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;
140 move->Comment = NULL;
141 move->Variants = NULL;
146 /* Work out whatever can be gleaned from move_string of
147 * the starting and ending points of the given move.
148 * The move may be any legal string.
149 * The scanning here is libertarian, so it relies heavily on
150 * illegal moves having already been filtered out by the process
151 * of lexical analysis.
154 decode_move(const unsigned char *move_string)
155 { /* The four components of the co-ordinates when known. */
156 Rank from_rank = 0, to_rank = 0;
157 Col from_col = 0, to_col = 0;
160 /* Temporary locations until known whether they are from_ or to_. */
163 /* A pointer to move along the move string. */
164 const unsigned char *move = move_string;
165 /* A pointer to the structure containing the details to be returned. */
167 Piece piece_to_move = EMPTY;
169 /* Make an initial distinction between pawn moves and piece moves. */
173 piece_to_move = PAWN;
180 if(is_capture(*move)){
199 if(is_capture(*move)){
204 /* ab, or bg8 for liberal bishop moves. */
211 /* Check the sanity of this. */
212 if((from_col != 'b') &&
213 (from_col != (to_col+1)) && (from_col != (to_col-1))){
218 /* Check the sanity of this. */
219 if((from_col != (to_col+1)) && (from_col != (to_col-1))){
225 print_error_context(GlobalState.logfile);
226 fprintf(GlobalState.logfile,"Unknown pawn move %s.\n",move_string);
231 /* Look for promotions. */
235 if(is_piece(move) != EMPTY){
236 class = PAWN_MOVE_WITH_PROMOTION;
237 /* @@@ Strictly speaking, if the piece is a RUSSIAN_KING
238 * then we should skip two chars.
244 else if((piece_to_move = is_piece(move)) != EMPTY){
246 /* Check for a two-character piece. */
247 if((RUSSIAN_PIECE_CHECK(*move) == RUSSIAN_KNIGHT_OR_KING) &&
248 (piece_to_move == KING)){
253 /* A disambiguating rank.
258 if(is_capture(*move)){
271 print_error_context(GlobalState.logfile);
272 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
276 if(is_capture(*move)){
288 print_error_context(GlobalState.logfile);
289 fprintf(GlobalState.logfile,
290 "Unknown piece move %s.\n",move_string);
295 print_error_context(GlobalState.logfile);
296 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
299 else if(is_col(*move)){
302 if(is_capture(*move)){
306 /* Re1, Re1d1, Re1xd1 */
309 if(is_capture(*move)){
324 print_error_context(GlobalState.logfile);
325 fprintf(GlobalState.logfile,
326 "Unknown piece move %s.\n",move_string);
334 else if(is_col(*move)){
346 print_error_context(GlobalState.logfile);
347 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
352 print_error_context(GlobalState.logfile);
353 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
357 else if(is_castling_character(*move)){
358 /* Some form of castling. */
360 /* Allow separators to be optional. */
364 if(is_castling_character(*move)){
369 if(is_castling_character(*move)){
370 class = QUEENSIDE_CASTLE;
374 class = KINGSIDE_CASTLE;
378 print_error_context(GlobalState.logfile);
379 fprintf(GlobalState.logfile,"Unknown castling move %s.\n",move_string);
383 else if(strcmp((char *) move_string, NULL_MOVE_STRING) == 0) {
387 print_error_context(GlobalState.logfile);
388 fprintf(GlobalState.logfile,"Unknown move %s.\n",move_string);
391 if(Ok && class != NULL_MOVE){
392 /* Allow trailing checks. */
393 while(is_check(*move)){
397 /* Nothing more to check. */
399 else if(((strcmp((const char *) move,"ep") == 0) ||
400 (strcmp((const char *) move,"e.p.") == 0)) &&
401 (class == PAWN_MOVE)){
403 class = ENPASSANT_PAWN_MOVE;
407 print_error_context(GlobalState.logfile);
408 fprintf(GlobalState.logfile,
409 "Unknown text trailing move %s <%s>.\n",move_string,move);
412 /* Store all of the details gathered, even if the move is illegal. */
414 class = UNKNOWN_MOVE;
416 move_details = new_move_structure();
417 strcpy((char *) move_details->move,(const char *) move_string);
418 move_details->class = class;
419 move_details->piece_to_move = piece_to_move;
420 move_details->from_col = from_col;
421 move_details->from_rank = from_rank;
422 move_details->to_col = to_col;
423 move_details->to_rank = to_rank;
424 move_details->captured_piece = EMPTY;
425 move_details->check_status = NOCHECK;
430 decode_algebraic(Move *move_details, Board *board)
431 { short from_r = RankConvert(move_details->from_rank);
432 short from_c = ColConvert(move_details->from_col);
433 Piece piece_to_move = EXTRACT_PIECE(board->board[from_r][from_c]);
435 if(piece_to_move != EMPTY){
436 /* Check for the special case of castling. */
437 if((piece_to_move == KING) && (move_details->from_col == 'e')){
438 if(move_details->to_col == 'g'){
439 move_details->class = KINGSIDE_CASTLE;
441 else if(move_details->to_col == 'c'){
442 move_details->class = QUEENSIDE_CASTLE;
445 move_details->class = PIECE_MOVE;
446 move_details->piece_to_move = piece_to_move;
450 if(piece_to_move == PAWN){
451 move_details->class = PAWN_MOVE;
454 move_details->class = PIECE_MOVE;
456 move_details->piece_to_move = piece_to_move;
458 move_details->captured_piece = EMPTY;
459 move_details->check_status = NOCHECK;
464 /* See if move_string seems to represent the text of a valid move.
465 * Don't print any error messages, just return TRUE or FALSE.
468 move_seems_valid(const unsigned char *move_string)
471 /* A pointer to move along the move string. */
472 unsigned const char *move = move_string;
474 /* Make an initial distinction between pawn moves and piece moves. */
482 if(is_capture(*move)){
495 if(is_capture(*move)){
511 /* Look for promotions. */
515 if(is_piece(move) != EMPTY){
516 class = PAWN_MOVE_WITH_PROMOTION;
517 /* @@@ Strictly speaking, if the piece is a RUSSIAN_KING
518 * then we should skip two chars.
524 else if(is_piece(move) != EMPTY){
526 /* Check for a two-character piece. */
527 if((RUSSIAN_PIECE_CHECK(*move) == RUSSIAN_KNIGHT_OR_KING) &&
528 (is_piece(move) == KING)){
533 /* A disambiguating rank.
537 if(is_capture(*move)){
551 if(is_capture(*move)){
567 else if(is_col(*move)){
569 if(is_capture(*move)){
573 /* Re1, Re1d1, Re1xd1 */
575 if(is_capture(*move)){
589 else if(is_col(*move)){
608 else if(is_castling_character(*move)){
609 /* Some form of castling. */
611 /* Allow separators to be optional. */
615 if(is_castling_character(*move)){
620 if(is_castling_character(*move)){
621 class = QUEENSIDE_CASTLE;
625 class = KINGSIDE_CASTLE;
636 /* Allow trailing checks. */
637 while(is_check(*move)){
641 /* Nothing more to check. */
643 else if(((strcmp((const char *) move,"ep") == 0) ||
644 (strcmp((const char *) move,"e.p.") == 0)) &&
645 (class == PAWN_MOVE)){
647 class = ENPASSANT_PAWN_MOVE;