]> git.sesse.net Git - pgn-extract/blob - decode.c
Add support for outputting positions in my own bit-packed FEN format.
[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->Nags = NULL;
139     move->Comment = NULL;
140     move->Variants = NULL;
141     move->next = NULL;
142     return move;
143 }
144
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.
151          */
152 Move *
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;
157     MoveClass class;
158     Boolean Ok = TRUE;
159     /* Temporary locations until known whether they are from_ or to_. */
160     Col col = 0;
161     Rank rank = 0;
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. */
165     Move *move_details;
166     Piece piece_to_move = EMPTY;
167
168     /* Make an initial distinction between pawn moves and piece moves. */
169     if(is_col(*move)){
170         /* Pawn move. */
171         class = PAWN_MOVE;
172         piece_to_move = PAWN;
173         col = *move;
174         move++;
175         if(is_rank(*move)){
176             /* e4, e2e4 */
177             rank = *move;
178             move++;
179             if(is_capture(*move)){
180                 move++;
181             }
182             if(is_col(*move)){
183                 from_col = col;
184                 from_rank = rank;
185                 to_col = *move;
186                 move++;
187                 if(is_rank(*move)){
188                     to_rank = *move;
189                     move++;
190                 }
191             }
192             else{
193                 to_col = col;
194                 to_rank = rank;
195             }
196         }
197         else{
198             if(is_capture(*move)){
199                 /* axb */
200                 move++;
201             }
202             if(is_col(*move)){
203                 /* ab, or bg8 for liberal bishop moves. */
204                 from_col = col;
205                 to_col = *move;
206                 move++;
207                 if(is_rank(*move)){
208                     to_rank = *move;
209                     move++;
210                     /* Check the sanity of this. */
211                     if((from_col != 'b') &&
212                             (from_col != (to_col+1)) && (from_col != (to_col-1))){
213                         Ok = FALSE;
214                     }
215                 }
216                 else{
217                     /* Check the sanity of this. */
218                     if((from_col != (to_col+1)) && (from_col != (to_col-1))){
219                         Ok = FALSE;
220                     }
221                 }
222             }
223             else{
224                 print_error_context(GlobalState.logfile);
225                 fprintf(GlobalState.logfile,"Unknown pawn move %s.\n",move_string);
226                 Ok = FALSE;
227             }
228         }
229         if(Ok){
230             /* Look for promotions. */
231             if(*move == '='){
232                 move++;
233             }
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.
238                  */
239                 move++;
240             }
241         }
242     }
243     else if((piece_to_move = is_piece(move)) != EMPTY){
244         class = PIECE_MOVE;
245         /* Check for a two-character piece. */
246         if((RUSSIAN_PIECE_CHECK(*move) == RUSSIAN_KNIGHT_OR_KING) &&
247                                 (piece_to_move == KING)){
248             move++;
249         }
250         move++;
251         if(is_rank(*move)){
252             /* A disambiguating rank.
253              * R1e1, R1xe3.
254              */
255             from_rank = *move;
256             move++;
257             if(is_capture(*move)){
258                 move++;
259             }
260             if(is_col(*move)){
261                 to_col = *move;
262                 move++;
263                 if(is_rank(*move)){
264                     to_rank = *move;
265                     move++;
266                 }
267             }
268             else{
269                 Ok = FALSE;
270                 print_error_context(GlobalState.logfile);
271                 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
272             }
273         }
274         else{
275             if(is_capture(*move)){
276                 /* Rxe1 */
277                 move++;
278                 if(is_col(*move)){
279                     to_col = *move;
280                     move++;
281                     if(is_rank(*move)){
282                         to_rank = *move;
283                         move++;
284                     }
285                     else{
286                         Ok = FALSE;
287                         print_error_context(GlobalState.logfile);
288                         fprintf(GlobalState.logfile,
289                                         "Unknown piece move %s.\n",move_string);
290                     }
291                 }
292                 else{
293                     Ok = FALSE;
294                     print_error_context(GlobalState.logfile);
295                     fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
296                 }
297             }
298             else if(is_col(*move)){
299                 col = *move;
300                 move++;
301                 if(is_capture(*move)){
302                     move++;
303                 }
304                 if(is_rank(*move)){
305                     /* Re1, Re1d1, Re1xd1 */
306                     rank = *move;
307                     move++;
308                     if(is_capture(*move)){
309                         move++;
310                     }
311                     if(is_col(*move)){
312                         /* Re1d1 */
313                         from_col = col;
314                         from_rank = rank;
315                         to_col = *move;
316                         move++;
317                         if(is_rank(*move)){
318                             to_rank = *move;
319                             move++;
320                         }
321                         else{
322                             Ok = FALSE;
323                             print_error_context(GlobalState.logfile);
324                             fprintf(GlobalState.logfile,
325                                         "Unknown piece move %s.\n",move_string);
326                         }
327                     }
328                     else{
329                         to_col = col;
330                         to_rank = rank;
331                     }
332                 }
333                 else if(is_col(*move)){
334                     /* Rae1 */
335                     from_col = col;
336                     to_col = *move;
337                     move++;
338                     if(is_rank(*move)){
339                         to_rank = *move;
340                         move++;
341                     }
342                 }
343                 else{
344                     Ok = FALSE;
345                     print_error_context(GlobalState.logfile);
346                     fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
347                 }
348             }
349             else{
350                 Ok = FALSE;
351                 print_error_context(GlobalState.logfile);
352                 fprintf(GlobalState.logfile,"Unknown piece move %s.\n",move_string);
353             }
354         }
355     }
356     else if(is_castling_character(*move)){
357          /* Some form of castling. */
358          move++;
359          /* Allow separators to be optional. */
360          if(*move == '-'){
361              move++;
362          }
363          if(is_castling_character(*move)){
364              move++;
365              if(*move == '-'){
366                  move++;
367              }
368              if(is_castling_character(*move)){
369                  class = QUEENSIDE_CASTLE;
370                  move++;
371              }
372              else{
373                  class = KINGSIDE_CASTLE;
374              }
375          }
376          else{
377              print_error_context(GlobalState.logfile);
378              fprintf(GlobalState.logfile,"Unknown castling move %s.\n",move_string);
379              Ok = FALSE;
380          }
381     }
382     else if(strcmp((char *) move_string, NULL_MOVE_STRING) == 0) {
383         class = NULL_MOVE;
384     }
385     else{
386         print_error_context(GlobalState.logfile);
387         fprintf(GlobalState.logfile,"Unknown move %s.\n",move_string);
388         Ok = FALSE;
389     }
390     if(Ok && class != NULL_MOVE){
391         /* Allow trailing checks. */
392         while(is_check(*move)){
393             move++;
394         }
395         if(*move == '\0'){
396             /* Nothing more to check. */
397         }
398         else if(((strcmp((const char *) move,"ep") == 0) ||
399                             (strcmp((const char *) move,"e.p.") == 0)) &&
400                 (class == PAWN_MOVE)){
401             /* These are ok. */
402             class = ENPASSANT_PAWN_MOVE;
403         }
404         else{
405             Ok = FALSE;
406             print_error_context(GlobalState.logfile);
407             fprintf(GlobalState.logfile,
408                 "Unknown text trailing move %s <%s>.\n",move_string,move);
409         }
410     }
411     /* Store all of the details gathered, even if the move is illegal. */
412     if(!Ok){
413         class = UNKNOWN_MOVE;
414     }
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;
425     return move_details;
426 }
427
428 Move *
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]);
433
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;
439             }
440             else if(move_details->to_col == 'c'){
441                 move_details->class = QUEENSIDE_CASTLE;
442             }
443             else{
444                 move_details->class = PIECE_MOVE;
445                 move_details->piece_to_move = piece_to_move;
446             }
447         }
448         else{
449             if(piece_to_move == PAWN){
450                 move_details->class = PAWN_MOVE;
451             }
452             else{
453                 move_details->class = PIECE_MOVE;
454             }
455             move_details->piece_to_move = piece_to_move;
456         }
457         move_details->captured_piece = EMPTY;
458         move_details->check_status = NOCHECK;
459     }
460     return move_details;
461 }
462
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.
465          */
466 Boolean
467 move_seems_valid(const unsigned char *move_string)
468 {   MoveClass class;
469     Boolean Ok = TRUE;
470     /* A pointer to move along the move string. */
471     unsigned const char *move = move_string;
472
473     /* Make an initial distinction between pawn moves and piece moves. */
474     if(is_col(*move)){
475         /* Pawn move. */
476         class = PAWN_MOVE;
477         move++;
478         if(is_rank(*move)){
479             /* e4, e2e4 */
480             move++;
481             if(is_capture(*move)){
482                 move++;
483             }
484             if(is_col(*move)){
485                 move++;
486                 if(is_rank(*move)){
487                     move++;
488                 }
489             }
490             else{
491             }
492         }
493         else{
494             if(is_capture(*move)){
495                 /* axb */
496                 move++;
497             }
498             if(is_col(*move)){
499                 /* ab */
500                 move++;
501                 if(is_rank(*move)){
502                     move++;
503                 }
504             }
505             else{
506                 Ok = FALSE;
507             }
508         }
509         if(Ok){
510             /* Look for promotions. */
511             if(*move == '='){
512                 move++;
513             }
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.
518                  */
519                 move++;
520             }
521         }
522     }
523     else if(is_piece(move) != EMPTY){
524         class = PIECE_MOVE;
525         /* Check for a two-character piece. */
526         if((RUSSIAN_PIECE_CHECK(*move) == RUSSIAN_KNIGHT_OR_KING) &&
527                         (is_piece(move) == KING)){
528             move++;
529         }
530         move++;
531         if(is_rank(*move)){
532             /* A disambiguating rank.
533              * R1e1, R1xe3.
534              */
535             move++;
536             if(is_capture(*move)){
537                 move++;
538             }
539             if(is_col(*move)){
540                 move++;
541                 if(is_rank(*move)){
542                     move++;
543                 }
544             }
545             else{
546                 Ok = FALSE;
547             }
548         }
549         else{
550             if(is_capture(*move)){
551                 /* Rxe1 */
552                 move++;
553                 if(is_col(*move)){
554                     move++;
555                     if(is_rank(*move)){
556                         move++;
557                     }
558                     else{
559                         Ok = FALSE;
560                     }
561                 }
562                 else{
563                     Ok = FALSE;
564                 }
565             }
566             else if(is_col(*move)){
567                 move++;
568                 if(is_capture(*move)){
569                     move++;
570                 }
571                 if(is_rank(*move)){
572                     /* Re1, Re1d1, Re1xd1 */
573                     move++;
574                     if(is_capture(*move)){
575                         move++;
576                     }
577                     if(is_col(*move)){
578                         /* Re1d1 */
579                         move++;
580                         if(is_rank(*move)){
581                             move++;
582                         }
583                         else{
584                             Ok = FALSE;
585                         }
586                     }
587                 }
588                 else if(is_col(*move)){
589                     /* Rae1 */
590                     move++;
591                     if(is_rank(*move)){
592                         move++;
593                     }
594                     else{
595                         Ok = FALSE;
596                     }
597                 }
598                 else{
599                     Ok = FALSE;
600                 }
601             }
602             else{
603                 Ok = FALSE;
604             }
605         }
606     }
607     else if(is_castling_character(*move)){
608          /* Some form of castling. */
609          move++;
610          /* Allow separators to be optional. */
611          if(*move == '-'){
612              move++;
613          }
614          if(is_castling_character(*move)){
615              move++;
616              if(*move == '-'){
617                  move++;
618              }
619              if(is_castling_character(*move)){
620                  class = QUEENSIDE_CASTLE;
621                  move++;
622              }
623              else{
624                  class = KINGSIDE_CASTLE;
625              }
626          }
627          else{
628              Ok = FALSE;
629          }
630     }
631     else{
632         Ok = FALSE;
633     }
634     if(Ok){
635         /* Allow trailing checks. */
636         while(is_check(*move)){
637             move++;
638         }
639         if(*move == '\0'){
640             /* Nothing more to check. */
641         }
642         else if(((strcmp((const char *) move,"ep") == 0) ||
643                         (strcmp((const char *) move,"e.p.") == 0)) &&
644                 (class == PAWN_MOVE)){
645             /* These are ok. */
646             class = ENPASSANT_PAWN_MOVE;
647         }
648         else{
649             Ok = FALSE;
650         }
651     }
652     return Ok;
653 }