]> git.sesse.net Git - pgn-extract/blob - moves.c
Push through a computer/human flag to the binary output.
[pgn-extract] / moves.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         /***
24          * These routines are concerned with gathering moves of
25          * the various sorts of variations specified by the -v
26          * and -x flags.  In the former case, there are also functions
27          * for checking the moves of a game against the variation
28          * lists that are wanted.  Checking of the variations specified
29          * by the -x flag is handled elsewhere by apply_move_list().
30          */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include "bool.h"
37 #include "mymalloc.h"
38 #include "lines.h"
39 #include "defs.h"
40 #include "typedef.h"
41 #include "map.h"
42 #include "lists.h"
43 #include "tokens.h"
44 #include "taglist.h"
45 #include "lex.h"
46 #include "moves.h"
47 #include "apply.h"
48 #include "decode.h"
49 #include "fenmatcher.h"
50
51 /* Define a character that can be used in the variations file to
52  * mean that we don't mind what move was played at this point.
53  * So: 
54  *        * b6
55  * means look for all games in which Black playes 1...b6, regardless
56  * of White's first move.
57  */
58 #define ANY_MOVE '*'
59 /* Define a character that can be used in the variations file to
60  * mean that we do not wish to match a particular move.
61  * So:
62  *        e4 c5 !Nf3
63  * means look for games in which Black does not play 2. Nf3 against
64  * the Sicilian defence.
65  */
66 #define DISALLOWED_MOVE '!'
67
68     /* Hold details of a single move within a variation. */
69 typedef struct{
70     /* Characters of the move.
71      * Alternative notations for the same move are separated by
72      * a non-move character, e.g.:
73      *        cxd|cxd4|c5xd4
74      * could all be alternatives for the same basic pawn capture.
75      */
76     char *move;
77     /* If we are interested in matching the moves in any order,
78      * then we need to record whether or not the current move has
79      * been matched or not.
80      */
81     Boolean matched;
82 } variant_move;
83
84     /* Hold details of a single variation, with a pointer to
85      * an alternative variation.
86      */
87 typedef struct variation_list {
88     /* The list of moves. */
89     variant_move *moves;
90     /* Keep a count of how many ANY_MOVE moves there are in the move
91      * list for each colour.  If these are non-zero then one is used
92      * when a match fails when looking for permutations.
93      */
94     unsigned num_white_any_moves;
95     unsigned num_black_any_moves;
96     /* Keep a count of how many DISALLOWED_MOVE moves there are in the move
97      * list for each colour.  If these are non-zero then the game
98      * must be searched for them when looking for permutations before
99      * the full search is made.
100      */
101     unsigned num_white_disallowed_moves;
102     unsigned num_black_disallowed_moves;
103     /* How many half-moves in the variation? */
104     unsigned length;
105     struct variation_list *next;
106 } variation_list;
107
108     /* The head of the variations-of-interest list. */
109 static variation_list *games_to_keep = NULL;
110
111 static Boolean textual_variation_match(const char *variation_move,
112                                         const unsigned char *actual_move);
113
114         /*** Functions concerned with reading details of the variations
115          *** of interest.
116          ***/
117
118         /* Remove any move number prefix from str.
119          * Return NULL if there is no move (only number)
120          * otherwise return the head of the move portion.
121          */
122 static char *
123 strip_move_number(char *str)
124 {
125     while(isdigit((int) *str)){
126       str++;
127     }
128     while(*str == '.'){
129       str++;
130     }
131     if(*str != '\0'){
132         return str;
133     }
134     else{
135         return (char *)NULL;
136     }
137 }
138
139
140
141 /* Define values for malloc/realloc allocation. */
142 #define INIT_MOVE_NUMBER 10
143 #define MOVE_INCREMENT 5
144
145         /* Break up a single line of moves into a list of moves
146          * comprising a variation.
147          */
148 static variation_list *
149 compose_variation(char *line)
150 {   variation_list *variation;
151     variant_move *move_list;
152     /* Keep track of the number of moves extracted from line. */
153     unsigned num_moves = 0;
154     unsigned max_moves = 0;
155     char *move;
156
157     variation = (variation_list *)MallocOrDie(sizeof(variation_list));
158     /* We don't yet know how many ANY_MOVEs or DISALLOWED_MOVES there
159      * will be in this variation.
160      */
161     variation->num_white_any_moves = 0;
162     variation->num_black_any_moves = 0;
163     variation->num_white_disallowed_moves = 0;
164     variation->num_black_disallowed_moves = 0;
165     /* Allocate an initial number of pointers for the moves of the variation. */
166     move_list = (variant_move *) MallocOrDie(INIT_MOVE_NUMBER *
167                                              sizeof(*move_list));
168     max_moves = INIT_MOVE_NUMBER;
169     /* Find the first move. */
170     move = strtok(line," ");
171     while(move != NULL){
172         if((move = strip_move_number(move)) == NULL){
173             /* Only a move number. */
174         }
175         else{
176             /* See if we need some more space. */
177             if(num_moves == max_moves){
178                  move_list = (variant_move *)realloc((void *)move_list,
179                             (max_moves+MOVE_INCREMENT) * sizeof(*move_list));
180                  if(move_list == NULL){
181                      /* Catastrophic failure. */
182                      free((void *)variation);
183                      return NULL;
184                  }
185                  else{
186                      max_moves += MOVE_INCREMENT;
187                  }
188             }
189             /* Keep the move and initialise the matched field for
190              * when we start matching games.
191              */
192             move_list[num_moves].move = move;
193             move_list[num_moves].matched = FALSE;
194             /* Keep track of moves that will match anything. */
195             if(*move == ANY_MOVE){
196                 /* Odd numbered half-moves in the variant list are Black. */
197                 if(num_moves & 0x01){
198                     variation->num_black_any_moves++;
199                 }
200                 else{
201                     variation->num_white_any_moves++;
202                 }
203                 /* Beware of the potential for false matches. */
204                 if(strlen(move) > 1){
205                     fprintf(GlobalState.logfile,
206                         "Warning: %c in %s should not be followed by additional move text.\n",
207                             *move,move);
208                     fprintf(GlobalState.logfile,"It could give false matches.\n");
209                 }
210             }
211             else if(*move == DISALLOWED_MOVE){
212                 /* Odd numbered half-moves in the variant list are Black. */
213                 if(num_moves & 0x01){
214                     variation->num_black_disallowed_moves++;
215                 }
216                 else{
217                     variation->num_white_disallowed_moves++;
218                 }
219             }
220             else{
221                 /* Unadorned move. */
222             }
223             num_moves++;
224         }
225         move = strtok((char *)NULL," ");
226     }
227     variation->moves = move_list;
228     variation->length = num_moves;
229     variation->next = NULL;
230     return variation;
231 }
232
233         /* Read each line of input and decompose it into a variation
234          * to be placed in the games_to_keep list.
235          */
236 void
237 add_textual_variations_from_file(FILE *fpin)
238 {   char *line;
239
240     while((line = read_line(fpin)) != NULL){
241         add_textual_variation_from_line(line);
242     }
243 }
244
245         /* Add the text of the given line to the list of games_to_keep.
246          */
247 void
248 add_textual_variation_from_line(char *line)
249 {
250     if(non_blank_line(line)){
251         variation_list *next_variation = compose_variation(line);
252         if(next_variation != NULL){
253             next_variation->next = games_to_keep;
254             games_to_keep = next_variation;
255         }
256     }
257 }
258
259         /*** Functions concerned with reading details of the positional
260          *** variations of interest.
261          ***/
262
263         /* Break up a single line of moves into a list of moves
264          * comprising a positional variation.
265          * In doing so, set GlobalState.depth_of_positional_search
266          * if this variation is longer than the default.
267          */
268 static Move *
269 compose_positional_variation(char *line)
270 {   char *move;
271     /* Build a linked list of the moves of the variation. */
272     Move *head = NULL, *tail = NULL;
273     Boolean Ok = TRUE;
274     /* Keep track of the depth. */
275     unsigned depth = 0;
276
277     move = strtok(line," ");
278     while(Ok && (move != NULL) && (*move != '*')){
279         if((move = strip_move_number(move)) == NULL){
280             /* Only a move number. */
281         }
282         else{
283             Move *next = decode_move((unsigned char *) move);
284
285             if(next == NULL){
286                 fprintf(GlobalState.logfile,"Failed to identify %s\n",move);
287                 Ok = FALSE;
288             }
289             else{
290                 /* Chain it on to the list. */
291                 if(tail == NULL){
292                     head = next;
293                     tail = next;
294                 }
295                 else{
296                     tail->next = next;
297                     tail = next;
298                 }
299                 next->next = NULL;
300             }
301             depth++;
302         }
303         /* Pick up the next likely move. */
304         move = strtok(NULL," ");
305     }
306     if(Ok){
307         /* Determine whether the depth of this variation exceeds
308          * the current default.
309          * Depth is counted in move pairs.
310          * Add some extras, in order to be surer of catching transpositions.
311          */
312         depth = ((depth+1) / 2) + 4;
313         if(depth > GlobalState.depth_of_positional_search){
314             GlobalState.depth_of_positional_search = depth;
315         }
316     }
317     else{
318         if(head != NULL){
319             free_move_list(head);
320         }
321         head = NULL;
322     }
323     return head;
324 }
325
326         /* Read each line of input and decompose it into a positional variation
327          * to be placed in the list of required hash values.
328          */
329 void
330 add_positional_variations_from_file(FILE *fpin)
331 {   char *line;
332
333     while((line = read_line(fpin)) != NULL){
334         add_positional_variation_from_line(line);
335     }
336 }
337
338 void
339 add_positional_variation_from_line(char *line)
340 {
341     if(non_blank_line(line)){
342         Move *next_variation = compose_positional_variation(line);
343         if(next_variation != NULL){
344             /* We need a NULL fen string, because this is from
345              * the initial position.
346              */
347             store_hash_value(next_variation,(const char *)NULL);
348             free_move_list(next_variation);
349             /* We need to know globally that positional variations
350              * are of interest.
351              */
352             GlobalState.positional_variations = TRUE;
353         }
354     }
355 }
356
357         /* Treat fen_string as being a position to be matched.
358          */
359 void
360 add_fen_positional_match(const char *fen_string)
361 {
362     store_hash_value((Move *)NULL,fen_string);
363     GlobalState.positional_variations = TRUE;
364 }
365
366         /* Treat fen_pattern as being a position to be matched.
367          */
368 void
369 add_fen_pattern_match(const char *fen_pattern)
370 {
371     add_fen_pattern(fen_pattern);
372     GlobalState.positional_variations = TRUE;
373 }
374
375         /* Roughly define a move character for the purposes of textual
376          * matching.
377          */
378 static Boolean
379 move_char(char c)
380 {
381     return (Boolean) isalpha((int) c) || isdigit((int) c) || (c == '-');
382 }
383
384         /* Return TRUE if there is a match for actual_move in variation_move.
385          * A match means that the string in actual_move is found surrounded
386          * by non-move characters in variation_move. For instance,
387          *    variation_move == "Nc6|Nf3|f3" would match
388          *    actual_move == "f3" but not actual_move == "c6".
389          */
390 static Boolean
391 textual_variation_match(const char *variation_move,const unsigned char *actual_move)
392 {   const char *match_point;
393     Boolean found = FALSE;
394
395     for(match_point = variation_move; !found && (match_point != NULL); ){
396         /* Try for a match from where we are. */
397         match_point = strstr(match_point,(const char *) actual_move);
398         if(match_point != NULL){
399             /* A possible match. Make sure that the match point
400              * is surrounded by non-move characters so as to be sure
401              * that we haven't picked up part way through a variation string.
402              * Assume success.
403              */
404             found = TRUE;
405             if(match_point != variation_move){
406                 if(move_char(match_point[-1])){
407                     found = FALSE;
408                 }
409             }
410             if(move_char(match_point[strlen((const char *) actual_move)])){
411                 found = FALSE;
412             }
413             if(!found){
414                 /* Move on the match point and try again. */
415                 while(move_char(*match_point)){
416                     match_point++;
417                 }
418             }
419         }
420     }
421     return found;
422                         
423 }
424
425         /*** Functions concerned with matching the moves of the current game
426          *** against the variations of interest.
427          ***/
428
429         /* Do the moves of the current game match the given variation?
430          * Go for a straight 1-1 match in the ordering, without considering
431          * permutations.
432          * Return TRUE if so, FALSE otherwise.
433          */
434 static Boolean
435 straight_match(Move *current_game_head, variation_list variation)
436 {
437     variant_move *moves_of_the_variation;
438     /* Which is the next move that we wish to match. */
439     Move *next_move;
440     unsigned move_index = 0;
441     /* Assume that it matches. */
442     Boolean matches = TRUE;
443
444     /* Access the head of the current game. */
445     next_move = current_game_head;
446     /* Go for a straight move-by-move match in the order in which
447      * the variation is listed.
448      * Point at the head of the moves list.
449      */
450     moves_of_the_variation = variation.moves;
451     move_index = 0;
452     while(matches && (next_move != NULL) && (move_index < variation.length)){
453         Boolean this_move_matches;
454
455         if(*(moves_of_the_variation[move_index].move) == ANY_MOVE){
456             /* Still matching as we don't care what the actual move is. */
457         }
458         else{
459             this_move_matches =
460                 textual_variation_match(moves_of_the_variation[move_index].move,
461                                             next_move->move);
462             if(this_move_matches){
463                 /* We found a match, check that it isn't disallowed. */
464                 if(*moves_of_the_variation[move_index].move == DISALLOWED_MOVE){
465                     /* This move is disallowed and implies failure. */
466                     matches = FALSE;
467                 }
468             }
469             else{
470                 if(*moves_of_the_variation[move_index].move != DISALLOWED_MOVE){
471                     /* No match found for this move. */
472                     matches = FALSE;
473                 }
474                 else{
475                     /* This is ok, because we didn't want a match. */
476                 }
477             }
478                 
479         }
480         /* If we are still matching, go on to the next move. */
481         if(matches){
482             move_index++;
483             next_move = next_move->next;
484         }
485     }
486     /* The game could be shorter than the variation, so don't rely
487      * on the fact that matches is still true.
488      */
489     matches = (move_index == variation.length);
490     return matches;
491 }
492
493     /* Do the moves of the current game match the given variation?
494      * Try all possible orderings for the moves, within the
495      * constraint of proper WHITE/BLACK moves.
496      * The parameter variation is passed as a copy because we modify
497      * the num_ fields within it.
498      * Note that there is a possibility of a false match in this
499      * function if a variant move is specified in a form such as:
500      *                *|c4
501      * This is because the num_ field is set from this on the basis of the
502      * ANY_MOVE character at the start.  However, this could also match a
503      * normal move with its c4 component.  If it is used for the latter
504      * purpose then it should not count as an any_ move.  There is a warning
505      * issued about this when variations are read in.
506      * Return TRUE if we match, FALSE otherwise.
507      *
508      * The DISALLOWED_MOVE presents some problems with permutation matches
509      * because an ANY_MOVE could match an otherwise disallowed move. The
510      * approach that has been taken is to cause matching of a single disallowed
511      * move to result in complete failure of the current match.
512      */
513 static Boolean
514 permutation_match(Move *current_game_head, variation_list variation)
515 {   variant_move *moves_of_the_variation;
516     /* Which is the next move that we wish to match? */
517     Move *next_move;
518     unsigned variant_index = 0;
519     /* Assume that it matches. */
520     Boolean matches = TRUE;
521     /* How many moves have we matched?
522      * When this reaches variation.length we have a full match.
523      */
524     unsigned matched_moves = 0;
525     Boolean white_to_move = TRUE;
526
527     moves_of_the_variation = variation.moves;
528     /* Clear all of the matched fields of the variation. */
529     for(variant_index = 0; variant_index < variation.length; variant_index++){
530         moves_of_the_variation[variant_index].matched = FALSE;
531     }
532     /* Access the head of the current game. */
533     next_move = current_game_head;
534
535     /*** Stage One.
536      * The first task is to ensure that there are no DISALLOWED_MOVEs in
537      * the current game.
538      */
539     if((variation.num_white_disallowed_moves > 0) ||
540             (variation.num_black_disallowed_moves > 0)){
541         unsigned tested_moves = 0;
542         Boolean disallowed_move_found = FALSE;
543
544         /* Keep going as long as we still have not found a diallowed move,
545          * we haven't matched the whole variation, and we haven't reached the end of
546          * the game.
547          */
548         while((!disallowed_move_found) && (tested_moves < variation.length) &&
549                                         (next_move != NULL)){
550             /* We want to see if next_move is a disallowed move of the variation. */
551             if(white_to_move){
552                 /* White; start with the first move. */
553                 variant_index = 0;
554             }
555             else{
556                 /* For a Black move, start at the second half-move in the list, if any. */
557                 variant_index = 1;
558             }
559             /* Try each move of the variation in turn, until a match is found. */
560             while((!disallowed_move_found) && (variant_index < variation.length)){
561                 if((*moves_of_the_variation[variant_index].move ==
562                             DISALLOWED_MOVE) &&
563                    (textual_variation_match(
564                       moves_of_the_variation[variant_index].move,next_move->move))){
565                     /* Found one. */
566                     disallowed_move_found = TRUE;
567                 }
568                 if(!disallowed_move_found){
569                     /* Move on to the next available move -- 2 half moves along. */
570                     variant_index += 2;
571                 }
572             }
573             if(!disallowed_move_found){
574                 /* Ok so far, so move on. */
575                 tested_moves++;
576                 white_to_move = !white_to_move;
577                 next_move = next_move->next;
578             }
579         }
580         if(disallowed_move_found){
581             /* This rules out the whole match. */
582             matches = FALSE;
583         }
584         else{
585             /* In effect, each DISALLOWED_MOVE now becomes an ANY_MOVE. */
586             for(variant_index = 0; variant_index < variation.length; variant_index++){
587                 if(*moves_of_the_variation[variant_index].move == DISALLOWED_MOVE){
588                     moves_of_the_variation[variant_index].matched = TRUE;
589                     if((variant_index & 1) == 0){
590                         variation.num_white_any_moves++;
591                     }
592                     else{
593                         variation.num_black_any_moves++;
594                     }
595                 }
596             }
597         }
598     }
599
600     /*** Stage Two.
601      * Having eliminated moves which have been disallowed, try permutations
602      * of the variation against the moves of the current game.
603      */
604     /* Access the head of the current game. */
605     next_move = current_game_head;
606     white_to_move = TRUE;
607     /* Keep going as long as we still have matches, we haven't
608      * matched the whole variation, and we haven't reached the end of
609      * the game.
610      */
611     while(matches && (matched_moves < variation.length) && (next_move != NULL)){
612         /* Assume failure. */
613         matches = FALSE;
614         /* We want to find next_move in an unmatched move of the variation. */
615         if(white_to_move){
616             /* Start with the first move. */
617             variant_index = 0;
618         }
619         else{
620             /* For a Black move, start at the second half-move in the list, if any. */
621             variant_index = 1;
622         }
623         /* Try each move of the variation in turn, until a match is found. */
624         while((!matches) && (variant_index < variation.length)){
625             if(moves_of_the_variation[variant_index].matched){
626                 /* We can't try this. */
627             }
628             else{
629                 Boolean this_move_matches = textual_variation_match(
630                             moves_of_the_variation[variant_index].move,
631                                         next_move->move);
632                 if(this_move_matches){
633                     /* Found it. */
634                     moves_of_the_variation[variant_index].matched = TRUE;
635                     matches = TRUE;
636                 }
637             }
638             if(!matches){
639                 /* Move on to the next available move -- 2 half moves along. */
640                 variant_index += 2;
641             }
642         }
643         /* See if we made a straight match. */
644         if(!matches){
645             /* See if we have some ANY_MOVEs available. */
646             if(white_to_move && (variation.num_white_any_moves > 0)){
647                 matches = TRUE;
648                 variation.num_white_any_moves--;
649             }
650             else if(!white_to_move && (variation.num_black_any_moves > 0)){
651                 matches = TRUE;
652                 variation.num_black_any_moves--;
653             }
654             else{
655                 /* No slack. */
656             }
657         }
658         /* We have tried everything, did we succeed? */
659         if(matches){
660             /* Yes, so move on. */
661             matched_moves++;
662             next_move = next_move->next;
663             white_to_move = !white_to_move;
664         }
665     }
666     if(matches){
667         /* Ensure that we completed the variation. */
668         matches = matched_moves == (variation.length);
669     }
670     return matches;
671 }
672
673
674         /* Determine whether or not the current game is wanted.
675          * It will be if we are either not looking for checkmate-only
676          * games, or if we are and the games does end in checkmate.
677          */
678 Boolean
679 check_for_only_checkmate(const Move *moves)
680 {
681     if(GlobalState.match_only_checkmate) {
682         /* Check that the final move is checkmate. */
683         while(moves != NULL && moves->check_status != CHECKMATE) {
684             moves = moves->next;
685         }
686         if(moves == NULL) {
687             return FALSE;
688         }
689         else {
690             return TRUE;
691         }
692     }
693     else {
694         /* No restriction to a checkmate game. */
695         return TRUE;
696     }
697 }
698
699         /* Determine whether or not the current game is wanted.
700          * It will be if we are either not looking for stalemate-only
701          * games, or if we are and the games does end in stalemate.
702          */
703 Boolean
704 check_for_only_stalemate(const Board *board, const Move *moves)
705 {
706     if(GlobalState.match_only_stalemate) {
707         const Move *move = moves;
708         if(move != NULL) {
709             MovePair *available_moves;
710
711             /* Check that the final move is not check or checkmate. */
712             while(move->next != NULL) {
713                 move = move->next;
714             }
715             if(move->check_status != NOCHECK) {
716                 /* Cannot be stalemate. */
717                 return FALSE;
718             }
719             /* See if the player to move has any moves available. */
720             available_moves = find_all_moves(board, board->to_move);
721             if(available_moves == NULL) {
722                 return TRUE;
723             }
724             else {
725                 return FALSE;
726             }
727         }
728         else {
729             return TRUE;
730         }
731     }
732     else {
733         /* No restriction to a stalemate game. */
734         return TRUE;
735     }
736 }
737         /* Determine whether or not the current game is wanted.
738          * It will be if it matches one of the current variations
739          * and its tag details match those that we are interested in.
740          */
741 Boolean
742 check_textual_variations(Game game_details)
743 {   Boolean wanted = FALSE;
744     variation_list *variation;
745
746     if(games_to_keep != NULL){
747         for(variation = games_to_keep; (variation != NULL) && !wanted;
748                     variation = variation->next){
749             if(GlobalState.match_permutations){
750                 wanted = permutation_match(game_details.moves,*variation);
751             }
752             else{
753                 wanted = straight_match(game_details.moves,*variation);
754             }
755         }
756     }
757     else{
758         /* There are no variations, assume that selection is done
759          * on the basis of the Details.
760          */
761         wanted = TRUE;
762     }
763     return wanted;
764 }
765
766         /* Determine whether the number of moves in this game
767          * is within the bounds of what we want.
768          */
769 Boolean
770 check_move_bounds(unsigned plycount)
771 {   Boolean in_bounds = TRUE;
772
773     unsigned number_of_moves = (plycount + 1) / 2;
774     if(GlobalState.check_move_bounds){
775          in_bounds = (GlobalState.lower_move_bound <= number_of_moves) &&
776                          (number_of_moves <= GlobalState.upper_move_bound);
777     }
778     return in_bounds;
779 }