/* ******************************************************************************* * * * Analyze() is used to handle the "analyze" command. This mode basically * * puts Crafty into a "permanent pondering" state, where it reads a move * * from the input stream, and then "ponders" for the opposite side. * * Whenever a move is entered, Crafty reads this move, updates the game * * board, and then starts "pondering" for the other side. * * * * The purpose of this mode is to force Crafty to follow along in a game, * * providing analysis continually for the side on move until a move is * * entered, advancing the game to the next position. * * * ******************************************************************************* */ void Analyze() { int i, move, back_number, readstat = 1; TREE *const tree = block[0]; /* ************************************************************ * * * Initialize. * * * ************************************************************ */ int save_swindle_mode = swindle_mode; swindle_mode = 0; ponder_move = 0; analyze_mode = 1; if (!xboard) display_options |= 1 + 2 + 4; _printf("Analyze Mode: type \"exit\" to terminate.\n"); /* ************************************************************ * * * Now loop waiting on input, searching the current * * position continually until a move comes in. * * * ************************************************************ */ do { do { last_pv.pathd = 0; last_pv.pathl = 0; input_status = 0; pondering = 1; tree->status[1] = tree->status[0]; Iterate(game_wtm, think, 0); pondering = 0; if (book_move) moves_out_of_book = 0; if (!xboard) { if (game_wtm) _printf("analyze.White(%d): ", move_number); else _printf("analyze.Black(%d): ", move_number); fflush(stdout); } /* ************************************************************ * * * If we get back to here, something has been typed in and * * is in the command buffer normally, unless the search * * terminated naturally due to finding a mate or reaching * * the max depth allowable. * * * ************************************************************ */ if (!input_status) do { readstat = Read(1, buffer); if (readstat < 0) break; nargs = ReadParse(buffer, args, " ;"); Print(128, "%s\n", buffer); if (strstr(args[0], "timeleft") && !xboard) { if (game_wtm) _printf("analyze.White(%d): ", move_number); else _printf("analyze.Black(%d): ", move_number); fflush(stdout); } } while (strstr(args[0], "timeleft")); else nargs = ReadParse(buffer, args, " ;"); if (readstat < 0) break; move = 0; if (!strcmp(args[0], "exit")) break; /* ************************************************************ * * * First, check for the special analyze command "back n" * * and handle it if present, otherwise try Option() to see * * if it recognizes the input as a command. * * * ************************************************************ */ if (OptionMatch("back", args[0])) { if (nargs > 1) back_number = atoi(args[1]); else back_number = 1; for (i = 0; i < back_number; i++) { game_wtm = Flip(game_wtm); if (Flip(game_wtm)) move_number--; } if (move_number == 0) { move_number = 1; game_wtm = 1; } sprintf(buffer, "reset %d", move_number); Option(tree); display = tree->position; } else if (Option(tree)) { display = tree->position; } /* ************************************************************ * * * If InputMove() can recognize this as a move, make it, * * swap sides, and return to the top of the loop to call * * search from this new position. * * * ************************************************************ */ else if ((move = InputMove(tree, buffer, 0, game_wtm, 1, 0))) { char *outmove = OutputMove(tree, move, 0, game_wtm); if (history_file) { fseek(history_file, ((move_number - 1) * 2 + 1 - game_wtm) * 10, SEEK_SET); fprintf(history_file, "%9s\n", outmove); } if (game_wtm) Print(128, "White(%d): ", move_number); else Print(128, "Black(%d): ", move_number); Print(128, "%s\n", outmove); if (speech) { char announce[64]; //strcpy(announce, SPEAK); //strcat(announce, outmove); //system(announce); SPEAK(outmove); } MakeMoveRoot(tree, move, game_wtm); display = tree->position; last_mate_score = 0; if (log_file) DisplayChessBoardFile(log_file, tree->position); } /* ************************************************************ * * * If Option() didn't handle the input, then it is illegal * * and should be reported to the user. * * * ************************************************************ */ else { pondering = 0; if (Option(tree) == 0) _printf("illegal move: %s\n", buffer); pondering = 1; display = tree->position; } } while (!move); if (readstat < 0 || !strcmp(args[0], "exit")) break; game_wtm = Flip(game_wtm); if (game_wtm) move_number++; } while (1); analyze_mode = 0; _printf("analyze complete.\n"); pondering = 0; swindle_mode = save_swindle_mode; }
/* ******************************************************************************** * * * "annotate" command is used to search through the game in the "history" file * * (often set by the "read" command which reads moves in, skipping non-move * * information such as move numbers, times, etc.) * * * * the normal output of this command is a file, in PGN format, that contains * * the moves of the game, along with analysis when Crafty does not think that * * move was the best choice. the definition of "best choice" is somewhat * * vague, because if the move played is "close" to the best move available, * * Crafty will not comment on the move. "close" is defined by the <margin> * * option explained below. this basic type of annotation works by first * * using the normal tree search algorithm to find the best move. if this * * move was the move played, no output is produced. if a different move is * * considered best, then the actual move played is searched to the same depth * * and if the best move and actual move scores are within <margin> of each * * other, no comment is produced, otherwise crafty inserts the evaluation for * * the move played, followed by the eval and PV for the best continuation it * * found. * * * * the format of the command is as follows: * * * * annotate filename b|w|bw moves margin time [n] * * * * filename is the input file where Crafty will obtain the moves to annotate, * * and output will be written to file "filename.ann". * * * * where b/w/bw indicates whether to annotate only the white side (w), the * * black side (b) or both (bw). * * * * moves indicates the move or moves to annotate. it can be a single move, * * which indicates the starting move number to annotate, or it can be a range, * * which indicates a range of move (1-999 gets the whole game.) * * * * margin is the difference between Crafty's evaluation for the move actually * * played and for the move Crafty thinks is best, before crafty will generate * * a comment in the annotation file. 1.0 is a pawn, and will only generate * * comments if the move played is 1.000 (1 pawn) worse than the best move * * found by doing a complete search. * * * * time is time per move to search, in seconds. * * * * [n] is optional and tells Crafty to produce the PV/score for the "n" best * * moves. Crafty stops when the best move reaches the move played in the game * * or after displaying n moves, whichever comes first. if you use -n, then it * * will display n moves regardless of where the game move ranks. * * * ******************************************************************************** */ void Annotate() { FILE *annotate_in, *annotate_out; char command[80], text[128], colors[32], next; int annotate_margin, annotate_score[100], player_score, best_moves; int annotate_search_time_limit, search_player; int twtm, path_len, analysis_printed=0, *mv; int wtm, move_number, move_num, line1, line2, move, suggested, i; int temp_draw_score_is_zero, searches_done; CHESS_PATH temp[100], player_pv; /* ---------------------------------------------------------- | | | first, quiz the user for the options needed to | | successfully annotate a game. | | | ---------------------------------------------------------- */ Print(0,"\nannotate filename: "); fscanf(input_stream,"%s",text); annotate_in = fopen(text,"r"); if (annotate_in == NULL) { Print(0,"unable to open %s for input\n", text); return; } sprintf(command,"read=%s",text); Option(command); strcpy(text+strlen(text),".can"); annotate_out = fopen(text,"w"); if (annotate_out == NULL) { Print(0,"unable to open %s for output\n", text); return; } Print(0,"\ncolor(s): "); fscanf(input_stream,"%s",colors); line1=1; line2=999; Print(0,"\nstarting move number or range: "); fscanf(input_stream,"%s",text); if(strchr(text,'-')) sscanf(text,"%d-%d",&line1,&line2); else { sscanf(text,"%d",&line1); line2=999; } Print(0,"\nannotation margin: "); fscanf(input_stream,"%s",text); annotate_margin=atof(text)*PAWN_VALUE; Print(0,"\ntime per move: "); fscanf(input_stream,"%s",text); annotate_search_time_limit=atoi(text)*100; next=getc(input_stream); best_moves=1; if (next == ' ') { fscanf(input_stream,"%s",text); best_moves=atoi(text); } /* ---------------------------------------------------------- | | | reset the game to "square 0" to start the annotation | | procedure. then we read moves from the input file, | | make them on the game board, and annotate if the move | | is for the correct side. if we haven't yet reached | | the starting move to annotate, we skip the Search() | | stuff and read another move. | | | ---------------------------------------------------------- */ annotate_mode=1; temp_draw_score_is_zero=draw_score_is_zero; draw_score_is_zero=1; ponder_completed=0; ponder_move=0; last_pv.path_iteration_depth=0; last_pv.path_length=0; InitializeChessBoard(&position[0]); wtm=1; move_number=1; fprintf(annotate_out,"[Event \"%s\"]\n",pgn_event); fprintf(annotate_out,"[Site \"%s\"]\n",pgn_site); fprintf(annotate_out,"[Date \"%s\"]\n",pgn_date); fprintf(annotate_out,"[Round \"%s\"]\n",pgn_round); fprintf(annotate_out,"[White \"%s\"]\n",pgn_white); fprintf(annotate_out,"[WhiteElo \"%s\"]\n",pgn_white_elo); fprintf(annotate_out,"[Black \"%s\"]\n",pgn_black); fprintf(annotate_out,"[BlackElo \"%s\"]\n",pgn_black_elo); fprintf(annotate_out,"[Result \"%s\"]\n",pgn_result); fprintf(annotate_out,"[Annotator \"Crafty v%s\"]\n",version); if (!strcmp(colors,"bw") || !strcmp(colors,"wb")) fprintf(annotate_out,"{annotating both black and white moves.}\n"); else if (strchr(colors,'b')) fprintf(annotate_out,"{annotating only black moves.}\n"); else if (strchr(colors,'w')) fprintf(annotate_out,"{annotating only white moves.}\n"); fprintf(annotate_out,"{using a scoring margin of %s pawns.}\n", DisplayEvaluationWhisper(annotate_margin)); fprintf(annotate_out,"{search time limit is %s}\n\n", DisplayTimeWhisper(annotate_search_time_limit)); do { fflush(annotate_out); move=ReadChessMove(annotate_in,wtm,0); if (move <= 0) break; strcpy(text,OutputMove(&move,0,wtm)); fseek(history_file,((move_number-1)*2+1-wtm)*10,SEEK_SET); fprintf(history_file,"%10s ",text); if (wtm) Print(0,"White(%d): %s\n",move_number,text); else Print(0,"Black(%d): %s\n",move_number,text); if (analysis_printed) fprintf(annotate_out,"%3d.%s%6s\n",move_number,(wtm?"":" ..."),text); else { if (wtm) fprintf(annotate_out,"%3d.%6s",move_number,text); else fprintf(annotate_out,"%6s\n",text); } analysis_printed=0; if (move_number >= line1) { if ((!wtm && strchr(colors,'b')) | ( wtm && strchr(colors,'w'))) { last_pv.path_iteration_depth=0; last_pv.path_length=0; thinking=1; RootMoveList(wtm); /* ---------------------------------------------------------- | | | now search the position to see if the move played is | | the best move possible. if not, then search just the | | move played to get a score for it as well, so we can | | determine if annotated output is appropriate. | | | ---------------------------------------------------------- */ search_time_limit=annotate_search_time_limit; search_depth=0; player_score=-999999; search_player=1; for (searches_done=0;searches_done<abs(best_moves);searches_done++) { if (searches_done > 0) { search_time_limit=3*annotate_search_time_limit; search_depth=temp[0].path_iteration_depth; } Print(0,"\n Searching all legal moves."); Print(0,"----------------------------------\n"); position[1]=position[0]; InitializeHashTables(); annotate_score[searches_done]=Iterate(wtm,annotate); if (pv[0].path[1] == move) { player_score=annotate_score[searches_done]; player_pv=pv[0]; search_player=0; } temp[searches_done]=pv[0]; for (mv=last[0];mv<last[1];mv++) { if (*mv == pv[0].path[1]) { for (;mv<last[1]-1;mv++) *mv=*(mv+1); last[1]--; break; } } if (last[1] < last[0] || (player_score+annotate_margin>annotate_score[searches_done] && best_moves>0)) break; } if (search_player) { Print(0,"\n Searching only the move played in game."); Print(0,"--------------------\n"); position[1]=position[0]; search_move=move; search_time_limit=3*annotate_search_time_limit; search_depth=temp[0].path_iteration_depth; if (search_depth==0) search_time_limit=annotate_search_time_limit; InitializeHashTables(); player_score=Iterate(wtm,annotate); player_pv=pv[0]; search_depth=0; search_time_limit=annotate_search_time_limit; search_move=0; } /* ---------------------------------------------------------- | | | output the score/pv for the move played unless it | | matches what Crafty would have played. if it doesn't | | then output the pv for what Crafty thinks is best. | | | ---------------------------------------------------------- */ thinking=0; if (player_pv.path_iteration_depth>1 && player_pv.path_length>=1 && player_score+annotate_margin<annotate_score[0] && (temp[0].path[1]!=player_pv.path[1] || annotate_margin<0.0 || best_moves!=1)) { if (wtm) { analysis_printed=1; fprintf(annotate_out,"\n"); } twtm = wtm; fprintf(annotate_out," {%d:%s", player_pv.path_iteration_depth, DisplayEvaluationWhisper(player_score)); path_len=player_pv.path_length; for (i=1;i<=path_len;i++) { fprintf(annotate_out," %s", OutputMove(&player_pv.path[i],i,twtm)); MakeMove(i,player_pv.path[i],twtm); twtm=ChangeSide(twtm); } for (i=path_len;i>0;i--) { twtm=ChangeSide(twtm); UnMakeMove(i,player_pv.path[i],twtm); } fprintf(annotate_out,"}\n"); for (move_num=0;move_num<searches_done;move_num++) { twtm = wtm; if (move != temp[move_num].path[1]) { fprintf(annotate_out," {%d:%s", temp[move_num].path_iteration_depth, DisplayEvaluationWhisper(annotate_score[move_num])); path_len=temp[move_num].path_length; for (i=1;i<=path_len;i++) { fprintf(annotate_out," %s", OutputMove(&temp[move_num].path[i],i,twtm)); MakeMove(i,temp[move_num].path[i],twtm); twtm=ChangeSide(twtm); } for (i=path_len;i>0;i--) { twtm=ChangeSide(twtm); UnMakeMove(i,temp[move_num].path[i],twtm); } fprintf(annotate_out,"}\n"); } } } } } /* ---------------------------------------------------------- | | | before going on to the next move, see if the user has | | included a set of other moves that require a search. | | if so, search them one at a time and produce the ana- | | lysis for each one. | | | ---------------------------------------------------------- */ do { next=getc(annotate_in); } while (next==' ' || next=='\n'); ungetc(next,annotate_in); if (next == EOF) break; if (next == '{') do { do { next=getc(annotate_in); } while (next==' '); ungetc(next,annotate_in); if (next == EOF || next == '}') break; suggested=ReadChessMove(annotate_in,wtm,1); if (suggested < 0) break; if (suggested > 0) { thinking=1; Print(0,"\n Searching only the move suggested."); Print(0,"--------------------\n"); position[1]=position[0]; search_move=suggested; search_time_limit=3*annotate_search_time_limit; search_depth=temp[0].path_iteration_depth; InitializeHashTables(); annotate_score[0]=Iterate(wtm,annotate); search_depth=0; search_time_limit=annotate_search_time_limit; search_move=0; thinking=0; twtm = wtm; path_len = pv[0].path_length; if (pv[0].path_iteration_depth > 1 && path_len >= 1) { if (wtm && !analysis_printed) { analysis_printed=1; fprintf(annotate_out,"\n"); } fprintf(annotate_out," {suggested %d:%s", pv[0].path_iteration_depth, DisplayEvaluationWhisper(annotate_score[0])); for (i=1;i<=path_len;i++) { fprintf(annotate_out," %s",OutputMove(&pv[0].path[i],i,twtm)); MakeMove(i,pv[0].path[i],twtm); twtm=ChangeSide(twtm); } for (i=path_len;i>0;i--) { twtm=ChangeSide(twtm); UnMakeMove(i,pv[0].path[i],twtm); } fprintf(annotate_out,"}\n"); } } } while(1); MakeMoveRoot(move,wtm); wtm=ChangeSide(wtm); if (wtm) move_number++; if (move_number > line2) break; } while (1); fprintf(annotate_out,"\n"); if (annotate_out) fclose(annotate_out); if (annotate_in) fclose(annotate_in); search_time_limit=0; draw_score_is_zero=temp_draw_score_is_zero; annotate_mode=0; }