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.
906 next_move->eco = eco_match;
908 next_move = next_move->next;
911 print_error_context(GlobalState.logfile);
912 fprintf(GlobalState.logfile,
913 "Failed to make move %u%s %s in the game:\n",
915 (board->to_move == WHITE)?".":"...",
917 print_board(board,GlobalState.logfile);
918 report_details(GlobalState.logfile);
920 /* Work out where the error was. */
921 error_ply = 2 * board->move_number - 1;
922 /* Check who has just moved. */
923 if(board->to_move == BLACK) {
929 /* Go through the motions as if the move were checked. */
930 board->to_move = OPPOSITE_COLOUR(board->to_move);
931 if(board->to_move == WHITE){
932 board->move_number++;
934 next_move = next_move->next;
939 fprintf(GlobalState.logfile,
940 "Internal error: Empty move in play_moves.\n");
941 report_details(GlobalState.logfile);
943 /* Work out where the error was. */
944 error_ply = 2 * board->move_number - 1;
945 /* Check who has just moved. */
946 if(board->to_move == BLACK) {
952 if(eco_match != NULL){
953 /* Free any details of the old one. */
954 if(game_details->tags[ECO_TAG] != NULL){
955 (void) free((void *) game_details->tags[ECO_TAG]);
956 game_details->tags[ECO_TAG] = NULL;
958 if(game_details->tags[OPENING_TAG] != NULL){
959 (void) free((void *)game_details->tags[OPENING_TAG]);
960 game_details->tags[OPENING_TAG] = NULL;
962 if(game_details->tags[VARIATION_TAG] != NULL){
963 (void) free((void *)game_details->tags[VARIATION_TAG]);
964 game_details->tags[VARIATION_TAG] = NULL;
966 if(game_details->tags[SUB_VARIATION_TAG] != NULL){
967 (void) free((void *)game_details->tags[SUB_VARIATION_TAG]);
968 game_details->tags[SUB_VARIATION_TAG] = NULL;
970 /* Add in the new one. */
971 if(eco_match->ECO_tag != NULL){
972 game_details->tags[ECO_TAG] = copy_string(eco_match->ECO_tag);
974 if(eco_match->Opening_tag != NULL){
975 game_details->tags[OPENING_TAG] = copy_string(eco_match->Opening_tag);
977 if(eco_match->Variation_tag != NULL){
978 game_details->tags[VARIATION_TAG] =
979 copy_string(eco_match->Variation_tag);
981 if(eco_match->Sub_Variation_tag != NULL){
982 game_details->tags[SUB_VARIATION_TAG] =
983 copy_string(eco_match->Sub_Variation_tag);
987 /* Fill in the hash value of the final position reached. */
988 game_details->final_hash_value = board->hash_value;
989 game_details->moves_ok = game_ok;
990 game_details->error_ply = error_ply;
992 /* Decide whether to keep it anyway. */
993 if(GlobalState.keep_broken_games) {
995 else if(GlobalState.positional_variations){
998 /* Only return a match if it genuinely matched a variation
999 * in which we were interested.
1001 /* We can't have found a genuine match. */
1002 game_matches = FALSE;
1005 return game_matches;
1008 /* Play out the moves of an ECO line on the given board.
1009 * game_details is updated with the final_ and cumulative_ hash
1014 play_eco_moves(Game *game_details, Board *board,Move *moves)
1015 { Boolean game_ok = TRUE;
1016 /* Ply number at which any error was found. */
1018 Move *next_move = moves;
1020 /* Keep going while the game is ok, and we have some more
1021 * moves and we haven't exceeded the search depth without finding
1024 while(game_ok && (next_move != NULL)){
1025 if(*(next_move->move) != '\0'){
1026 /* Ignore variations. */
1027 if(apply_move(board->to_move,next_move,board)){
1028 /* Combine this hash value to the cumulative one. */
1029 game_details->cumulative_hash_value += board->hash_value;
1030 board->to_move = OPPOSITE_COLOUR(board->to_move);
1031 if(board->to_move == WHITE){
1032 board->move_number++;
1034 next_move = next_move->next;
1037 print_error_context(GlobalState.logfile);
1038 fprintf(GlobalState.logfile,
1039 "Failed to make move %u%s %s in the game:\n",
1041 (board->to_move == WHITE)?".":"...",
1043 print_board(board,GlobalState.logfile);
1044 report_details(GlobalState.logfile);
1046 /* Work out where the error was. */
1047 error_ply = 2 * board->move_number - 1;
1048 /* Check who has just moved. */
1049 if(board->to_move == BLACK) {
1055 /* An empty move. */
1056 fprintf(GlobalState.logfile,
1057 "Internal error: Empty move in play_eco_moves.\n");
1058 report_details(GlobalState.logfile);
1060 /* Work out where the error was. */
1061 error_ply = 2 * board->move_number - 1;
1062 /* Check who has just moved. */
1063 if(board->to_move == BLACK) {
1068 /* Fill in the hash value of the final position reached. */
1069 game_details->final_hash_value = board->hash_value;
1070 game_details->moves_ok = game_ok;
1071 game_details->error_ply = error_ply;
1074 /* Play out a variation.
1075 * Check move validity unless a NULL_MOVE has been found in this
1077 * Return TRUE if the variation matches a position that
1078 * we are looking for.
1081 apply_variations(const Game *game_details,const Board *board,Variation *variation,
1082 Boolean check_move_validity)
1083 { /* Force a match if we aren't looking for positional variations. */
1084 Boolean variation_matches = GlobalState.positional_variations?FALSE:TRUE;
1085 /* Allocate space for the copies.
1086 * Allocation is done, rather than relying on local copies in the body
1087 * of the loop because the recursive nature of this function has
1088 * resulted in stack overflow on the PC version.
1090 Game *copy_game = (Game *) MallocOrDie(sizeof(*copy_game));
1091 Board *copy_board = allocate_new_board();
1093 while(variation != NULL){
1094 /* Work on the copies. */
1095 *copy_game = *game_details;
1096 *copy_board = *board;
1098 /* We only need one variation to match to declare a match.
1099 * Play out the variation to its full depth, because we
1100 * will want the full move information if the main line
1103 variation_matches |= play_moves(copy_game,copy_board,variation->moves,
1104 DEFAULT_POSITIONAL_DEPTH,
1105 check_move_validity);
1106 variation = variation->next;
1108 (void) free((void *)copy_game);
1109 (void) free_board((void *)copy_board);
1110 return variation_matches;
1113 /* game_details contains a complete move score.
1114 * Try to apply each move on a new board.
1115 * Store in plycount the number of ply played.
1116 * Return TRUE if the game matches a variation that we are
1120 apply_move_list(Game *game_details,unsigned *plycount)
1121 { Move *moves = game_details->moves;
1122 Board *board = new_game_board(game_details->tags[FEN_TAG]);
1123 Boolean game_matches;
1124 /* Set the default search depth. */
1125 unsigned max_depth = GlobalState.depth_of_positional_search;
1127 /* Ensure that we have a sensible search depth. */
1129 /* No positional variations specified. */
1130 max_depth = DEFAULT_POSITIONAL_DEPTH;
1133 /* Start off the cumulative hash value. */
1134 game_details->cumulative_hash_value = 0;
1136 /* Play through the moves and see if we have a match.
1137 * Check move validity.
1139 game_matches = play_moves(game_details,board,moves,max_depth,TRUE);
1141 game_details->moves_checked = TRUE;
1143 /* Record how long the game was. */
1144 if(board->to_move == BLACK){
1145 *plycount = 2 * board->move_number - 1;
1148 /* This move number hasn't been played. */
1149 *plycount = 2 * (board->move_number - 1);
1153 game_matches = check_for_only_stalemate(board, moves);
1156 (void) free_board((void *)board);
1157 return game_matches;
1160 /* game_details contains a complete move score.
1161 * Try to apply each move on a new board.
1162 * Store in number_of_moves the length of the game.
1163 * Return TRUE if the game is ok.
1166 apply_eco_move_list(Game *game_details,unsigned *number_of_half_moves)
1167 { Move *moves = game_details->moves;
1168 Board *board = new_game_board(game_details->tags[FEN_TAG]);
1170 /* Start off the cumulative hash value. */
1171 game_details->cumulative_hash_value = 0;
1172 play_eco_moves(game_details,board,moves);
1173 game_details->moves_checked = TRUE;
1174 /* Record how long the game was. */
1175 *number_of_half_moves = half_moves_played(board);
1176 (void) free_board((void *)board);
1177 return game_details->moves_ok;
1181 /* Return the string associated with the given piece. */
1183 piece_str(Piece piece)
1185 if(piece < NUM_PIECE_VALUES){
1186 return output_piece_characters[piece];
1193 /* Rewrite move_details->move according to the details held
1194 * within the structure and the current state of the board.
1197 rewrite_SAN_string(Colour colour,Move *move_details, Board *board)
1198 { Boolean Ok = TRUE;
1200 if(move_details == NULL){
1201 /* Shouldn't happen. */
1202 fprintf(GlobalState.logfile,
1203 "Internal error: NULL move details in rewrite_SAN_string.\n");
1206 else if(move_details->move[0] == '\0'){
1207 /* Shouldn't happen. */
1208 fprintf(GlobalState.logfile,"Empty move in rewrite_SAN_string.\n");
1212 const unsigned char *move = move_details->move;
1213 MoveClass class = move_details->class;
1214 MovePair *move_list = NULL;
1215 Col to_col = move_details->to_col;
1216 Rank to_rank = move_details->to_rank;
1217 unsigned char new_move_str[MAX_MOVE_LEN+1] = "";
1221 case ENPASSANT_PAWN_MOVE:
1222 case PAWN_MOVE_WITH_PROMOTION:
1223 move_list = find_pawn_moves(move_details->from_col,
1228 switch(move_details->piece_to_move){
1230 move_list = find_king_moves(to_col,to_rank,colour,board);
1233 move_list = find_queen_moves(to_col,to_rank,colour,board);
1236 move_list = find_rook_moves(to_col,to_rank,colour,board);
1239 move_list = find_knight_moves(to_col,to_rank,colour,board);
1242 move_list = find_bishop_moves(to_col,to_rank,colour,board);
1245 fprintf(GlobalState.logfile,"Unknown piece move %s\n",move);
1250 case KINGSIDE_CASTLE:
1251 case QUEENSIDE_CASTLE:
1252 /* No move list to prepare. */
1255 /* No move list to prepare. */
1259 fprintf(GlobalState.logfile,
1260 "Unknown move class in rewrite_SAN_string(%d).\n",
1261 move_details->class);
1265 if(move_list != NULL){
1266 move_list = exclude_checks(move_details->piece_to_move,colour,
1269 if((move_list == NULL) && (class != KINGSIDE_CASTLE) &&
1270 (class != QUEENSIDE_CASTLE) && (class != NULL_MOVE)){
1273 /* We should now have enough information in move_details to compose a
1277 size_t new_move_index = 0;
1281 case ENPASSANT_PAWN_MOVE:
1282 case PAWN_MOVE_WITH_PROMOTION:
1283 /* See if we need to give the source column. */
1284 if(move_details->captured_piece != EMPTY){
1285 new_move_str[new_move_index] = move_details->from_col;
1287 new_move_str[new_move_index] = 'x';
1290 else if(move_list->next != NULL){
1291 new_move_str[new_move_index] = move_details->from_col;
1294 /* Add in the destination. */
1295 new_move_str[new_move_index] = to_col;
1297 new_move_str[new_move_index] = to_rank;
1299 if(class == PAWN_MOVE_WITH_PROMOTION){
1300 const char *promoted_piece =
1301 piece_str(move_details->promoted_piece);
1302 new_move_str[new_move_index] = '=';
1304 strcpy((char *) &new_move_str[new_move_index],
1306 new_move_index += strlen(promoted_piece);
1308 new_move_str[new_move_index] = '\0';
1311 { const char *piece = piece_str(move_details->piece_to_move);
1312 strcpy((char *) &new_move_str[0],piece);
1313 new_move_index += strlen(piece);
1314 /* Check for the need to disambiguate. */
1315 if(move_list->next != NULL){
1316 /* It is necessary. Count how many times
1317 * the from_ col and rank occur in the list
1318 * of possibles in order to determine which to use
1321 int col_times = 0, rank_times = 0;
1323 Col from_col = move_details->from_col;
1324 Rank from_rank = move_details->from_rank;
1326 for(possible = move_list; possible != NULL;
1327 possible = possible->next){
1328 if(possible->from_col == from_col){
1331 if(possible->from_rank == from_rank){
1337 new_move_str[new_move_index] = from_col;
1340 else if(rank_times == 1){
1342 new_move_str[new_move_index] = from_rank;
1347 new_move_str[new_move_index] = from_col;
1349 new_move_str[new_move_index] = from_rank;
1353 /* See if a capture symbol is needed. */
1354 if(move_details->captured_piece != EMPTY){
1355 new_move_str[new_move_index] = 'x';
1358 /* Add in the destination. */
1359 new_move_str[new_move_index] = to_col;
1361 new_move_str[new_move_index] = to_rank;
1363 new_move_str[new_move_index] = '\0';
1366 case KINGSIDE_CASTLE:
1367 strcpy((char *) new_move_str,"O-O");
1369 case QUEENSIDE_CASTLE:
1370 strcpy((char *) new_move_str,"O-O-O");
1373 strcpy((char *) new_move_str, (char *) NULL_MOVE_STRING);
1381 if(move_details->check_status != NOCHECK){
1382 if(move_details->check_status == CHECK){
1383 /* It isn't mate. */
1384 strcat((char *) new_move_str,"+");
1387 if(GlobalState.output_format == CM){
1388 strcat((char *) new_move_str,"++");
1391 strcat((char *) new_move_str,"#");
1396 /* Update the move_details structure with the new string. */
1397 strcpy((char *) move_details->move,
1398 (const char *) new_move_str);
1400 if(move_list != NULL){
1401 free_move_pair_list(move_list);
1407 /* Rewrite move_details->move and apply the move to board.
1408 * Return TRUE if the move is ok, FALSE otherwise.
1411 rewrite_move(Colour colour,Move *move_details, Board *board, Boolean null_move_found)
1412 { /* Assume success. */
1415 if(rewrite_SAN_string(colour,move_details,board)){
1416 Piece piece_to_move = move_details->piece_to_move;
1418 if(move_details->class != NULL_MOVE) {
1419 make_move(move_details->class, move_details->from_col,move_details->from_rank,
1420 move_details->to_col,move_details->to_rank,
1421 piece_to_move,colour,board);
1424 null_move_found = TRUE;
1426 /* See if there are any subsiduary actions. */
1427 switch(move_details->class){
1430 case ENPASSANT_PAWN_MOVE:
1431 /* Nothing more to do. */
1433 case PAWN_MOVE_WITH_PROMOTION:
1434 if(move_details->class == PAWN_MOVE_WITH_PROMOTION){
1435 if(move_details->promoted_piece != EMPTY){
1436 /* @@@ Do promoted moves have '+' properly appended? */
1437 /* Now make the promotion. */
1438 make_move(move_details->class, move_details->to_col,move_details->to_rank,
1439 move_details->to_col,move_details->to_rank,
1440 move_details->promoted_piece,colour,board);
1447 case KINGSIDE_CASTLE:
1448 /* Step the Rook to the left of the king. */
1449 piece_to_move = ROOK;
1450 make_move(move_details->class, LASTCOL,move_details->from_rank,move_details->to_col-1,
1451 move_details->to_rank,
1452 piece_to_move,colour,board);
1454 case QUEENSIDE_CASTLE:
1455 /* Step the Rook to the right of the king. */
1456 piece_to_move = ROOK;
1457 make_move(move_details->class, FIRSTCOL,move_details->from_rank,move_details->to_col+1,
1458 move_details->to_rank,
1459 piece_to_move,colour,board);
1476 /* Rewrite the list of moves by playing through the game. */
1478 rewrite_moves(Board *board, Move *moves, Boolean null_move_found)
1479 { Boolean game_ok = TRUE;
1480 Move *move_details = moves;
1482 while(game_ok && (move_details != NULL)){
1483 if(*(move_details->move) != '\0'){
1484 /* See if there are any variations associated with this move. */
1485 if((move_details->Variants != NULL) &&
1486 GlobalState.keep_variations &&
1487 !rewrite_variations(board,move_details->Variants, null_move_found)){
1488 /* Something wrong with the variations. */
1491 /* @@@ There was a else-if here; not sure why?! */
1492 if(move_details->class == NULL_MOVE) {
1493 null_move_found = TRUE;
1495 if(rewrite_move(board->to_move,move_details,board, null_move_found)){
1496 board->to_move = OPPOSITE_COLOUR(board->to_move);
1498 if(GlobalState.output_evaluation) {
1499 /* Append an evaluation of the new state of the board
1500 * with the move having been played.
1502 append_evaluation(move_details, board);
1505 if(GlobalState.add_FEN_comments) {
1506 /* Append a FEN comment with the new state of the board
1507 * with the move having been played.
1509 append_FEN_comment(move_details, board);
1512 move_details = move_details->next;
1513 if(board->to_move == WHITE){
1514 board->move_number++;
1518 /* Broken games that are being kept have probably already been
1519 * reported, so don't repeat the error.
1521 if(!GlobalState.keep_broken_games) {
1522 fprintf(GlobalState.logfile,
1523 "Failed to rewrite move %u%s %s in the game:\n",
1525 (board->to_move == WHITE)?".":"...",
1526 move_details->move);
1527 report_details(GlobalState.logfile);
1528 print_board(board,GlobalState.logfile);
1534 /* An empty move. */
1535 fprintf(GlobalState.logfile,
1536 "Internal error: Empty move in rewrite_moves.\n");
1537 report_details(GlobalState.logfile);
1541 if(!game_ok && GlobalState.keep_broken_games && move_details != NULL) {
1542 /* Try to place the remaining moves into a comment. */
1543 CommentList *comment = (CommentList* ) MallocOrDie(sizeof(*comment));
1544 /* Break the link from the previous move. */
1546 StringList *commented_move_list = NULL;
1548 /* Find the move before the current one. */
1550 while(prev != NULL && prev->next != move_details) {
1556 while(move_details != NULL) {
1557 commented_move_list = save_string_list_item(commented_move_list, (char *) move_details->move);
1558 if(move_details->next == NULL && prev != NULL) {
1559 /* Pass the terminating result to prev. */
1560 prev->terminating_result = move_details->terminating_result;
1561 move_details->terminating_result = NULL;
1562 move_details = NULL;
1565 move_details = move_details->next;
1568 comment->Comment = commented_move_list;
1569 comment->next = NULL;
1572 append_comments_to_move(prev, comment);
1578 /* Rewrite the list of variations.
1579 * Return TRUE if the variation are ok. a position that
1582 rewrite_variations(const Board *board,Variation *variation, Boolean null_move_found)
1583 { Board *copy_board = allocate_new_board();
1584 Boolean variations_ok = TRUE;
1586 while((variation != NULL) && variations_ok){
1587 /* Work on the copy. */
1588 *copy_board = *board;
1590 variations_ok = rewrite_moves(copy_board,variation->moves, null_move_found);
1591 variation = variation->next;
1593 (void) free_board((void *)copy_board);
1594 return variations_ok;
1597 /* moves contains a complete game score.
1598 * Try to rewrite each move into SAN as it is played on a new board.
1599 * Return the final Board position if the game was rewritten alright,
1603 rewrite_game(Game *current_game)
1605 const char *fen = current_game->tags[FEN_TAG];
1606 Board *board = new_game_board(fen);
1609 /* No null-move found at the start of the game. */
1610 game_ok = rewrite_moves(board,current_game->moves,FALSE);
1613 else if(GlobalState.keep_broken_games) {
1616 (void) free_board((void *)board);
1623 /* Define a table to hold the positional hash codes of interest. */
1625 static HashLog *codes_of_interest[MAX_CODE];
1627 /* move_details is a variation in which we are interested.
1628 * Generate and store the hash value in codes_of_interest.
1631 store_hash_value(Move *move_details,const char *fen)
1632 { Move *move = move_details;
1633 Board *board = new_game_board(fen);
1636 while((move != NULL) && Ok){
1637 /* Reset print_move number if a variation was printed. */
1638 if(*(move->move) == '\0'){
1639 /* A comment node, not a proper move. */
1642 else if(apply_move(board->to_move,move,board)){
1643 board->to_move = OPPOSITE_COLOUR(board->to_move);
1645 if(board->to_move == WHITE){
1646 board->move_number++;
1650 print_error_context(GlobalState.logfile);
1651 fprintf(GlobalState.logfile,"Failed to make move %u%s %s\n",
1653 (board->to_move == WHITE)?".":"...",
1663 HashLog *entry = (HashLog *)MallocOrDie(sizeof(*entry));
1664 unsigned ix = board->hash_value % MAX_CODE;
1666 /* We don't include the cumulative hash value as the sequence
1667 * of moves to reach this position is not important.
1669 entry->cumulative_hash_value = 0;
1670 entry->final_hash_value = board->hash_value;
1671 /* Link it into the head at this index. */
1672 entry->next = codes_of_interest[ix];
1673 codes_of_interest[ix] = entry;
1675 (void) free_board((void *)board);
1678 /* Does the current board match a position of interest.
1679 * Look in codes_of_interest for current_hash_value.
1682 position_matches(const Board *board)
1684 HashCode current_hash_value = board->hash_value;
1685 unsigned ix = current_hash_value % MAX_CODE;
1686 Boolean found = FALSE;
1689 for(entry = codes_of_interest[ix]; !found && (entry != NULL);
1690 entry = entry->next){
1691 /* We can test against just the position value. */
1692 if(entry->final_hash_value == current_hash_value){
1697 const char *matching_pattern = matchBoard(board);
1698 found = matching_pattern != NULL;
1703 /* Build a basic EPD string from the given board. */
1705 build_basic_EPD_string(const Board *board,char *epd)
1708 Boolean castling_allowed = FALSE;
1709 for(rank = LASTRANK; rank >= FIRSTRANK; rank--){
1711 int consecutive_spaces = 0;
1712 for(col = FIRSTCOL; col <= LASTCOL; col++){
1713 int coloured_piece = board->board[RankConvert(rank)]
1715 if(coloured_piece != EMPTY){
1716 if(consecutive_spaces > 0){
1717 epd[ix] = '0'+consecutive_spaces;
1719 consecutive_spaces = 0;
1721 epd[ix] = coloured_piece_to_SAN_letter(coloured_piece);
1725 consecutive_spaces++;
1728 if(consecutive_spaces > 0){
1729 epd[ix] = '0'+consecutive_spaces;
1732 /* Terminate the row. */
1733 if(rank != FIRSTRANK){
1734 epd[ix] = '/'; ix++;
1737 epd[ix] = ' '; ix++;
1739 epd[ix] = board->to_move == WHITE? 'w' : 'b'; ix++;
1740 epd[ix] = ' '; ix++;
1742 /* Castling details. */
1743 if(board->WKingCastle){
1744 epd[ix] = 'K'; ix++;
1745 castling_allowed = TRUE;
1747 if(board->WQueenCastle){
1748 epd[ix] = 'Q'; ix++;
1749 castling_allowed = TRUE;
1751 if(board->BKingCastle){
1752 epd[ix] = 'k'; ix++;
1753 castling_allowed = TRUE;
1755 if(board->BQueenCastle){
1756 epd[ix] = 'q'; ix++;
1757 castling_allowed = TRUE;
1759 if(!castling_allowed){
1760 /* There are no castling rights. */
1764 epd[ix] = ' '; ix++;
1767 if(board->EnPassant){
1768 epd[ix] = board->ep_col; ix++;
1769 epd[ix] = board->ep_rank; ix++;
1772 epd[ix] = '-'; ix++;
1777 /* Build a FEN string from the given board. */
1779 build_FEN_string(const Board *board,char *fen)
1782 int full_move_number =
1783 board->to_move == BLACK ? board->move_number : (board->move_number + 1);
1785 build_basic_EPD_string(board,fen);
1786 /* Append the (pseudo) half move count and the full move count. */
1788 fen[ix] = ' '; ix++;
1790 /* Half moves since the last capture or pawn move. */
1791 sprintf(&fen[ix], "%u", board->halfmove_clock);
1793 fen[ix] = ' '; ix++;
1795 /* The full move number. */
1796 sprintf(&fen[ix],"%u", full_move_number);
1800 build_BPFEN_string(const Board *board,char **bpfen, int* bpfen_len)
1801 { static int bits[256]; /* Max six bits per piece, 32 blanks, and then some. */
1805 for(rank = LASTRANK; rank >= FIRSTRANK; rank--){
1807 for(col = FIRSTCOL; col <= LASTCOL; col++){
1808 int coloured_piece = board->board[RankConvert(rank)]
1810 if(coloured_piece == EMPTY){
1811 bits[bit_pos++] = 0;
1814 bits[bit_pos++] = 1;
1815 bits[bit_pos++] = (EXTRACT_COLOUR(coloured_piece) == WHITE) ? 1 : 0;
1816 switch(EXTRACT_PIECE(coloured_piece)) {
1818 bits[bit_pos++] = 0;
1821 bits[bit_pos++] = 1;
1822 bits[bit_pos++] = 0;
1823 bits[bit_pos++] = 0;
1826 bits[bit_pos++] = 1;
1827 bits[bit_pos++] = 0;
1828 bits[bit_pos++] = 1;
1831 bits[bit_pos++] = 1;
1832 bits[bit_pos++] = 1;
1833 bits[bit_pos++] = 1;
1834 bits[bit_pos++] = 0;
1837 bits[bit_pos++] = 1;
1838 bits[bit_pos++] = 1;
1839 bits[bit_pos++] = 1;
1840 bits[bit_pos++] = 1;
1841 bits[bit_pos++] = 0;
1844 bits[bit_pos++] = 1;
1845 bits[bit_pos++] = 1;
1846 bits[bit_pos++] = 1;
1847 bits[bit_pos++] = 1;
1848 bits[bit_pos++] = 1;
1854 while ((bit_pos % 8) != 0) {
1855 bits[bit_pos++] = 0;
1858 /* Non-board information. */
1859 bits[bit_pos++] = (board->to_move == WHITE) ? 0 : 1;
1860 bits[bit_pos++] = board->WKingCastle ? 1 : 0;
1861 bits[bit_pos++] = board->WQueenCastle ? 1 : 0;
1862 bits[bit_pos++] = board->BKingCastle ? 1 : 0;
1863 bits[bit_pos++] = board->BQueenCastle ? 1 : 0;
1865 if(board->EnPassant){
1866 int col_index = board->ep_col - COLBASE;
1867 bits[bit_pos++] = 1;
1868 bits[bit_pos++] = (col_index >> 2) & 1;
1869 bits[bit_pos++] = (col_index >> 1) & 1;
1870 bits[bit_pos++] = col_index & 1;
1873 bits[bit_pos++] = 0;
1876 /* Pad non-board. */
1877 while ((bit_pos % 8) != 0) {
1878 bits[bit_pos++] = 0;
1881 /* Convert from bits to binary form. */
1882 *bpfen = (char *)malloc(bit_pos / 8);
1883 *bpfen_len = bit_pos / 8;
1885 for (ix = 0; ix < bit_pos / 8; ++ix) {
1886 unsigned char ch = 0;
1889 for (jx = 0; jx < 8; ++jx) {
1890 ch |= (bits[ix * 8 + jx] << jx);
1897 /* Append to move_details a FEN comment of the board.
1898 * The board state is immediately following application of the
1902 append_FEN_comment(Move *move_details, const Board *board)
1904 char *FEN_comment = MallocOrDie(FEN_SPACE);
1905 CommentList *comment = (CommentList* ) MallocOrDie(sizeof(*comment));
1906 StringList *current_comment = save_string_list_item(NULL, FEN_comment);
1908 build_FEN_string(board, FEN_comment);
1909 comment->Comment = current_comment;
1910 comment->next = NULL;
1911 append_comments_to_move(move_details, comment);
1914 /* Append to move_details an evaluation value for board.
1915 * The board state is immediately following application of the
1919 append_evaluation(Move *move_details, const Board *board)
1921 CommentList *comment = (CommentList* ) MallocOrDie(sizeof(*comment));
1922 /* Space template for the value.
1923 * @@@ There is a buffer-overflow risk here if the evaluation value
1926 const char valueSpace[] = "-012456789.00";
1927 char *evaluation = (char *) MallocOrDie(sizeof(valueSpace));
1928 StringList *current_comment;
1930 double value = evaluate(board);
1932 /* @@@ Overflow possible here if the value is too big to fit. */
1933 sprintf(evaluation,"%.2f", value);
1934 if(strlen(evaluation) > strlen(valueSpace)) {
1935 fprintf(GlobalState.logfile,
1936 "Overflow in evaluation space in append_evaluation()\n");
1940 current_comment = save_string_list_item(NULL, evaluation);
1941 comment->Comment = current_comment;
1942 comment->next = NULL;
1943 append_comments_to_move(move_details, comment);
1946 /* Append to move_details a comment indicating that this
1947 * move resulted in a positional match.
1950 create_match_comment(Move *move_details)
1952 /* The comment string. */
1953 char *match_comment = copy_string(GlobalState.position_match_comment);
1954 StringList *current_comment = save_string_list_item(NULL, match_comment);
1955 CommentList *comment = (CommentList* ) MallocOrDie(sizeof(*comment));
1957 comment->Comment = current_comment;
1958 comment->next = NULL;
1961 /* Return an evaluation of board. */
1963 evaluate(const Board *board)
1965 return shannonEvaluation(board);
1968 /* Return an evaluation of board based on
1969 * Claude Shannon's technique.
1972 shannonEvaluation(const Board *board)
1974 MovePair *white_moves, *black_moves;
1975 int whiteMoveCount = 0, blackMoveCount = 0;
1976 int whitePieceCount = 0, blackPieceCount = 0;
1977 double shannonValue = 0.0;
1982 /* Determine the mobilities. */
1983 white_moves = find_all_moves(board, WHITE);
1984 if(white_moves != NULL){
1986 for(m = white_moves; m != NULL; m = m->next) {
1989 free_move_pair_list(white_moves);
1992 black_moves = find_all_moves(board, BLACK);
1993 if(black_moves != NULL){
1995 for(m = black_moves; m != NULL; m = m->next) {
1998 free_move_pair_list(black_moves);
2002 /* Pick up each piece of the required colour. */
2003 for(rank = LASTRANK; rank >= FIRSTRANK; rank--){
2004 short r = RankConvert(rank);
2005 for(col = FIRSTCOL; col <= LASTCOL; col++){
2006 short c = ColConvert(col);
2008 Piece occupant = board->board[r][c];
2009 if(occupant != EMPTY) {
2010 /* This square is occupied by a piece of the required colour. */
2011 Piece piece = EXTRACT_PIECE(occupant);
2030 fprintf(GlobalState.logfile,
2031 "Internal error: unknown piece %d in append_evaluation().\n",
2034 if(EXTRACT_COLOUR(occupant) == WHITE) {
2035 whitePieceCount += pieceValue;
2038 blackPieceCount += pieceValue;
2044 shannonValue = (whitePieceCount - blackPieceCount) +
2045 (whiteMoveCount - blackMoveCount) * 0.1;
2046 return shannonValue;