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/
42 #include "fenmatcher.h"
44 /* Define a positional search depth that should look at the
45 * full length of a game. This is used in play_moves().
47 #define DEFAULT_POSITIONAL_DEPTH 300
49 /* Prototypes of functions limited to this file. */
50 static Boolean position_matches(const Board *board);
51 static Boolean play_moves(Game *game_details, Board *board, Move *moves,
52 unsigned max_depth, Boolean check_move_validity);
53 static Boolean apply_variations(const Game *game_details,const Board *board,
54 Variation *variation, Boolean check_move_validity);
55 static Boolean rewrite_variations(const Board *board,Variation *variation, Boolean null_move_found);
56 static Boolean rewrite_moves(Board *board, Move *move_details, Boolean null_move_found);
57 static void append_evaluation(Move *move_details, const Board *board);
58 static void append_FEN_comment(Move *move_details, const Board *board);
59 static double evaluate(const Board *board);
60 static double shannonEvaluation(const Board *board);
62 /* The English SAN piece characters. These are
63 * always used when building a FEN string, rather
64 * than using any language-dependent user settings.
66 static char SAN_piece_characters[NUM_PIECE_VALUES] = {
68 'P', 'N', 'B', 'R', 'Q', 'K'
72 /* These letters may be changed via a call to set_output_piece_characters
73 * with a string of the form "PNBRQK".
74 * This would normally be done with the -Wsan argument.
76 static const char *output_piece_characters[NUM_PIECE_VALUES] = {
78 "P", "N", "B", "R", "Q", "K"
81 /* letters should contain a string of the form: "PNBRQK" */
82 void set_output_piece_characters(const char *letters)
85 fprintf(GlobalState.logfile,
86 "NULL string passed to set_output_piece_characters.\n");
91 for(piece_index = 0, piece = PAWN; piece <= KING &&
92 letters[piece_index] != '\0'; piece++){
93 /* Check whether we have a single character piece,
94 * or one of the form X+Y, where the piece is represented
95 * by the combination XY.
97 if(letters[piece_index+1] == '+'){
98 /* A two-char piece. */
99 static char double_char_piece[] = "XY";
100 double_char_piece[0] = letters[piece_index];
104 if(letters[piece_index] != '\0'){
105 double_char_piece[1] = letters[piece_index];
106 output_piece_characters[piece] = copy_string(double_char_piece);
110 fprintf(GlobalState.logfile,
111 "Missing piece letter following + in -Wsan%s.\n",
117 static char single_char_piece[] = "X";
118 *single_char_piece = letters[piece_index];
119 output_piece_characters[piece] = copy_string(single_char_piece);
123 if(piece < NUM_PIECE_VALUES){
124 fprintf(GlobalState.logfile,
125 "Insufficient piece letters found with -Wsan%s.\n",
127 fprintf(GlobalState.logfile,
128 "The argument should be of the form -Wsan%s.\n",
132 else if(letters[piece_index] != '\0'){
133 fprintf(GlobalState.logfile,
134 "Too many piece letters found with -Wsan%s.\n",
136 fprintf(GlobalState.logfile,
137 "The argument should be of the form -Wsan%s.\n",
147 /* Return a fresh copy of the given string. */
149 copy_string(const char *str)
151 size_t len = strlen(str);
153 result = MallocOrDie(len+1);
158 /* Allocate space for a new board. */
160 allocate_new_board(void)
162 return (Board *) MallocOrDie(sizeof(Board));
165 /* Free the board space. */
167 free_board(Board *board)
169 (void) free((void *)board);
174 { Piece piece = EMPTY;
199 /* Return the SAN letter associated with the given piece. */
201 SAN_piece_letter(Piece piece)
203 if(piece < NUM_PIECE_VALUES){
204 return SAN_piece_characters[piece];
211 /* Return the SAN letter for the given Piece. */
213 coloured_piece_to_SAN_letter(Piece coloured_piece)
214 { Piece piece = EXTRACT_PIECE(coloured_piece);
215 char letter = SAN_piece_letter(piece);
216 if(EXTRACT_COLOUR(coloured_piece) == BLACK){
217 letter = tolower(letter);
222 /* Set up the board from the FEN string passed as
225 * Forsythe string of the setup position.
226 * w/b - colour to move.
227 * castling permissions.
229 * half-moves since pawn move/piece capture.
233 new_fen_board(const char *fen)
234 { Board *new_board = allocate_new_board();
235 /* Start with a clear board. */
236 static const Board initial_board = {
237 { { OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF},
238 { OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF},
239 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
240 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
241 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
242 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
243 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
244 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
245 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
246 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
247 { OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF},
248 { OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF}
250 /* Who to move next. */
254 /* Castling rights. */
255 FALSE, FALSE, FALSE, FALSE,
256 /* Initial king positions. */
259 /* En Passant rights. */
261 /* Initial hash value. */
266 Rank rank = LASTRANK;
268 const char *fen_char = fen;
270 /* In some circumstances we will try to parse the game data,
271 * even if there are errors.
273 Boolean try_to_parse_game = FALSE;
275 /* Reset the contents of the new board. */
276 *new_board = initial_board;
277 /* Extract the piece positions. */
279 while(Ok && (*fen_char != ' ') && (*fen_char != '\0') &&
280 (rank >= FIRSTRANK)){
285 if((piece = is_FEN_piece(ch)) != EMPTY){
286 if(isupper((int) ch)){
293 new_board->board[RankConvert(rank)][ColConvert(col)] =
294 MAKE_COLOURED_PIECE(colour,piece);
297 new_board->WKingCol = col;
298 new_board->WKingRank = rank;
301 new_board->BKingCol = col;
302 new_board->BKingRank = rank;
312 else if(isdigit((int) ch)){
313 if(('1' <= ch) && (ch <= '8')){
315 /* In filling up the remaining columns of a rank we will
316 * temporarily exceed LASTCOL, but we expect the
317 * next character to be '/' so that we reset.
319 if(col <= (LASTCOL+1)){
331 /* End of that rank. We should have completely filled the
334 if(col == (LASTCOL+1)){
344 /* Unknown character. */
348 /* As we don't print any error messages until the end of the function,
349 * we don't need to guard everything with if(Ok).
351 if(*fen_char == ' '){
352 /* Find out who is to move. */
358 if(*fen_char == 'w'){
359 new_board->to_move = WHITE;
362 else if(*fen_char == 'b'){
363 new_board->to_move = BLACK;
369 if(*fen_char == ' '){
375 /* Determine castling rights. */
376 if(*fen_char == '-'){
377 /* No castling rights -- default above. */
378 new_board->WKingCastle = new_board->WQueenCastle =
379 new_board->BKingCastle = new_board->BQueenCastle = FALSE;
383 /* Check to make sure that this section isn't empty. */
384 if(*fen_char == ' '){
387 if(*fen_char == 'K'){
388 new_board->WKingCastle = TRUE;
391 if(*fen_char == 'Q'){
392 new_board->WQueenCastle = TRUE;
395 if(*fen_char == 'k'){
396 new_board->BKingCastle = TRUE;
399 if(*fen_char == 'q'){
400 new_board->BQueenCastle = TRUE;
404 if(*fen_char == ' '){
410 /* If we are ok to this point, try to make a best efforts approach
411 * to handle the game, even if there are subsequent errors.
414 try_to_parse_game = TRUE;
416 /* Check for an en-passant square. */
417 if(*fen_char == '-'){
421 else if(is_col(*fen_char)){
424 if(is_rank(*fen_char)){
427 /* Make sure that the en-passant indicator is consistent
428 * with whose move it is.
430 if(((new_board->to_move == WHITE) && (rank == '6')) ||
431 ((new_board->to_move == BLACK) && (rank == '3'))){
433 new_board->EnPassant = TRUE;
434 new_board->ep_rank = rank;
435 new_board->ep_col = col;
448 if(*fen_char == ' '){
454 /* Check for half-move count since last pawn move
457 if(isdigit((int) *fen_char)){
458 unsigned halfmove_clock = *fen_char-'0';
460 while(isdigit((int) *fen_char)){
461 halfmove_clock = (halfmove_clock*10)+(*fen_char-'0');
464 new_board->halfmove_clock = halfmove_clock;
469 if(*fen_char == ' '){
475 /* Check for current move number. */
476 if(isdigit((int) *fen_char)){
477 unsigned move_number = 0;
479 move_number = *fen_char-'0';
481 while(isdigit((int) *fen_char)){
482 move_number = (move_number*10)+(*fen_char-'0');
485 if(move_number < 1) {
488 new_board->move_number = move_number;
493 /* Allow trailing space. */
494 while(isspace((int) *fen_char)){
497 if(*fen_char != '\0'){
503 fprintf(GlobalState.logfile,"Illegal FEN string %s at %s",fen,fen_char);
504 if(try_to_parse_game){
505 fprintf(GlobalState.logfile," Attempting to parse the game, anyway.");
508 (void) free_board((void *)new_board);
511 putc('\n',GlobalState.logfile);
516 /* Set up a board structure for a new game.
517 * This involves placing the pieces in their initial positions,
518 * setting up castling and en-passant rights, and initialising
519 * the hash positions.
520 * If the fen argument is NULL then a completely new board is
521 * setup, otherwise the indicated FEN position is returned.
524 new_game_board(const char *fen)
526 static const Board initial_board =
528 { { OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF},
529 { OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF},
530 { OFF,OFF,W(ROOK),W(KNIGHT),W(BISHOP),W(QUEEN),
531 W(KING),W(BISHOP),W(KNIGHT),W(ROOK),OFF,OFF},
532 { OFF,OFF,W(PAWN),W(PAWN),W(PAWN),W(PAWN),
533 W(PAWN),W(PAWN),W(PAWN),W(PAWN),OFF,OFF},
534 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
535 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
536 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
537 { OFF,OFF,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,EMPTY,OFF,OFF},
538 { OFF,OFF,B(PAWN),B(PAWN),B(PAWN),B(PAWN),
539 B(PAWN),B(PAWN),B(PAWN),B(PAWN),OFF,OFF},
540 { OFF,OFF,B(ROOK),B(KNIGHT),B(BISHOP),B(QUEEN),
541 B(KING),B(BISHOP),B(KNIGHT),B(ROOK),OFF,OFF},
542 { OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF},
543 { OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF,OFF}
545 /* Who to move next. */
549 /* Castling rights. */
550 TRUE, TRUE, TRUE, TRUE,
551 /* Initial king positions. */
554 /* En Passant rights. */
556 /* Initial hash value. */
561 /* Iterate over the columns. */
565 new_board = new_fen_board(fen);
567 /* Guard against failure of new_fen_board as well as the
568 * normal game situation.
570 if((fen == NULL) || (new_board == NULL)){
571 /* Use the initial board setup. */
572 new_board = allocate_new_board();
573 *new_board = initial_board;
576 /* Generate the hash value for the initial position. */
577 for(col = FIRSTCOL; col <= LASTCOL; col++){
580 for(rank = FIRSTRANK; rank <= LASTRANK; rank++){
581 /* Find the basic components. */
582 Piece coloured_piece = new_board->board[
583 RankConvert(rank)][ColConvert(col)];
584 Piece piece = EXTRACT_PIECE(coloured_piece);
585 Colour colour = EXTRACT_COLOUR(coloured_piece);
587 if(coloured_piece != EMPTY){
588 new_board->hash_value ^= HashLookup(col,rank,piece,colour);
595 /* Print out the current occupant of the given square. */
597 print_square(Col col, Rank rank, const Board *board,FILE *outfp)
598 { short r = RankConvert(rank);
599 short c = ColConvert(col);
601 Piece coloured_piece = board->board[r][c];
602 switch((int) coloured_piece){
615 putc(coloured_piece_to_SAN_letter(coloured_piece),outfp);
624 fprintf(GlobalState.logfile,
625 "Attempt to print illegal square %c%c in print_square.\n",
631 /* Print out the contents of the given board. */
633 print_board(const Board *board,FILE *outfp)
637 for(rank = LASTRANK; rank >= FIRSTRANK; rank--){
638 for(col = FIRSTCOL; col <= LASTCOL; col++){
639 print_square(col,rank,board,outfp);
646 #if INCLUDE_UNUSED_FUNCTIONS
648 /* Check the consistency of the board. */
650 check_board(const Board *board,const char *where)
654 for(rank = LASTRANK; rank >= FIRSTRANK; rank--){
655 for(col = FIRSTCOL; col <= LASTCOL; col++){
656 short r = RankConvert(rank);
657 short c = ColConvert(col);
659 switch(board->board[r][c]){
675 fprintf(GlobalState.logfile,
676 "%s: Illegal square %c%c (%u %u) contains %d.\n",
677 where,col,rank,r,c,board->board[r][c]);
678 report_details(GlobalState.logfile);
687 /* Return the number of half moves that have been completed
691 half_moves_played(const Board *board)
693 int half_moves = 2*(board->move_number-1);
694 if(board->to_move == BLACK){
700 /* Implement move_details on the board.
701 * Return TRUE if the move is ok, FALSE otherwise.
702 * move_details is completed by the call to determine_move_details.
703 * Thereafter, it is safe to make the move on board.
706 apply_move(Colour colour,Move *move_details, Board *board)
707 { /* Assume success. */
710 if(determine_move_details(colour,move_details,board)){
711 Piece piece_to_move = move_details->piece_to_move;
713 if(GlobalState.output_format == EPD){
714 move_details->epd = (char *) MallocOrDie(FEN_SPACE);
715 build_basic_EPD_string(board,move_details->epd);
717 else if(GlobalState.output_format == SESSE_BIN){
718 build_BPFEN_string(board, &move_details->bpfen, &move_details->bpfen_len);
721 if(move_details->class != NULL_MOVE) {
722 make_move(move_details->class, move_details->from_col,move_details->from_rank,
723 move_details->to_col,move_details->to_rank,
724 piece_to_move,colour,board);
726 /* See if there are any subsiduary actions. */
727 switch(move_details->class){
730 case ENPASSANT_PAWN_MOVE:
731 /* Nothing more to do. */
733 case PAWN_MOVE_WITH_PROMOTION:
734 if(move_details->class == PAWN_MOVE_WITH_PROMOTION){
735 if(move_details->promoted_piece != EMPTY){
736 /* Now make the promotion. */
737 make_move(move_details->class, move_details->to_col,move_details->to_rank,
738 move_details->to_col,move_details->to_rank,
739 move_details->promoted_piece,colour,board);
746 case KINGSIDE_CASTLE:
747 /* Step the Rook to the left of the king. */
748 piece_to_move = ROOK;
749 make_move(move_details->class, LASTCOL,move_details->from_rank,move_details->to_col-1,
750 move_details->to_rank,
751 piece_to_move,colour,board);
753 case QUEENSIDE_CASTLE:
754 /* Step the Rook to the right of the king. */
755 piece_to_move = ROOK;
756 make_move(move_details->class, FIRSTCOL,move_details->from_rank,move_details->to_col+1,
757 move_details->to_rank,
758 piece_to_move,colour,board);
761 /* Nothing more to do. */
768 /* Determine whether or not this move gives check. */
770 move_details->check_status =
771 king_is_in_check(board,OPPOSITE_COLOUR(colour));
772 if(move_details->check_status == CHECK){
773 /* See whether it is CHECKMATE. */
774 if(king_is_in_checkmate(OPPOSITE_COLOUR(colour),board)){
775 move_details->check_status = CHECKMATE;
778 /* Handle the halfmove_clock. */
779 if(piece_to_move == PAWN ||
780 move_details->captured_piece != EMPTY) {
781 board->halfmove_clock = 0;
784 board->halfmove_clock++;
794 /* Play out the moves on the given board.
795 * game_details is updated with the final_ and cumulative_ hash
797 * Check move validity unless a NULL_MOVE has been found in this
801 play_moves(Game *game_details, Board *board, Move *moves, unsigned max_depth,
802 Boolean check_move_validity)
803 { Boolean game_ok = TRUE;
804 /* Ply number at which any error was found. */
806 /* Force a match if we aren't looking for positional variations. */
807 Boolean game_matches = GlobalState.positional_variations?FALSE:TRUE;
808 Move *next_move = moves;
809 /* Keep track of the final ECO match. */
810 EcoLog *eco_match = NULL;
812 /* Try the initial board position for a match.
813 * This is required because the game might have been set up
814 * from a FEN string, rather than being the normal starting
817 if(!game_matches && position_matches(board)){
819 if(GlobalState.add_position_match_comments) {
820 CommentList *comment = create_match_comment(next_move);
821 comment->next = game_details->prefix_comment;
822 game_details->prefix_comment = comment;
825 /* Keep going while the game is ok, and we have some more
826 * moves and we haven't exceeded the search depth without finding
829 while(game_ok && (next_move != NULL) &&
830 (game_matches || (board->move_number <= max_depth))){
831 if(*(next_move->move) != '\0'){
832 /* See if there are any variations associated with this move. */
833 if((next_move->Variants != NULL) && GlobalState.keep_variations){
834 game_matches |= apply_variations(game_details,board,
836 check_move_validity);
838 /* Now try the main move. */
839 if(next_move->class == NULL_MOVE) {
840 /* We might not be able to check the validity of
844 check_move_validity = FALSE;
847 if(check_move_validity) {
848 if(apply_move(board->to_move,next_move,board)){
849 /* Don't try for a positional match if we already have one. */
850 if(!game_matches && position_matches(board)){
852 if(GlobalState.add_position_match_comments) {
853 CommentList *comment = create_match_comment(next_move);
854 append_comments_to_move(next_move, comment);
857 /* Combine this hash value with the cumulative one. */
858 game_details->cumulative_hash_value += board->hash_value;
859 if(GlobalState.fuzzy_match_duplicates) {
860 int plies = 2 * board->move_number - 1;
861 /* Check who has just moved. */
862 if(board->to_move == BLACK) {
865 /* Consider remembering this hash value for fuzzy matches. */
866 if(GlobalState.fuzzy_match_depth == plies) {
868 game_details->fuzzy_duplicate_hash = board->hash_value;
871 board->to_move = OPPOSITE_COLOUR(board->to_move);
872 if(board->to_move == WHITE){
873 board->move_number++;
875 if(GlobalState.add_ECO && !GlobalState.parsing_ECO_file){
876 int half_moves = half_moves_played(board);
877 EcoLog *entry = eco_matches(
879 game_details->cumulative_hash_value,
882 /* Consider keeping the match.
883 * Could try to avoid spurious matches which become
884 * more likely with larger ECO files and
885 * the longer a game goes on.
886 * Could be mitigated partly by preferring
887 * an ECO line of exactly the same length as
888 * the current game line.
889 * Not currently implemented.
891 if(eco_match == NULL){
892 /* We don't have one yet. */
897 * This logic always prefers a longer match
898 * to a shorter, irrespective of whether
899 * either match is exact or not.
900 * This logic was followed in versions
901 * up to and including v13.8.
907 next_move = next_move->next;
910 print_error_context(GlobalState.logfile);
911 fprintf(GlobalState.logfile,
912 "Failed to make move %u%s %s in the game:\n",
914 (board->to_move == WHITE)?".":"...",
916 print_board(board,GlobalState.logfile);
917 report_details(GlobalState.logfile);
919 /* Work out where the error was. */
920 error_ply = 2 * board->move_number - 1;
921 /* Check who has just moved. */
922 if(board->to_move == BLACK) {
928 /* Go through the motions as if the move were checked. */
929 board->to_move = OPPOSITE_COLOUR(board->to_move);
930 if(board->to_move == WHITE){
931 board->move_number++;
933 next_move = next_move->next;
938 fprintf(GlobalState.logfile,
939 "Internal error: Empty move in play_moves.\n");
940 report_details(GlobalState.logfile);
942 /* Work out where the error was. */
943 error_ply = 2 * board->move_number - 1;
944 /* Check who has just moved. */
945 if(board->to_move == BLACK) {
951 if(eco_match != NULL){
952 /* Free any details of the old one. */
953 if(game_details->tags[ECO_TAG] != NULL){
954 (void) free((void *) game_details->tags[ECO_TAG]);
955 game_details->tags[ECO_TAG] = NULL;
957 if(game_details->tags[OPENING_TAG] != NULL){
958 (void) free((void *)game_details->tags[OPENING_TAG]);
959 game_details->tags[OPENING_TAG] = NULL;
961 if(game_details->tags[VARIATION_TAG] != NULL){
962 (void) free((void *)game_details->tags[VARIATION_TAG]);
963 game_details->tags[VARIATION_TAG] = NULL;
965 if(game_details->tags[SUB_VARIATION_TAG] != NULL){
966 (void) free((void *)game_details->tags[SUB_VARIATION_TAG]);
967 game_details->tags[SUB_VARIATION_TAG] = NULL;
969 /* Add in the new one. */
970 if(eco_match->ECO_tag != NULL){
971 game_details->tags[ECO_TAG] = copy_string(eco_match->ECO_tag);
973 if(eco_match->Opening_tag != NULL){
974 game_details->tags[OPENING_TAG] = copy_string(eco_match->Opening_tag);
976 if(eco_match->Variation_tag != NULL){
977 game_details->tags[VARIATION_TAG] =
978 copy_string(eco_match->Variation_tag);
980 if(eco_match->Sub_Variation_tag != NULL){
981 game_details->tags[SUB_VARIATION_TAG] =
982 copy_string(eco_match->Sub_Variation_tag);
986 /* Fill in the hash value of the final position reached. */
987 game_details->final_hash_value = board->hash_value;
988 game_details->moves_ok = game_ok;
989 game_details->error_ply = error_ply;
991 /* Decide whether to keep it anyway. */
992 if(GlobalState.keep_broken_games) {
994 else if(GlobalState.positional_variations){
997 /* Only return a match if it genuinely matched a variation
998 * in which we were interested.
1000 /* We can't have found a genuine match. */
1001 game_matches = FALSE;
1004 return game_matches;
1007 /* Play out the moves of an ECO line on the given board.
1008 * game_details is updated with the final_ and cumulative_ hash
1013 play_eco_moves(Game *game_details, Board *board,Move *moves)
1014 { Boolean game_ok = TRUE;
1015 /* Ply number at which any error was found. */
1017 Move *next_move = moves;
1019 /* Keep going while the game is ok, and we have some more
1020 * moves and we haven't exceeded the search depth without finding
1023 while(game_ok && (next_move != NULL)){
1024 if(*(next_move->move) != '\0'){
1025 /* Ignore variations. */
1026 if(apply_move(board->to_move,next_move,board)){
1027 /* Combine this hash value to the cumulative one. */
1028 game_details->cumulative_hash_value += board->hash_value;
1029 board->to_move = OPPOSITE_COLOUR(board->to_move);
1030 if(board->to_move == WHITE){
1031 board->move_number++;
1033 next_move = next_move->next;
1036 print_error_context(GlobalState.logfile);
1037 fprintf(GlobalState.logfile,
1038 "Failed to make move %u%s %s in the game:\n",
1040 (board->to_move == WHITE)?".":"...",
1042 print_board(board,GlobalState.logfile);
1043 report_details(GlobalState.logfile);
1045 /* Work out where the error was. */
1046 error_ply = 2 * board->move_number - 1;
1047 /* Check who has just moved. */
1048 if(board->to_move == BLACK) {
1054 /* An empty move. */
1055 fprintf(GlobalState.logfile,
1056 "Internal error: Empty move in play_eco_moves.\n");
1057 report_details(GlobalState.logfile);
1059 /* Work out where the error was. */
1060 error_ply = 2 * board->move_number - 1;
1061 /* Check who has just moved. */
1062 if(board->to_move == BLACK) {
1067 /* Fill in the hash value of the final position reached. */
1068 game_details->final_hash_value = board->hash_value;
1069 game_details->moves_ok = game_ok;
1070 game_details->error_ply = error_ply;
1073 /* Play out a variation.
1074 * Check move validity unless a NULL_MOVE has been found in this
1076 * Return TRUE if the variation matches a position that
1077 * we are looking for.
1080 apply_variations(const Game *game_details,const Board *board,Variation *variation,
1081 Boolean check_move_validity)
1082 { /* Force a match if we aren't looking for positional variations. */
1083 Boolean variation_matches = GlobalState.positional_variations?FALSE:TRUE;
1084 /* Allocate space for the copies.
1085 * Allocation is done, rather than relying on local copies in the body
1086 * of the loop because the recursive nature of this function has
1087 * resulted in stack overflow on the PC version.
1089 Game *copy_game = (Game *) MallocOrDie(sizeof(*copy_game));
1090 Board *copy_board = allocate_new_board();
1092 while(variation != NULL){
1093 /* Work on the copies. */
1094 *copy_game = *game_details;
1095 *copy_board = *board;
1097 /* We only need one variation to match to declare a match.
1098 * Play out the variation to its full depth, because we
1099 * will want the full move information if the main line
1102 variation_matches |= play_moves(copy_game,copy_board,variation->moves,
1103 DEFAULT_POSITIONAL_DEPTH,
1104 check_move_validity);
1105 variation = variation->next;
1107 (void) free((void *)copy_game);
1108 (void) free_board((void *)copy_board);
1109 return variation_matches;
1112 /* game_details contains a complete move score.
1113 * Try to apply each move on a new board.
1114 * Store in plycount the number of ply played.
1115 * Return TRUE if the game matches a variation that we are
1119 apply_move_list(Game *game_details,unsigned *plycount)
1120 { Move *moves = game_details->moves;
1121 Board *board = new_game_board(game_details->tags[FEN_TAG]);
1122 Boolean game_matches;
1123 /* Set the default search depth. */
1124 unsigned max_depth = GlobalState.depth_of_positional_search;
1126 /* Ensure that we have a sensible search depth. */
1128 /* No positional variations specified. */
1129 max_depth = DEFAULT_POSITIONAL_DEPTH;
1132 /* Start off the cumulative hash value. */
1133 game_details->cumulative_hash_value = 0;
1135 /* Play through the moves and see if we have a match.
1136 * Check move validity.
1138 game_matches = play_moves(game_details,board,moves,max_depth,TRUE);
1140 game_details->moves_checked = TRUE;
1142 /* Record how long the game was. */
1143 if(board->to_move == BLACK){
1144 *plycount = 2 * board->move_number - 1;
1147 /* This move number hasn't been played. */
1148 *plycount = 2 * (board->move_number - 1);
1152 game_matches = check_for_only_stalemate(board, moves);
1155 (void) free_board((void *)board);
1156 return game_matches;
1159 /* game_details contains a complete move score.
1160 * Try to apply each move on a new board.
1161 * Store in number_of_moves the length of the game.
1162 * Return TRUE if the game is ok.
1165 apply_eco_move_list(Game *game_details,unsigned *number_of_half_moves)
1166 { Move *moves = game_details->moves;
1167 Board *board = new_game_board(game_details->tags[FEN_TAG]);
1169 /* Start off the cumulative hash value. */
1170 game_details->cumulative_hash_value = 0;
1171 play_eco_moves(game_details,board,moves);
1172 game_details->moves_checked = TRUE;
1173 /* Record how long the game was. */
1174 *number_of_half_moves = half_moves_played(board);
1175 (void) free_board((void *)board);
1176 return game_details->moves_ok;
1180 /* Return the string associated with the given piece. */
1182 piece_str(Piece piece)
1184 if(piece < NUM_PIECE_VALUES){
1185 return output_piece_characters[piece];
1192 /* Rewrite move_details->move according to the details held
1193 * within the structure and the current state of the board.
1196 rewrite_SAN_string(Colour colour,Move *move_details, Board *board)
1197 { Boolean Ok = TRUE;
1199 if(move_details == NULL){
1200 /* Shouldn't happen. */
1201 fprintf(GlobalState.logfile,
1202 "Internal error: NULL move details in rewrite_SAN_string.\n");
1205 else if(move_details->move[0] == '\0'){
1206 /* Shouldn't happen. */
1207 fprintf(GlobalState.logfile,"Empty move in rewrite_SAN_string.\n");
1211 const unsigned char *move = move_details->move;
1212 MoveClass class = move_details->class;
1213 MovePair *move_list = NULL;
1214 Col to_col = move_details->to_col;
1215 Rank to_rank = move_details->to_rank;
1216 unsigned char new_move_str[MAX_MOVE_LEN+1] = "";
1220 case ENPASSANT_PAWN_MOVE:
1221 case PAWN_MOVE_WITH_PROMOTION:
1222 move_list = find_pawn_moves(move_details->from_col,
1227 switch(move_details->piece_to_move){
1229 move_list = find_king_moves(to_col,to_rank,colour,board);
1232 move_list = find_queen_moves(to_col,to_rank,colour,board);
1235 move_list = find_rook_moves(to_col,to_rank,colour,board);
1238 move_list = find_knight_moves(to_col,to_rank,colour,board);
1241 move_list = find_bishop_moves(to_col,to_rank,colour,board);
1244 fprintf(GlobalState.logfile,"Unknown piece move %s\n",move);
1249 case KINGSIDE_CASTLE:
1250 case QUEENSIDE_CASTLE:
1251 /* No move list to prepare. */
1254 /* No move list to prepare. */
1258 fprintf(GlobalState.logfile,
1259 "Unknown move class in rewrite_SAN_string(%d).\n",
1260 move_details->class);
1264 if(move_list != NULL){
1265 move_list = exclude_checks(move_details->piece_to_move,colour,
1268 if((move_list == NULL) && (class != KINGSIDE_CASTLE) &&
1269 (class != QUEENSIDE_CASTLE) && (class != NULL_MOVE)){
1272 /* We should now have enough information in move_details to compose a
1276 size_t new_move_index = 0;
1280 case ENPASSANT_PAWN_MOVE:
1281 case PAWN_MOVE_WITH_PROMOTION:
1282 /* See if we need to give the source column. */
1283 if(move_details->captured_piece != EMPTY){
1284 new_move_str[new_move_index] = move_details->from_col;
1286 new_move_str[new_move_index] = 'x';
1289 else if(move_list->next != NULL){
1290 new_move_str[new_move_index] = move_details->from_col;
1293 /* Add in the destination. */
1294 new_move_str[new_move_index] = to_col;
1296 new_move_str[new_move_index] = to_rank;
1298 if(class == PAWN_MOVE_WITH_PROMOTION){
1299 const char *promoted_piece =
1300 piece_str(move_details->promoted_piece);
1301 new_move_str[new_move_index] = '=';
1303 strcpy((char *) &new_move_str[new_move_index],
1305 new_move_index += strlen(promoted_piece);
1307 new_move_str[new_move_index] = '\0';
1310 { const char *piece = piece_str(move_details->piece_to_move);
1311 strcpy((char *) &new_move_str[0],piece);
1312 new_move_index += strlen(piece);
1313 /* Check for the need to disambiguate. */
1314 if(move_list->next != NULL){
1315 /* It is necessary. Count how many times
1316 * the from_ col and rank occur in the list
1317 * of possibles in order to determine which to use
1320 int col_times = 0, rank_times = 0;
1322 Col from_col = move_details->from_col;
1323 Rank from_rank = move_details->from_rank;
1325 for(possible = move_list; possible != NULL;
1326 possible = possible->next){
1327 if(possible->from_col == from_col){
1330 if(possible->from_rank == from_rank){
1336 new_move_str[new_move_index] = from_col;
1339 else if(rank_times == 1){
1341 new_move_str[new_move_index] = from_rank;
1346 new_move_str[new_move_index] = from_col;
1348 new_move_str[new_move_index] = from_rank;
1352 /* See if a capture symbol is needed. */
1353 if(move_details->captured_piece != EMPTY){
1354 new_move_str[new_move_index] = 'x';
1357 /* Add in the destination. */
1358 new_move_str[new_move_index] = to_col;
1360 new_move_str[new_move_index] = to_rank;
1362 new_move_str[new_move_index] = '\0';
1365 case KINGSIDE_CASTLE:
1366 strcpy((char *) new_move_str,"O-O");
1368 case QUEENSIDE_CASTLE:
1369 strcpy((char *) new_move_str,"O-O-O");
1372 strcpy((char *) new_move_str, (char *) NULL_MOVE_STRING);
1380 if(move_details->check_status != NOCHECK){
1381 if(move_details->check_status == CHECK){
1382 /* It isn't mate. */
1383 strcat((char *) new_move_str,"+");
1386 if(GlobalState.output_format == CM){
1387 strcat((char *) new_move_str,"++");
1390 strcat((char *) new_move_str,"#");
1395 /* Update the move_details structure with the new string. */
1396 strcpy((char *) move_details->move,
1397 (const char *) new_move_str);
1399 if(move_list != NULL){
1400 free_move_pair_list(move_list);
1406 /* Rewrite move_details->move and apply the move to board.
1407 * Return TRUE if the move is ok, FALSE otherwise.
1410 rewrite_move(Colour colour,Move *move_details, Board *board, Boolean null_move_found)
1411 { /* Assume success. */
1414 if(rewrite_SAN_string(colour,move_details,board)){
1415 Piece piece_to_move = move_details->piece_to_move;
1417 if(move_details->class != NULL_MOVE) {
1418 make_move(move_details->class, move_details->from_col,move_details->from_rank,
1419 move_details->to_col,move_details->to_rank,
1420 piece_to_move,colour,board);
1423 null_move_found = TRUE;
1425 /* See if there are any subsiduary actions. */
1426 switch(move_details->class){
1429 case ENPASSANT_PAWN_MOVE:
1430 /* Nothing more to do. */
1432 case PAWN_MOVE_WITH_PROMOTION:
1433 if(move_details->class == PAWN_MOVE_WITH_PROMOTION){
1434 if(move_details->promoted_piece != EMPTY){
1435 /* @@@ Do promoted moves have '+' properly appended? */
1436 /* Now make the promotion. */
1437 make_move(move_details->class, move_details->to_col,move_details->to_rank,
1438 move_details->to_col,move_details->to_rank,
1439 move_details->promoted_piece,colour,board);
1446 case KINGSIDE_CASTLE:
1447 /* Step the Rook to the left of the king. */
1448 piece_to_move = ROOK;
1449 make_move(move_details->class, LASTCOL,move_details->from_rank,move_details->to_col-1,
1450 move_details->to_rank,
1451 piece_to_move,colour,board);
1453 case QUEENSIDE_CASTLE:
1454 /* Step the Rook to the right of the king. */
1455 piece_to_move = ROOK;
1456 make_move(move_details->class, FIRSTCOL,move_details->from_rank,move_details->to_col+1,
1457 move_details->to_rank,
1458 piece_to_move,colour,board);
1475 /* Rewrite the list of moves by playing through the game. */
1477 rewrite_moves(Board *board, Move *moves, Boolean null_move_found)
1478 { Boolean game_ok = TRUE;
1479 Move *move_details = moves;
1481 while(game_ok && (move_details != NULL)){
1482 if(*(move_details->move) != '\0'){
1483 /* See if there are any variations associated with this move. */
1484 if((move_details->Variants != NULL) &&
1485 GlobalState.keep_variations &&
1486 !rewrite_variations(board,move_details->Variants, null_move_found)){
1487 /* Something wrong with the variations. */
1490 /* @@@ There was a else-if here; not sure why?! */
1491 if(move_details->class == NULL_MOVE) {
1492 null_move_found = TRUE;
1494 if(rewrite_move(board->to_move,move_details,board, null_move_found)){
1495 board->to_move = OPPOSITE_COLOUR(board->to_move);
1497 if(GlobalState.output_evaluation) {
1498 /* Append an evaluation of the new state of the board
1499 * with the move having been played.
1501 append_evaluation(move_details, board);
1504 if(GlobalState.add_FEN_comments) {
1505 /* Append a FEN comment with the new state of the board
1506 * with the move having been played.
1508 append_FEN_comment(move_details, board);
1511 move_details = move_details->next;
1512 if(board->to_move == WHITE){
1513 board->move_number++;
1517 /* Broken games that are being kept have probably already been
1518 * reported, so don't repeat the error.
1520 if(!GlobalState.keep_broken_games) {
1521 fprintf(GlobalState.logfile,
1522 "Failed to rewrite move %u%s %s in the game:\n",
1524 (board->to_move == WHITE)?".":"...",
1525 move_details->move);
1526 report_details(GlobalState.logfile);
1527 print_board(board,GlobalState.logfile);
1533 /* An empty move. */
1534 fprintf(GlobalState.logfile,
1535 "Internal error: Empty move in rewrite_moves.\n");
1536 report_details(GlobalState.logfile);
1540 if(!game_ok && GlobalState.keep_broken_games && move_details != NULL) {
1541 /* Try to place the remaining moves into a comment. */
1542 CommentList *comment = (CommentList* ) MallocOrDie(sizeof(*comment));
1543 /* Break the link from the previous move. */
1545 StringList *commented_move_list = NULL;
1547 /* Find the move before the current one. */
1549 while(prev != NULL && prev->next != move_details) {
1555 while(move_details != NULL) {
1556 commented_move_list = save_string_list_item(commented_move_list, (char *) move_details->move);
1557 if(move_details->next == NULL && prev != NULL) {
1558 /* Pass the terminating result to prev. */
1559 prev->terminating_result = move_details->terminating_result;
1560 move_details->terminating_result = NULL;
1561 move_details = NULL;
1564 move_details = move_details->next;
1567 comment->Comment = commented_move_list;
1568 comment->next = NULL;
1571 append_comments_to_move(prev, comment);
1577 /* Rewrite the list of variations.
1578 * Return TRUE if the variation are ok. a position that
1581 rewrite_variations(const Board *board,Variation *variation, Boolean null_move_found)
1582 { Board *copy_board = allocate_new_board();
1583 Boolean variations_ok = TRUE;
1585 while((variation != NULL) && variations_ok){
1586 /* Work on the copy. */
1587 *copy_board = *board;
1589 variations_ok = rewrite_moves(copy_board,variation->moves, null_move_found);
1590 variation = variation->next;
1592 (void) free_board((void *)copy_board);
1593 return variations_ok;
1596 /* moves contains a complete game score.
1597 * Try to rewrite each move into SAN as it is played on a new board.
1598 * Return the final Board position if the game was rewritten alright,
1602 rewrite_game(Game *current_game)
1604 const char *fen = current_game->tags[FEN_TAG];
1605 Board *board = new_game_board(fen);
1608 /* No null-move found at the start of the game. */
1609 game_ok = rewrite_moves(board,current_game->moves,FALSE);
1612 else if(GlobalState.keep_broken_games) {
1615 (void) free_board((void *)board);
1622 /* Define a table to hold the positional hash codes of interest. */
1624 static HashLog *codes_of_interest[MAX_CODE];
1626 /* move_details is a variation in which we are interested.
1627 * Generate and store the hash value in codes_of_interest.
1630 store_hash_value(Move *move_details,const char *fen)
1631 { Move *move = move_details;
1632 Board *board = new_game_board(fen);
1635 while((move != NULL) && Ok){
1636 /* Reset print_move number if a variation was printed. */
1637 if(*(move->move) == '\0'){
1638 /* A comment node, not a proper move. */
1641 else if(apply_move(board->to_move,move,board)){
1642 board->to_move = OPPOSITE_COLOUR(board->to_move);
1644 if(board->to_move == WHITE){
1645 board->move_number++;
1649 print_error_context(GlobalState.logfile);
1650 fprintf(GlobalState.logfile,"Failed to make move %u%s %s\n",
1652 (board->to_move == WHITE)?".":"...",
1662 HashLog *entry = (HashLog *)MallocOrDie(sizeof(*entry));
1663 unsigned ix = board->hash_value % MAX_CODE;
1665 /* We don't include the cumulative hash value as the sequence
1666 * of moves to reach this position is not important.
1668 entry->cumulative_hash_value = 0;
1669 entry->final_hash_value = board->hash_value;
1670 /* Link it into the head at this index. */
1671 entry->next = codes_of_interest[ix];
1672 codes_of_interest[ix] = entry;
1674 (void) free_board((void *)board);
1677 /* Does the current board match a position of interest.
1678 * Look in codes_of_interest for current_hash_value.
1681 position_matches(const Board *board)
1683 HashCode current_hash_value = board->hash_value;
1684 unsigned ix = current_hash_value % MAX_CODE;
1685 Boolean found = FALSE;
1688 for(entry = codes_of_interest[ix]; !found && (entry != NULL);
1689 entry = entry->next){
1690 /* We can test against just the position value. */
1691 if(entry->final_hash_value == current_hash_value){
1696 const char *matching_pattern = matchBoard(board);
1697 found = matching_pattern != NULL;
1702 /* Build a basic EPD string from the given board. */
1704 build_basic_EPD_string(const Board *board,char *epd)
1707 Boolean castling_allowed = FALSE;
1708 for(rank = LASTRANK; rank >= FIRSTRANK; rank--){
1710 int consecutive_spaces = 0;
1711 for(col = FIRSTCOL; col <= LASTCOL; col++){
1712 int coloured_piece = board->board[RankConvert(rank)]
1714 if(coloured_piece != EMPTY){
1715 if(consecutive_spaces > 0){
1716 epd[ix] = '0'+consecutive_spaces;
1718 consecutive_spaces = 0;
1720 epd[ix] = coloured_piece_to_SAN_letter(coloured_piece);
1724 consecutive_spaces++;
1727 if(consecutive_spaces > 0){
1728 epd[ix] = '0'+consecutive_spaces;
1731 /* Terminate the row. */
1732 if(rank != FIRSTRANK){
1733 epd[ix] = '/'; ix++;
1736 epd[ix] = ' '; ix++;
1738 epd[ix] = board->to_move == WHITE? 'w' : 'b'; ix++;
1739 epd[ix] = ' '; ix++;
1741 /* Castling details. */
1742 if(board->WKingCastle){
1743 epd[ix] = 'K'; ix++;
1744 castling_allowed = TRUE;
1746 if(board->WQueenCastle){
1747 epd[ix] = 'Q'; ix++;
1748 castling_allowed = TRUE;
1750 if(board->BKingCastle){
1751 epd[ix] = 'k'; ix++;
1752 castling_allowed = TRUE;
1754 if(board->BQueenCastle){
1755 epd[ix] = 'q'; ix++;
1756 castling_allowed = TRUE;
1758 if(!castling_allowed){
1759 /* There are no castling rights. */
1763 epd[ix] = ' '; ix++;
1766 if(board->EnPassant){
1767 epd[ix] = board->ep_col; ix++;
1768 epd[ix] = board->ep_rank; ix++;
1771 epd[ix] = '-'; ix++;
1776 /* Build a FEN string from the given board. */
1778 build_FEN_string(const Board *board,char *fen)
1781 int full_move_number =
1782 board->to_move == BLACK ? board->move_number : (board->move_number + 1);
1784 build_basic_EPD_string(board,fen);
1785 /* Append the (pseudo) half move count and the full move count. */
1787 fen[ix] = ' '; ix++;
1789 /* Half moves since the last capture or pawn move. */
1790 sprintf(&fen[ix], "%u", board->halfmove_clock);
1792 fen[ix] = ' '; ix++;
1794 /* The full move number. */
1795 sprintf(&fen[ix],"%u", full_move_number);
1799 build_BPFEN_string(const Board *board,char **bpfen, int* bpfen_len)
1800 { static int bits[256]; /* Max six bits per piece, 32 blanks, and then some. */
1804 for(rank = LASTRANK; rank >= FIRSTRANK; rank--){
1806 for(col = FIRSTCOL; col <= LASTCOL; col++){
1807 int coloured_piece = board->board[RankConvert(rank)]
1809 if(coloured_piece == EMPTY){
1810 bits[bit_pos++] = 0;
1813 bits[bit_pos++] = 1;
1814 bits[bit_pos++] = (EXTRACT_COLOUR(coloured_piece) == WHITE) ? 1 : 0;
1815 switch(EXTRACT_PIECE(coloured_piece)) {
1817 bits[bit_pos++] = 0;
1820 bits[bit_pos++] = 1;
1821 bits[bit_pos++] = 0;
1822 bits[bit_pos++] = 0;
1825 bits[bit_pos++] = 1;
1826 bits[bit_pos++] = 0;
1827 bits[bit_pos++] = 1;
1830 bits[bit_pos++] = 1;
1831 bits[bit_pos++] = 1;
1832 bits[bit_pos++] = 1;
1833 bits[bit_pos++] = 0;
1836 bits[bit_pos++] = 1;
1837 bits[bit_pos++] = 1;
1838 bits[bit_pos++] = 1;
1839 bits[bit_pos++] = 1;
1840 bits[bit_pos++] = 0;
1843 bits[bit_pos++] = 1;
1844 bits[bit_pos++] = 1;
1845 bits[bit_pos++] = 1;
1846 bits[bit_pos++] = 1;
1847 bits[bit_pos++] = 1;
1853 while ((bit_pos % 8) != 0) {
1854 bits[bit_pos++] = 0;
1857 /* Non-board information. */
1858 bits[bit_pos++] = (board->to_move == WHITE) ? 0 : 1;
1859 bits[bit_pos++] = board->WKingCastle ? 1 : 0;
1860 bits[bit_pos++] = board->WQueenCastle ? 1 : 0;
1861 bits[bit_pos++] = board->BKingCastle ? 1 : 0;
1862 bits[bit_pos++] = board->BQueenCastle ? 1 : 0;
1864 if(board->EnPassant){
1865 int col_index = board->ep_col - COLBASE;
1866 bits[bit_pos++] = 1;
1867 bits[bit_pos++] = (col_index >> 2) & 1;
1868 bits[bit_pos++] = (col_index >> 1) & 1;
1869 bits[bit_pos++] = col_index & 1;
1872 bits[bit_pos++] = 0;
1875 /* Pad non-board. */
1876 while ((bit_pos % 8) != 0) {
1877 bits[bit_pos++] = 0;
1880 /* Convert from bits to binary form. */
1881 *bpfen = (char *)malloc(bit_pos / 8);
1882 *bpfen_len = bit_pos / 8;
1884 for (ix = 0; ix < bit_pos / 8; ++ix) {
1885 unsigned char ch = 0;
1888 for (jx = 0; jx < 8; ++jx) {
1889 ch |= (bits[ix * 8 + jx] << jx);
1896 /* Append to move_details a FEN comment of the board.
1897 * The board state is immediately following application of the
1901 append_FEN_comment(Move *move_details, const Board *board)
1903 char *FEN_comment = MallocOrDie(FEN_SPACE);
1904 CommentList *comment = (CommentList* ) MallocOrDie(sizeof(*comment));
1905 StringList *current_comment = save_string_list_item(NULL, FEN_comment);
1907 build_FEN_string(board, FEN_comment);
1908 comment->Comment = current_comment;
1909 comment->next = NULL;
1910 append_comments_to_move(move_details, comment);
1913 /* Append to move_details an evaluation value for board.
1914 * The board state is immediately following application of the
1918 append_evaluation(Move *move_details, const Board *board)
1920 CommentList *comment = (CommentList* ) MallocOrDie(sizeof(*comment));
1921 /* Space template for the value.
1922 * @@@ There is a buffer-overflow risk here if the evaluation value
1925 const char valueSpace[] = "-012456789.00";
1926 char *evaluation = (char *) MallocOrDie(sizeof(valueSpace));
1927 StringList *current_comment;
1929 double value = evaluate(board);
1931 /* @@@ Overflow possible here if the value is too big to fit. */
1932 sprintf(evaluation,"%.2f", value);
1933 if(strlen(evaluation) > strlen(valueSpace)) {
1934 fprintf(GlobalState.logfile,
1935 "Overflow in evaluation space in append_evaluation()\n");
1939 current_comment = save_string_list_item(NULL, evaluation);
1940 comment->Comment = current_comment;
1941 comment->next = NULL;
1942 append_comments_to_move(move_details, comment);
1945 /* Append to move_details a comment indicating that this
1946 * move resulted in a positional match.
1949 create_match_comment(Move *move_details)
1951 /* The comment string. */
1952 char *match_comment = copy_string(GlobalState.position_match_comment);
1953 StringList *current_comment = save_string_list_item(NULL, match_comment);
1954 CommentList *comment = (CommentList* ) MallocOrDie(sizeof(*comment));
1956 comment->Comment = current_comment;
1957 comment->next = NULL;
1960 /* Return an evaluation of board. */
1962 evaluate(const Board *board)
1964 return shannonEvaluation(board);
1967 /* Return an evaluation of board based on
1968 * Claude Shannon's technique.
1971 shannonEvaluation(const Board *board)
1973 MovePair *white_moves, *black_moves;
1974 int whiteMoveCount = 0, blackMoveCount = 0;
1975 int whitePieceCount = 0, blackPieceCount = 0;
1976 double shannonValue = 0.0;
1981 /* Determine the mobilities. */
1982 white_moves = find_all_moves(board, WHITE);
1983 if(white_moves != NULL){
1985 for(m = white_moves; m != NULL; m = m->next) {
1988 free_move_pair_list(white_moves);
1991 black_moves = find_all_moves(board, BLACK);
1992 if(black_moves != NULL){
1994 for(m = black_moves; m != NULL; m = m->next) {
1997 free_move_pair_list(black_moves);
2001 /* Pick up each piece of the required colour. */
2002 for(rank = LASTRANK; rank >= FIRSTRANK; rank--){
2003 short r = RankConvert(rank);
2004 for(col = FIRSTCOL; col <= LASTCOL; col++){
2005 short c = ColConvert(col);
2007 Piece occupant = board->board[r][c];
2008 if(occupant != EMPTY) {
2009 /* This square is occupied by a piece of the required colour. */
2010 Piece piece = EXTRACT_PIECE(occupant);
2029 fprintf(GlobalState.logfile,
2030 "Internal error: unknown piece %d in append_evaluation().\n",
2033 if(EXTRACT_COLOUR(occupant) == WHITE) {
2034 whitePieceCount += pieceValue;
2037 blackPieceCount += pieceValue;
2043 shannonValue = (whitePieceCount - blackPieceCount) +
2044 (whiteMoveCount - blackMoveCount) * 0.1;
2045 return shannonValue;