/* ******************************************************************************** * * * StorePV() is called by Iterate() to insert the PV moves so they will be * * searched before any other moves. * * * ******************************************************************************** */ void StorePV(int ply, int wtm) { register BITBOARD temp_hash_key; register HASH_ENTRY *htable; /* ---------------------------------------------------------- | | | make sure the move being stored is legal, so that a | | bad move doesn't get into hash table. | | | ---------------------------------------------------------- */ if (!ValidMove(ply,wtm,pv[ply].path[ply])) { printf("\ninstalling bogus move...ply=%d\n",ply); printf("installing %s\n",OutputMove(&pv[ply].path[ply],ply,wtm)); return; } /* ---------------------------------------------------------- | | | first, compute the initial hash address and choose | | which hash table (based on color) to probe. | | | ---------------------------------------------------------- */ temp_hash_key=HashKey; htable=((wtm) ? trans_ref_wb : trans_ref_bb)+(((int) temp_hash_key)&hash_maskb); temp_hash_key=temp_hash_key>>16; /* ---------------------------------------------------------- | | | now "fill in the blank" and build a table entry from | | current search information. | | | ---------------------------------------------------------- */ htable->word1=Shiftl((BITBOARD) 65536,21); htable->word1=Or(htable->word1,Shiftl((BITBOARD) ((transposition_id<<2)+WORTHLESS),59)); htable->word1=Or(htable->word1,(BITBOARD) pv[ply].path[ply]); htable->word2=temp_hash_key; }
/* ******************************************************************************** * * * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * * its operation is simple: find a predicted move by (a) taking the second * * move from the principal variation, or (b) call lookup to see if it finds * * a suggested move from the transposition table. then, make this move and * * do a search from the resulting position. while pondering, one of three * * things can happen: (1) a move is entered, and it matches the predicted * * move. we then switch from pondering to thinking and search as normal; * * (2) a move is entered, but it does not match the predicted move. we then * * abort the search, unmake the pondered move, and then restart with the move * * entered. (3) a command is entered. if it is a simple command, it can be * * done without aborting the search or losing time. if not, we abort the * * search, execute the command, and then attempt to restart pondering if the * * command didn't make that impossible. * * * ******************************************************************************** */ int Ponder(int wtm) { int dummy=0, i, *n_ponder_moves, *mv; /* ---------------------------------------------------------- | | | first, let's check to see if pondering is allowed, or | | if we should avoid pondering on this move since it is | | the first move of a game, or if the game is over, or | | "force" mode is active, or there is input in the queue | | that needs to be read and processed. | | | ---------------------------------------------------------- */ if (!ponder || force || over || CheckInput()) return(0); /* ---------------------------------------------------------- | | | if we don't have a predicted move to ponder, try two | | sources: (1) look up the current position in the | | transposition table and see if it has a suggested best | | move; (2) do a short tree search to calculate a move | | that we should ponder. | | | ---------------------------------------------------------- */ strcpy(hint,"none"); if (!ponder_move) { (void) LookUp(0,0,wtm,&dummy,&dummy); if (hash_move[0]) ponder_move=hash_move[0]; } if (!ponder_move) { TimeSet(puzzle); if (time_limit < 3) return(0); puzzling=1; position[1]=position[0]; printf(" puzzling over a move to ponder.\n"); last_pv.path_length=0; last_pv.path_iteration_depth=0; for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } (void) Iterate(wtm,puzzle,0); for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } puzzling=0; if (pv[0].path_length) ponder_move=pv[0].path[1]; if (!ponder_move) return(0); for (i=1;i<(int) pv[0].path_length-1;i++) last_pv.path[i]=pv[0].path[i+1]; last_pv.path_length=pv[0].path_length-1; last_pv.path_iteration_depth=0; if (!ValidMove(1,wtm,ponder_move)) { ponder_move=0; return(0); } } /* ---------------------------------------------------------- | | | display the move we are going to "ponder". | | | ---------------------------------------------------------- */ if (wtm) printf("White(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); else printf("Black(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); sprintf(hint,"%s",OutputMove(&ponder_move,0,wtm)); if (post) printf("Hint: %s\n",hint); /* ---------------------------------------------------------- | | | set the ponder move list and eliminate illegal moves. | | | ---------------------------------------------------------- */ n_ponder_moves=GenerateCaptures(0, wtm, ponder_moves); num_ponder_moves=GenerateNonCaptures(0, wtm, n_ponder_moves)-ponder_moves; for (mv=ponder_moves;mv<ponder_moves+num_ponder_moves;mv++) { MakeMove(0, *mv, wtm); if (Check(wtm)) { UnMakeMove(0, *mv, wtm); *mv=0; } else UnMakeMove(0, *mv, wtm); } /* ---------------------------------------------------------- | | | now, perform an iterated search, but with the special | | "pondering" flag set which changes the time controls | | since there is no need to stop searching until the | | opponent makes a move. | | | ---------------------------------------------------------- */ MakeMove(0,ponder_move,wtm); if (Rule50Moves(1)==90 || Rule50Moves(1)==91) ClearHashTables(); last_opponent_move=ponder_move; if (ChangeSide(wtm)) *rephead_w++=HashKey; else *rephead_b++=HashKey; if (RepetitionDraw(wtm)) printf("game is a draw by repetition\n"); if (whisper) strcpy(whisper_text,"n/a"); thinking=0; pondering=1; (void) Iterate(ChangeSide(wtm),think,0); pondering=0; thinking=0; if (ChangeSide(wtm)) rephead_w--; else rephead_b--; last_opponent_move=0; UnMakeMove(0,ponder_move,wtm); /* ---------------------------------------------------------- | | | search completed. the possible return values are: | | | | (0) no pondering was done, period. | | | | (1) pondering was done, opponent made the predicted | | move, and we searched until time ran out in a | | normal manner. | | | | (2) pondering was done, but the ponder search | | terminated due to either finding a mate, or the | | maximum search depth was reached. the result of | | this ponder search are valid, but only if the | | opponent makes the correct (predicted) move. | | | | (3) pondering was done, but the opponent either made | | a different move, or entered a command that has to | | interrupt the pondering search before the command | | (or move) can be processed. this forces Main() to | | avoid reading in a move/command since one has been | | read into the command buffer already. | | | ---------------------------------------------------------- */ if (made_predicted_move) return(1); if (abort_search) return(3); return(2); }
/* ******************************************************************************* * * * 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; }
/* ******************************************************************************** * * * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * * its operation is simple: find a predicted move by (a) taking the second * * move from the principal variation, or (b) call lookup to see if it finds * * a suggested move from the transposition table. then, make this move and * * do a search from the resulting position. while pondering, one of three * * things can happen: (1) a move is entered, and it matches the predicted * * move. we then switch from pondering to thinking and search as normal; * * (2) a move is entered, but it does not match the predicted move. we then * * abort the search, unmake the pondered move, and then restart with the move * * entered. (3) a command is entered. if it is a simple command, it can be * * done without aborting the search or losing time. if not, we abort the * * search, execute the command, and then attempt to restart pondering if the * * command didn't make that impossible. * * * ******************************************************************************** */ int Ponder(int wtm) { int dummy=0, i, *n_ponder_moves; /* ---------------------------------------------------------- | | | if we don't have a predicted move to ponder, try two | | sources: (1) look up the current position in the | | transposition table and see if it has a suggested best | | move; (2) do a short tree search to calculate a move | | that we should ponder. | | | ---------------------------------------------------------- */ ponder_completed=0; if (!ponder_move) { (void) LookUp(0,0,wtm,&dummy,dummy); if (hash_move[0]) ponder_move=hash_move[0]; } if (!ponder_move) { TimeSet(puzzle); if (time_limit < 3) return(0); puzzling=1; position[1]=position[0]; Print(2," puzzling over a move to ponder.\n"); last_pv.path_length=0; last_pv.path_iteration_depth=0; for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } (void) Iterate(wtm,puzzle); for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } puzzling=0; if (pv[0].path_length) ponder_move=pv[0].path[1]; if (!ponder_move) return(0); for (i=1;i<(int) pv[0].path_length-1;i++) last_pv.path[i]=pv[0].path[i+1]; last_pv.path_length=pv[0].path_length-1; last_pv.path_iteration_depth=0; if (!ValidMove(1,wtm,ponder_move)) { printf("puzzle returned an illegal move!\n"); DisplayChessMove("move= ",ponder_move); ponder_move=0; return(0); } } if (!ponder_move) { strcpy(hint,"none"); return(0); } /* ---------------------------------------------------------- | | | display the move we are going to "ponder". | | | ---------------------------------------------------------- */ if (wtm) Print(2,"White(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); else Print(2,"Black(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); sprintf(hint,"%s",OutputMove(&ponder_move,0,wtm)); n_ponder_moves=GenerateCaptures(0, wtm, ponder_moves); num_ponder_moves=GenerateNonCaptures(0, wtm, n_ponder_moves)-ponder_moves; /* ---------------------------------------------------------- | | | now, perform an iterated search, but with the special | | "pondering" flag set which changes the time controls | | since there is no need to stop searching until the | | opponent makes a move. | | | ---------------------------------------------------------- */ MakeMove(0,ponder_move,wtm); if (Rule50Moves(1)==90 || Rule50Moves(1)==91) ClearHashTables(); last_opponent_move=ponder_move; if (ChangeSide(wtm)) *rephead_w++=HashKey; else *rephead_b++=HashKey; if (RepetitionDraw(wtm)) Print(0,"game is a draw by repetition\n"); if (whisper) strcpy(whisper_text,"n/a"); pondering=1; (void) Iterate(ChangeSide(wtm),think); pondering=0; if (!abort_search) ponder_completed=1; if (ChangeSide(wtm)) rephead_w--; else rephead_b--; last_opponent_move=0; UnMakeMove(0,ponder_move,wtm); /* ---------------------------------------------------------- | | | search completed. there are two possible reasons for | | us to get here. (1) the opponent made the predicted | | move and we have used enough time on the search, or; | | the operator typed in a command (or not-predicted | | move) that requires the search to abort and restart. | | | ---------------------------------------------------------- */ if (made_predicted_move) return(1); return(0); }
/* ******************************************************************************* * * * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * * its operation is simple: Find a predicted move by (a) taking the second * * move from the principal variation, or (b) call lookup to see if it finds * * a suggested move from the transposition table. Then, make this move and * * do a search from the resulting position. While pondering, one of three * * things can happen: (1) A move is entered, and it matches the predicted * * move. We then switch from pondering to thinking and search as normal; * * (2) A move is entered, but it does not match the predicted move. We then * * abort the search, unmake the pondered move, and then restart with the * * move entered. (3) A command is entered. If it is a simple command, it * * can be done without aborting the search or losing time. If not, we abort * * the search, execute the command, and then attempt to restart pondering if * * the command didn't make that impossible. * * * ******************************************************************************* */ int Ponder(int wtm) { TREE *const tree = block[0]; int dalpha = -999999, dbeta = 999999, i; unsigned *n_ponder_moves, *mv; int save_move_number, tlom, value; /* ************************************************************ * * * First, let's check to see if pondering is allowed, or * * if we should avoid pondering on this move since it is * * the first move of a game, or if the game is over, or * * "force" mode is active, or there is input in the queue * * that needs to be read and processed. * * * ************************************************************ */ if (!ponder || force || over || CheckInput()) return 0; save_move_number = move_number; /* ************************************************************ * * * Check the ponder move for legality. If it is not a * * legal move, we have to take action to find something to * * ponder. * * * ************************************************************ */ strcpy(ponder_text, "none"); if (ponder_move) { if (!VerifyMove(tree, 1, wtm, ponder_move)) { ponder_move = 0; Print(4095, "ERROR. ponder_move is illegal (1).\n"); Print(4095, "ERROR. PV pathl=%d\n", last_pv.pathl); Print(4095, "ERROR. move=%d %x\n", ponder_move, ponder_move); } } /* ************************************************************ * * * First attempt, do a hash probe. However, since a hash * * collision is remotely possible, we still need to verify * * that the transposition/refutation best move is actually * * legal. * * * ************************************************************ */ if (!ponder_move) { HashProbe(tree, 0, 0, wtm, dalpha, dbeta, &value); if (tree->hash_move[0]) ponder_move = tree->hash_move[0]; if (ponder_move) { if (!VerifyMove(tree, 1, wtm, ponder_move)) { Print(4095, "ERROR. ponder_move is illegal (2).\n"); Print(4095, "ERROR. move=%d %x\n", ponder_move, ponder_move); ponder_move = 0; } } } /* ************************************************************ * * * Second attempt. If that didn't work, then we try what * * I call a "puzzling" search. Which is simply a shorter * * time-limit search for the other side, to find something * * to ponder. * * * ************************************************************ */ if (!ponder_move) { TimeSet(puzzle); if (time_limit < 20) return 0; puzzling = 1; tree->status[1] = tree->status[0]; Print(32, " puzzling over a move to ponder.\n"); last_pv.pathl = 0; last_pv.pathd = 0; for (i = 0; i < MAXPLY; i++) { tree->killers[i].move1 = 0; tree->killers[i].move2 = 0; } Iterate(wtm, puzzle, 0); for (i = 0; i < MAXPLY; i++) { tree->killers[i].move1 = 0; tree->killers[i].move2 = 0; } puzzling = 0; if (tree->pv[0].pathl) ponder_move = tree->pv[0].path[1]; if (!ponder_move) return 0; for (i = 1; i < (int) tree->pv[0].pathl; i++) last_pv.path[i] = tree->pv[0].path[i + 1]; last_pv.pathl = tree->pv[0].pathl - 1; last_pv.pathd = 0; if (!VerifyMove(tree, 1, wtm, ponder_move)) { ponder_move = 0; Print(4095, "ERROR. ponder_move is illegal (3).\n"); Print(4095, "ERROR. PV pathl=%d\n", last_pv.pathl); return 0; } } /* ************************************************************ * * * Display the move we are going to "ponder". * * * ************************************************************ */ if (wtm) Print(32, "White(%d): %s [pondering]\n", move_number, OutputMove(tree, 0, wtm, ponder_move)); else Print(32, "Black(%d): %s [pondering]\n", move_number, OutputMove(tree, 0, wtm, ponder_move)); sprintf(ponder_text, "%s", OutputMove(tree, 0, wtm, ponder_move)); if (post) printf("Hint: %s\n", ponder_text); /* ************************************************************ * * * Set the ponder move list and eliminate illegal moves. * * This list is used to test the move entered while we are * * pondering, since we need a move list for the input * * screening process. * * * ************************************************************ */ n_ponder_moves = GenerateCaptures(tree, 0, wtm, ponder_moves); num_ponder_moves = GenerateNoncaptures(tree, 0, wtm, n_ponder_moves) - ponder_moves; for (mv = ponder_moves; mv < ponder_moves + num_ponder_moves; mv++) { MakeMove(tree, 0, wtm, *mv); if (Check(wtm)) { UnmakeMove(tree, 0, wtm, *mv); *mv = 0; } else UnmakeMove(tree, 0, wtm, *mv); } /* ************************************************************ * * * Now, perform an iterated search, but with the special * * "pondering" flag set which changes the time controls * * since there is no need to stop searching until the * * opponent makes a move. * * * ************************************************************ */ MakeMove(tree, 0, wtm, ponder_move); tree->curmv[0] = ponder_move; tree->rep_list[++rep_index] = HashKey; tlom = last_opponent_move; last_opponent_move = ponder_move; if (kibitz) strcpy(kibitz_text, "n/a"); thinking = 0; pondering = 1; if (!wtm) move_number++; ponder_value = Iterate(Flip(wtm), think, 0); rep_index--; move_number = save_move_number; pondering = 0; thinking = 0; last_opponent_move = tlom; UnmakeMove(tree, 0, wtm, ponder_move); /* ************************************************************ * * * Search completed. the possible return values are: * * * * (0) No pondering was done, period. * * * * (1) Pondering was done, opponent made the predicted * * move, and we searched until time ran out in a * * normal manner. * * * * (2) Pondering was done, but the ponder search * * terminated due to either finding a mate, or the * * maximum search depth was reached. The result of * * this ponder search are valid, but only if the * * opponent makes the correct (predicted) move. * * * * (3) Pondering was done, but the opponent either made a * * different move, or entered a command that has to * * interrupt the pondering search before the command * * (or move) can be processed. This forces Main() to * * avoid reading in a move/command since one has been * * read into the command buffer already. * * * ************************************************************ */ if (input_status == 1) return 1; if (input_status == 2) return 3; return 2; }
/* ******************************************************************************** * * * "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; }