]> git.sesse.net Git - pgn-extract/blob - decode.c
Add code to dump out the ECO names by hash.
[pgn-extract] / decode.c
1 /*
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)
7  *  any later version.
8  *
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.
13  *
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.
17  *
18  *  David Barnes may be contacted as D.J.Barnes@kent.ac.uk
19  *  http://www.cs.kent.ac.uk/people/staff/djb/
20  *
21  */
22
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
29              * a board position.
30              * This information is later refined by the semantic analysis
31              * phase of the program as part of the checking of a game score.
32              */
33
34 #include <stdio.h>
35 #include <string.h>
36 #include "bool.h"
37 #include "mymalloc.h"
38 #include "defs.h"
39 #include "typedef.h"
40 #include "decode.h"
41 #include "tokens.h"
42 #include "taglist.h"
43 #include "lex.h"
44
45         /* Does the character represent a column of the board? */
46 Boolean
47 is_col(char c)
48 {
49     return (FIRSTCOL <= c) && (c <= LASTCOL);
50 }
51
52         /* Does the character represent a rank of the board? */
53 Boolean
54 is_rank(char c)
55 {
56     return (FIRSTRANK <= c) && (c <= LASTRANK);
57 }
58
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.
63          */
64 Piece
65 is_piece(const unsigned char *move)
66 {   Piece piece = EMPTY;
67
68     switch(*move){
69         case 'K': case 'k':
70             piece = KING;
71             break;
72         case 'Q': case 'q':
73         case 'D': /* Dutch/German. */
74         case RUSSIAN_QUEEN:
75             piece = QUEEN;
76             break;
77         case 'R': case 'r':
78         case 'T': /* Dutch/German. */
79         case RUSSIAN_ROOK:
80             piece = ROOK;
81             break;
82         case 'N': case 'n':
83         case 'P': /* Dutch. */
84         case 'S': /* German. */
85             piece = KNIGHT;
86             break;
87         case 'B': 
88         case 'L': /* Dutch/German. */
89         case RUSSIAN_BISHOP:
90             /* Lower case 'b' is most likely to be a pawn reference. */
91             piece = BISHOP;
92             break;
93         case RUSSIAN_KNIGHT_OR_KING:
94             if(RUSSIAN_PIECE_CHECK(*(move+1)) == RUSSIAN_KING_SECOND_LETTER){
95                 piece = KING;
96             }
97             else{
98                 piece = KNIGHT;
99             }
100             break;
101     }
102     return piece;
103 }
104
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.:
108          *        Nxc3, e2-e4, etc.
109          */
110
111 static Boolean
112 is_capture(char c)
113 {
114     return (c == 'x') || (c == 'X') || (c == ':') || (c == '-');
115 }
116
117 static Boolean
118 is_castling_character(char c)
119 {
120     return (c == 'O') || (c == '0') || (c == 'o');
121 }
122
123 Boolean
124 is_check(char c)
125 {
126     return (c == '+') || (c == '#');
127 }
128
129         /* Allocate space in which to return the information that
130          * has been gleaned from the move.
131          */
132 Move *
133 new_move_structure(void)
134 {   Move *move = (Move *)MallocOrDie(sizeof(Move));
135
136     move->terminating_result = NULL;
137     move->epd = NULL;
138     move->bpfen = NULL;
139     move->Nags = NULL;
140     move->Comment = NULL;
141     move->Variants = NULL;
142     move->next = NULL;
143     return move;
144 }
145
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.
152          */
153 Move *
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;
158     MoveClass class;
159     Boolean Ok = TRUE;
160     /* Temporary locations until known whether they are from_ or to_. */
161     Col col = 0;
162     Rank rank = 0;
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. */
166     Move *move_details;
167     Piece piece_to_move = EMPTY;
168
169     /* Make an initial distinction between pawn moves and piece moves. */
170     if(is_col(*move)){
171         /* Pawn move. */
172         class = PAWN_MOVE;
173         piece_to_move = PAWN;
174         col = *move;
175         move++;
176         if(is_rank(*move)){
177             /* e4, e2e4 */
178             rank = *move;
179             move++;
180             if(is_capture(*move)){
181                 move++;
182             }
183             if(is_col(*move)){
184                 from_col = col;
185                 from_rank = rank;
186                 to_col = *move;
187                 move++;
188                 if(is_rank(*move)){
189                     to_rank = *move;
190                     move++;
191                 }
192             }
193             else{
194                 to_col = col;
195                 to_rank = rank;
196             }
197         }
198         else{
199             if(is_capture(*move)){
200                 /* axb */
201                 move++;
202             }
203             if(is_col(*move)){
204                 /* ab, or bg8 for liberal bishop moves. */
205                 from_col = col;
206                 to_col = *move;
207                 move++;
208                 if(is_rank(*move)){
209                     to_rank = *move;
210                     move++;
211                     /* Check the sanity of this. */
212                     if((from_col != 'b') &&
213                             (from_col != (to_col+1)) && (from_col != (to_col-1))){
214                         Ok = FALSE;
215                     }
216                 }
217                 else{
218                     /* Check the sanity of this. */
219                     if((from_col != (to_col+1)) && (from_col != (to_col-1))){
220                         Ok = FALSE;
221                     }
222                 }
223             }
224             else{
225                 print_error_context(GlobalState.logfile);
226                 fprintf(GlobalState.logfile,"Unknown pawn move %s.\n",move_string);
227                 Ok = FALSE;
228             }
229         }
230         if(Ok){
231             /* Look for promotions. */
232             if(*move == '='){
233                 move++;
234             }
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.
239                  */
240                 move++;
241             }
242         }
243     }
244     else if((piece_to_move = is_piece(move)) != EMPTY){
245         class = PIECE_MOVE;
246         /* Check for a two-character piece. */
247         if((RUSSIAN_PIECE_CHECK(*move) == RUSSIAN_KNIGHT_OR_KING) &&
248                                 (piece_to_move == KING)){
249             move++;
250         }
251         move++;
252         if(is_rank(*move)){
253             /* A disambiguating rank.
254              * R1e1, R1xe3.
255              */
256             from_rank = *move;
257             move++;
258             if(is_capture(*move)){
259                 move++;
260             }
261             if(is_col(*move)){
262                 to_col = *move;
263                 move++;
264                 if(is_rank(*move)){
265                     to_rank = *move;
266                     move++;
267                 }
268             }
269             else{
270                 Ok = FALSE;
271                 print_error_context(GlobalState.logfile);
272                 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
273             }
274         }
275         else{
276             if(is_capture(*move)){
277                 /* Rxe1 */
278                 move++;
279                 if(is_col(*move)){
280                     to_col = *move;
281                     move++;
282                     if(is_rank(*move)){
283                         to_rank = *move;
284                         move++;
285                     }
286                     else{
287                         Ok = FALSE;
288                         print_error_context(GlobalState.logfile);
289                         fprintf(GlobalState.logfile,
290                                         "Unknown piece move %s.\n",move_string);
291                     }
292                 }
293                 else{
294                     Ok = FALSE;
295                     print_error_context(GlobalState.logfile);
296                     fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
297                 }
298             }
299             else if(is_col(*move)){
300                 col = *move;
301                 move++;
302                 if(is_capture(*move)){
303                     move++;
304                 }
305                 if(is_rank(*move)){
306                     /* Re1, Re1d1, Re1xd1 */
307                     rank = *move;
308                     move++;
309                     if(is_capture(*move)){
310                         move++;
311                     }
312                     if(is_col(*move)){
313                         /* Re1d1 */
314                         from_col = col;
315                         from_rank = rank;
316                         to_col = *move;
317                         move++;
318                         if(is_rank(*move)){
319                             to_rank = *move;
320                             move++;
321                         }
322                         else{
323                             Ok = FALSE;
324                             print_error_context(GlobalState.logfile);
325                             fprintf(GlobalState.logfile,
326                                         "Unknown piece move %s.\n",move_string);
327                         }
328                     }
329                     else{
330                         to_col = col;
331                         to_rank = rank;
332                     }
333                 }
334                 else if(is_col(*move)){
335                     /* Rae1 */
336                     from_col = col;
337                     to_col = *move;
338                     move++;
339                     if(is_rank(*move)){
340                         to_rank = *move;
341                         move++;
342                     }
343                 }
344                 else{
345                     Ok = FALSE;
346                     print_error_context(GlobalState.logfile);
347                     fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
348                 }
349             }
350             else{
351                 Ok = FALSE;
352                 print_error_context(GlobalState.logfile);
353                 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
354             }
355         }
356     }
357     else if(is_castling_character(*move)){
358          /* Some form of castling. */
359          move++;
360          /* Allow separators to be optional. */
361          if(*move == '-'){
362              move++;
363          }
364          if(is_castling_character(*move)){
365              move++;
366              if(*move == '-'){
367                  move++;
368              }
369              if(is_castling_character(*move)){
370                  class = QUEENSIDE_CASTLE;
371                  move++;
372              }
373              else{
374                  class = KINGSIDE_CASTLE;
375              }
376          }
377          else{
378              print_error_context(GlobalState.logfile);
379              fprintf(GlobalState.logfile,"Unknown castling move %s.\n",move_string);
380              Ok = FALSE;
381          }
382     }
383     else if(strcmp((char *) move_string, NULL_MOVE_STRING) == 0) {
384         class = NULL_MOVE;
385     }
386     else{
387         print_error_context(GlobalState.logfile);
388         fprintf(GlobalState.logfile,"Unknown move %s.\n",move_string);
389         Ok = FALSE;
390     }
391     if(Ok && class != NULL_MOVE){
392         /* Allow trailing checks. */
393         while(is_check(*move)){
394             move++;
395         }
396         if(*move == '\0'){
397             /* Nothing more to check. */
398         }
399         else if(((strcmp((const char *) move,"ep") == 0) ||
400                             (strcmp((const char *) move,"e.p.") == 0)) &&
401                 (class == PAWN_MOVE)){
402             /* These are ok. */
403             class = ENPASSANT_PAWN_MOVE;
404         }
405         else{
406             Ok = FALSE;
407             print_error_context(GlobalState.logfile);
408             fprintf(GlobalState.logfile,
409                 "Unknown text trailing move %s <%s>.\n",move_string,move);
410         }
411     }
412     /* Store all of the details gathered, even if the move is illegal. */
413     if(!Ok){
414         class = UNKNOWN_MOVE;
415     }
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;
426     return move_details;
427 }
428
429 Move *
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]);
434
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;
440             }
441             else if(move_details->to_col == 'c'){
442                 move_details->class = QUEENSIDE_CASTLE;
443             }
444             else{
445                 move_details->class = PIECE_MOVE;
446                 move_details->piece_to_move = piece_to_move;
447             }
448         }
449         else{
450             if(piece_to_move == PAWN){
451                 move_details->class = PAWN_MOVE;
452             }
453             else{
454                 move_details->class = PIECE_MOVE;
455             }
456             move_details->piece_to_move = piece_to_move;
457         }
458         move_details->captured_piece = EMPTY;
459         move_details->check_status = NOCHECK;
460     }
461     return move_details;
462 }
463
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.
466          */
467 Boolean
468 move_seems_valid(const unsigned char *move_string)
469 {   MoveClass class;
470     Boolean Ok = TRUE;
471     /* A pointer to move along the move string. */
472     unsigned const char *move = move_string;
473
474     /* Make an initial distinction between pawn moves and piece moves. */
475     if(is_col(*move)){
476         /* Pawn move. */
477         class = PAWN_MOVE;
478         move++;
479         if(is_rank(*move)){
480             /* e4, e2e4 */
481             move++;
482             if(is_capture(*move)){
483                 move++;
484             }
485             if(is_col(*move)){
486                 move++;
487                 if(is_rank(*move)){
488                     move++;
489                 }
490             }
491             else{
492             }
493         }
494         else{
495             if(is_capture(*move)){
496                 /* axb */
497                 move++;
498             }
499             if(is_col(*move)){
500                 /* ab */
501                 move++;
502                 if(is_rank(*move)){
503                     move++;
504                 }
505             }
506             else{
507                 Ok = FALSE;
508             }
509         }
510         if(Ok){
511             /* Look for promotions. */
512             if(*move == '='){
513                 move++;
514             }
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.
519                  */
520                 move++;
521             }
522         }
523     }
524     else if(is_piece(move) != EMPTY){
525         class = PIECE_MOVE;
526         /* Check for a two-character piece. */
527         if((RUSSIAN_PIECE_CHECK(*move) == RUSSIAN_KNIGHT_OR_KING) &&
528                         (is_piece(move) == KING)){
529             move++;
530         }
531         move++;
532         if(is_rank(*move)){
533             /* A disambiguating rank.
534              * R1e1, R1xe3.
535              */
536             move++;
537             if(is_capture(*move)){
538                 move++;
539             }
540             if(is_col(*move)){
541                 move++;
542                 if(is_rank(*move)){
543                     move++;
544                 }
545             }
546             else{
547                 Ok = FALSE;
548             }
549         }
550         else{
551             if(is_capture(*move)){
552                 /* Rxe1 */
553                 move++;
554                 if(is_col(*move)){
555                     move++;
556                     if(is_rank(*move)){
557                         move++;
558                     }
559                     else{
560                         Ok = FALSE;
561                     }
562                 }
563                 else{
564                     Ok = FALSE;
565                 }
566             }
567             else if(is_col(*move)){
568                 move++;
569                 if(is_capture(*move)){
570                     move++;
571                 }
572                 if(is_rank(*move)){
573                     /* Re1, Re1d1, Re1xd1 */
574                     move++;
575                     if(is_capture(*move)){
576                         move++;
577                     }
578                     if(is_col(*move)){
579                         /* Re1d1 */
580                         move++;
581                         if(is_rank(*move)){
582                             move++;
583                         }
584                         else{
585                             Ok = FALSE;
586                         }
587                     }
588                 }
589                 else if(is_col(*move)){
590                     /* Rae1 */
591                     move++;
592                     if(is_rank(*move)){
593                         move++;
594                     }
595                     else{
596                         Ok = FALSE;
597                     }
598                 }
599                 else{
600                     Ok = FALSE;
601                 }
602             }
603             else{
604                 Ok = FALSE;
605             }
606         }
607     }
608     else if(is_castling_character(*move)){
609          /* Some form of castling. */
610          move++;
611          /* Allow separators to be optional. */
612          if(*move == '-'){
613              move++;
614          }
615          if(is_castling_character(*move)){
616              move++;
617              if(*move == '-'){
618                  move++;
619              }
620              if(is_castling_character(*move)){
621                  class = QUEENSIDE_CASTLE;
622                  move++;
623              }
624              else{
625                  class = KINGSIDE_CASTLE;
626              }
627          }
628          else{
629              Ok = FALSE;
630          }
631     }
632     else{
633         Ok = FALSE;
634     }
635     if(Ok){
636         /* Allow trailing checks. */
637         while(is_check(*move)){
638             move++;
639         }
640         if(*move == '\0'){
641             /* Nothing more to check. */
642         }
643         else if(((strcmp((const char *) move,"ep") == 0) ||
644                         (strcmp((const char *) move,"e.p.") == 0)) &&
645                 (class == PAWN_MOVE)){
646             /* These are ok. */
647             class = ENPASSANT_PAWN_MOVE;
648         }
649         else{
650             Ok = FALSE;
651         }
652     }
653     return Ok;
654 }