Rev 108 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 33 | pmbaty | 1 | #include "chess.h" | 
| 2 | #include "data.h" | ||
| 3 | /* last modified 02/26/14 */ | ||
| 4 | /* | ||
| 5 |  ******************************************************************************* | ||
| 6 |  *                                                                             * | ||
| 7 |  *   "annotate" command is used to search through the game in a pgn file, and  * | ||
| 8 |  *   provide a qualitative analysis of each move played and then creating a    * | ||
| 9 |  *   new output file (xxx.can) containing the original game + new commentary.  * | ||
| 10 |  *                                                                             * | ||
| 11 |  *   The normal output of this command is a file, in PGN format, that contains * | ||
| 12 |  *   the moves of the game, along with analysis when Crafty does not think     * | ||
| 13 |  *   that move was the best choice.  The definition of "best choice" is        * | ||
| 14 |  *   somewhat vague, because if the move played is "close" to the best move    * | ||
| 15 |  *   available, Crafty will not comment on the move.  "Close" is defined by    * | ||
| 16 |  *   the <margin> option explained below.  This basic type of annotation works * | ||
| 17 |  *   by first using the normal tree search algorithm to find the best move.    * | ||
| 18 |  *   If this move was the move played, no output is produced.  If a different  * | ||
| 19 |  *   move is considered best, then the actual move played is searched to the   * | ||
| 20 |  *   same depth and if the best move and actual move scores are within         * | ||
| 21 |  *   <margin> of each other, no comment is produced, otherwise Crafty inserts  * | ||
| 22 |  *   the evaluation for the move played, followed by the eval and PV for the   * | ||
| 23 |  *   best continuation it found.  You can enter suggested moves for Crafty to  * | ||
| 24 |  *   analyze at any point by simply entering a move as an analysis-type        * | ||
| 25 |  *   comment using (move) or {move}.  Crafty will search that move in addition * | ||
| 26 |  *   to the move actually played and the move it thinks is best.               * | ||
| 27 |  *                                                                             * | ||
| 28 |  *   The format of the command is as follows:                                  * | ||
| 29 |  *                                                                             * | ||
| 108 | pmbaty | 30 |  *        annotate filename b|w|bw|name moves margin time [n]                  * | 
| 33 | pmbaty | 31 |  *                                                                             * | 
| 32 |  *   Filename is the input file where Crafty will obtain the moves to          * | ||
| 33 |  *   annotate, and output will be written to file "filename.can".              * | ||
| 34 |  *                                                                             * | ||
| 108 | pmbaty | 35 |  *        annotateh filename b|w|bw|name moves margin time [n]                 * | 
| 33 | pmbaty | 36 |  *                                                                             * | 
| 37 |  *   Can be used to produce an HTML-compatible file that includes bitmapped    * | ||
| 38 |  *   diagrams of the positions where Crafty provides analysis.  This file can  * | ||
| 39 |  *   be opened by a browser to provide much easier 'reading'.                  * | ||
| 40 |  *                                                                             * | ||
| 108 | pmbaty | 41 |  *        annotatet filename b|w|bw|name moves margin time [n]                 * | 
| 33 | pmbaty | 42 |  *                                                                             * | 
| 43 |  *   Can be used to produce a LaTeX-compatible file that includes LaTeX chess  * | ||
| 44 |  *   fonts.  This file can be read/printed by any program that can handle      * | ||
| 45 |  *   LaTeX input.                                                              * | ||
| 46 |  *                                                                             * | ||
| 47 |  *   Where b/w/bw indicates whether to annotate only the white side (w), the   * | ||
| 48 |  *   black side (b) or both (bw).  You can also specify a name (or part of a   * | ||
| 49 |  *   name, just be sure it is unique in the name tags for clarity in who you   * | ||
| 50 |  *   mean).                                                                    * | ||
| 51 |  *                                                                             * | ||
| 52 |  *   Moves indicates the move or moves to annotate.  It can be a single move,  * | ||
| 53 |  *   which indicates the starting move number to annotate, or it can be a      * | ||
| 54 |  *   range, which indicates a range of move (1-999 gets the whole game.)       * | ||
| 55 |  *                                                                             * | ||
| 56 |  *   Margin is the difference between Crafty's evaluation for the move         * | ||
| 57 |  *   actually played and for the move Crafty thinks is best, before Crafty     * | ||
| 58 |  *   will generate a comment in the annotation file.  1.0 is a pawn, and will  * | ||
| 59 |  *   only generate comments if the move played is 1.000 (1 pawn) worse than    * | ||
| 60 |  *   the best move found by doing a complete search.                           * | ||
| 61 |  *                                                                             * | ||
| 62 |  *   Time is time per move to search, in seconds.                              * | ||
| 63 |  *                                                                             * | ||
| 64 |  *   [n] is optional and tells Crafty to produce the PV/score for the "n" best * | ||
| 65 |  *   moves.  Crafty stops when the best move reaches the move played in the    * | ||
| 66 |  *   game or after displaying n moves, whichever comes first.  If you use -n,  * | ||
| 67 |  *   then it will display n moves regardless of where the game move ranks.     * | ||
| 68 |  *                                                                             * | ||
| 69 |  ******************************************************************************* | ||
| 70 |  */ | ||
| 71 | #define MIN_DECISIVE_ADV 150 | ||
| 72 | #define MIN_MODERATE_ADV  70 | ||
| 73 | #define MIN_SLIGHT_ADV    30 | ||
| 74 | void Annotate() { | ||
| 75 | FILE *annotate_in, *annotate_out; | ||
| 76 | char text[128], tbuffer[4096], colors[32] = { "" }, pname[128] = { | ||
| 77 | ""}; | ||
| 78 | int annotate_margin, annotate_score[100], player_score, best_moves, | ||
| 79 |       annotate_wtm; | ||
| 80 | int annotate_search_time_limit, search_player; | ||
| 81 | int twtm, path_len, analysis_printed = 0; | ||
| 82 | int wtm, move_num, line1, line2, move, suggested, i; | ||
| 83 | int searches_done, read_status; | ||
| 84 | PATH temp[100], player_pv; | ||
| 85 | int temp_search_depth; | ||
| 86 | TREE *const tree = block[0]; | ||
| 87 | char html_br[5] = { "" }; | ||
| 88 | int save_swindle_mode; | ||
| 89 | int html_mode = 0; | ||
| 90 | int latex = 0; | ||
| 91 | |||
| 92 | /* | ||
| 93 |  ************************************************************ | ||
| 94 |  *                                                          * | ||
| 95 |  *  First, extract the options from the command line to     * | ||
| 96 |  *  determine what the user wanted us to do.                * | ||
| 97 |  *                                                          * | ||
| 98 |  ************************************************************ | ||
| 99 |  */ | ||
| 100 | save_swindle_mode = swindle_mode; | ||
| 101 | if (!strcmp(args[0], "annotateh")) { | ||
| 102 | html_mode = 1; | ||
| 103 | strcpy(html_br, "<br>"); | ||
| 104 |   } | ||
| 105 | if (!strcmp(args[0], "annotatet")) { | ||
| 106 | latex = 1; | ||
| 107 | strcpy(html_br, "\\\\"); | ||
| 108 |   } | ||
| 109 | strcpy(tbuffer, buffer); | ||
| 108 | pmbaty | 110 | nargs = ReadParse(tbuffer, args, " \t;"); | 
| 33 | pmbaty | 111 | if (nargs < 6) { | 
| 112 | printf | ||
| 113 | ("usage: annotate <file> <color> <moves> <margin> <time> [nmoves]\n"); | ||
| 114 | return; | ||
| 115 |   } | ||
| 116 | annotate_in = fopen(args[1], "r"); | ||
| 117 | if (annotate_in == NULL) { | ||
| 118 | Print(4095, "unable to open %s for input\n", args[1]); | ||
| 119 | return; | ||
| 120 |   } | ||
| 108 | pmbaty | 121 | nargs = ReadParse(tbuffer, args, " \t;"); | 
| 33 | pmbaty | 122 | strcpy(text, args[1]); | 
| 123 | if (html_mode == 1) | ||
| 124 | strcpy(text + strlen(text), ".html"); | ||
| 125 | else if (latex == 1) | ||
| 126 | strcpy(text + strlen(text), ".tex"); | ||
| 127 |   else | ||
| 128 | strcpy(text + strlen(text), ".can"); | ||
| 129 | annotate_out = fopen(text, "w"); | ||
| 130 | if (annotate_out == NULL) { | ||
| 131 | Print(4095, "unable to open %s for output\n", text); | ||
| 132 | return; | ||
| 133 |   } | ||
| 134 | if (html_mode == 1) | ||
| 135 | AnnotateHeaderHTML(text, annotate_out); | ||
| 136 | if (latex == 1) | ||
| 137 | AnnotateHeaderTeX(annotate_out); | ||
| 138 | if (strlen(args[2]) <= 2) | ||
| 139 | strcpy(colors, args[2]); | ||
| 140 |   else | ||
| 141 | strcpy(pname, args[2]); | ||
| 142 | line1 = 1; | ||
| 143 | line2 = 999; | ||
| 144 | if (strchr(args[3], 'b')) | ||
| 145 | line2 = -1; | ||
| 146 | if (strchr(args[3], '-')) | ||
| 147 | sscanf(args[3], "%d-%d", &line1, &line2); | ||
| 148 | else { | ||
| 149 | sscanf(args[3], "%d", &line1); | ||
| 150 | line2 = 999; | ||
| 151 |   } | ||
| 154 | pmbaty | 152 | annotate_margin = atof(args[4]) * PieceValues(white, pawn); | 
| 153 | annotate_search_time_limit = atof(args[5]) * 100; | ||
| 33 | pmbaty | 154 | if (nargs > 6) | 
| 155 | best_moves = atoi(args[6]); | ||
| 156 |   else | ||
| 157 | best_moves = 1; | ||
| 158 | /* | ||
| 159 |  ************************************************************ | ||
| 160 |  *                                                          * | ||
| 161 |  *  Reset the game to "square 0" to start the annotation    * | ||
| 162 |  *  procedure.  Then we read moves from the input file,     * | ||
| 163 |  *  make them on the game board, and annotate if the move   * | ||
| 164 |  *  is for the correct side.  If we haven't yet reached the * | ||
| 165 |  *  starting move to annotate, we skip the Search() stuff   * | ||
| 166 |  *   and read another move.                                 * | ||
| 167 |  *                                                          * | ||
| 168 |  ************************************************************ | ||
| 169 |  */ | ||
| 170 | annotate_mode = 1; | ||
| 171 | swindle_mode = 0; | ||
| 172 | ponder = 0; | ||
| 173 | temp_search_depth = search_depth; | ||
| 174 | read_status = ReadPGN(0, 0); | ||
| 175 | read_status = ReadPGN(annotate_in, 0); | ||
| 176 | player_pv.path[1] = 0; | ||
| 177 | while (read_status != -1) { | ||
| 178 | ponder_move = 0; | ||
| 179 | last_pv.pathd = 0; | ||
| 180 | last_pv.pathl = 0; | ||
| 181 | player_pv.pathd = 0; | ||
| 182 | player_pv.pathl = 0; | ||
| 183 | tree->pv[0].pathl = 0; | ||
| 184 | tree->pv[0].pathd = 0; | ||
| 185 | analysis_printed = 0; | ||
| 186 | InitializeChessBoard(tree); | ||
| 187 | tree->status[1] = tree->status[0]; | ||
| 188 | wtm = 1; | ||
| 189 | move_number = 1; | ||
| 190 | /* | ||
| 191 |  ************************************************************ | ||
| 192 |  *                                                          * | ||
| 193 |  *  Now grab the PGN tag values so they can be copied to    * | ||
| 194 |  *  the .can file for reference.                            * | ||
| 195 |  *                                                          * | ||
| 196 |  ************************************************************ | ||
| 197 |  */ | ||
| 198 |     do | ||
| 199 | read_status = ReadPGN(annotate_in, 0); | ||
| 200 | while (read_status == 1); | ||
| 201 | if (read_status == -1) | ||
| 202 | break; | ||
| 203 | if (latex == 0) { | ||
| 204 | fprintf(annotate_out, "[Event \"%s\"]%s\n", pgn_event, html_br); | ||
| 205 | fprintf(annotate_out, "[Site \"%s\"]%s\n", pgn_site, html_br); | ||
| 206 | fprintf(annotate_out, "[Date \"%s\"]%s\n", pgn_date, html_br); | ||
| 207 | fprintf(annotate_out, "[Round \"%s\"]%s\n", pgn_round, html_br); | ||
| 208 | fprintf(annotate_out, "[White \"%s\"]%s\n", pgn_white, html_br); | ||
| 209 | fprintf(annotate_out, "[WhiteElo \"%s\"]%s\n", pgn_white_elo, html_br); | ||
| 210 | fprintf(annotate_out, "[Black \"%s\"]%s\n", pgn_black, html_br); | ||
| 211 | fprintf(annotate_out, "[BlackElo \"%s\"]%s\n", pgn_black_elo, html_br); | ||
| 212 | fprintf(annotate_out, "[Result \"%s\"]%s\n", pgn_result, html_br); | ||
| 213 | fprintf(annotate_out, "[Annotator \"Crafty v%s\"]%s\n", version, | ||
| 214 | html_br); | ||
| 215 | if (strlen(colors) != 0) { | ||
| 216 | if (!strcmp(colors, "bw") || !strcmp(colors, "wb")) | ||
| 217 | fprintf(annotate_out, | ||
| 218 | "{annotating both black and white moves.}%s\n", html_br); | ||
| 219 | else if (strchr(colors, 'b')) | ||
| 220 | fprintf(annotate_out, "{annotating only black moves.}%s\n", | ||
| 221 | html_br); | ||
| 222 | else if (strchr(colors, 'w')) | ||
| 223 | fprintf(annotate_out, "{annotating only white moves.}%s\n", | ||
| 224 | html_br); | ||
| 225 | } else | ||
| 226 | fprintf(annotate_out, "{annotating for player %s}%s\n", pname, | ||
| 227 | html_br); | ||
| 228 | fprintf(annotate_out, "{using a scoring margin of %s pawns.}%s\n", | ||
| 229 | DisplayEvaluationKibitz(annotate_margin, wtm), html_br); | ||
| 230 | fprintf(annotate_out, "{search time limit is %s}%s\n%s\n", | ||
| 231 | DisplayTimeKibitz(annotate_search_time_limit), html_br, html_br); | ||
| 232 | } else { | ||
| 233 | fprintf(annotate_out, "\\textbf{\\sc %s %s -- %s %s}%s\n", pgn_white, | ||
| 234 | pgn_white_elo, pgn_black, pgn_black_elo, html_br); | ||
| 235 | fprintf(annotate_out, "{\\em %s, %s}%s\n", pgn_site, pgn_date, html_br); | ||
| 236 | fprintf(annotate_out, "{\\small %s, Round: %s}%s\n", pgn_event, | ||
| 237 | pgn_round, html_br); | ||
| 238 | fprintf(annotate_out, "\\begin{mainline}{%s}{Crafty v%s}\n", pgn_result, | ||
| 239 | version); | ||
| 240 |     } | ||
| 241 | if (strlen(colors)) { | ||
| 242 | if (!strcmp(colors, "w")) | ||
| 243 | annotate_wtm = 1; | ||
| 244 | else if (!strcmp(colors, "b")) | ||
| 245 | annotate_wtm = 0; | ||
| 246 | else if (!strcmp(colors, "wb")) | ||
| 247 | annotate_wtm = 2; | ||
| 248 | else if (!strcmp(colors, "bw")) | ||
| 249 | annotate_wtm = 2; | ||
| 250 | else { | ||
| 251 | Print(4095, "invalid color specification, retry\n"); | ||
| 252 | fclose(annotate_out); | ||
| 253 | return; | ||
| 254 |       } | ||
| 255 | } else { | ||
| 256 | if (strstr(pgn_white, pname)) | ||
| 257 | annotate_wtm = 1; | ||
| 258 | else if (strstr(pgn_black, pname)) | ||
| 259 | annotate_wtm = 0; | ||
| 260 | else { | ||
| 261 | Print(4095, "Player name doesn't match any PGN name tag, retry\n"); | ||
| 262 | fclose(annotate_out); | ||
| 263 | return; | ||
| 264 |       } | ||
| 265 |     } | ||
| 154 | pmbaty | 266 | while (FOREVER) { | 
| 33 | pmbaty | 267 | fflush(annotate_out); | 
| 268 | move = ReadNextMove(tree, buffer, 0, wtm); | ||
| 269 | if (move <= 0) | ||
| 270 | break; | ||
| 108 | pmbaty | 271 | strcpy(text, OutputMove(tree, 0, wtm, move)); | 
| 33 | pmbaty | 272 | if (history_file) { | 
| 273 | fseek(history_file, ((move_number - 1) * 2 + 1 - wtm) * 10, SEEK_SET); | ||
| 274 | fprintf(history_file, "%9s\n", text); | ||
| 275 |       } | ||
| 276 | if (wtm) | ||
| 277 | Print(4095, "White(%d): %s\n", move_number, text); | ||
| 278 |       else | ||
| 279 | Print(4095, "Black(%d): %s\n", move_number, text); | ||
| 280 | if (analysis_printed) | ||
| 281 | fprintf(annotate_out, "%3d.%s%8s\n", move_number, | ||
| 282 | (wtm ? "" : " ..."), text); | ||
| 283 | else { | ||
| 284 | if (wtm) | ||
| 285 | fprintf(annotate_out, "%3d.%8s", move_number, text); | ||
| 286 |         else | ||
| 287 | fprintf(annotate_out, "%8s\n", text); | ||
| 288 |       } | ||
| 289 | analysis_printed = 0; | ||
| 290 | if (move_number >= line1 && move_number <= line2) { | ||
| 291 | if (annotate_wtm == 2 || annotate_wtm == wtm) { | ||
| 292 | last_pv.pathd = 0; | ||
| 293 | last_pv.pathl = 0; | ||
| 294 | thinking = 1; | ||
| 295 | RootMoveList(wtm); | ||
| 296 | /* | ||
| 297 |  ************************************************************ | ||
| 298 |  *                                                          * | ||
| 299 |  *  Search the position to see if the move played is the    * | ||
| 300 |  *  best move possible.  If not, then search just the move  * | ||
| 301 |  *  played to get a score for it as well, so we can         * | ||
| 302 |  *  determine if annotated output is appropriate.           * | ||
| 303 |  *                                                          * | ||
| 304 |  ************************************************************ | ||
| 305 |  */ | ||
| 306 | search_time_limit = annotate_search_time_limit; | ||
| 307 | search_depth = temp_search_depth; | ||
| 308 | player_score = -999999; | ||
| 309 | search_player = 1; | ||
| 310 | for (searches_done = 0; searches_done < Abs(best_moves); | ||
| 311 | searches_done++) { | ||
| 312 | if (searches_done > 0) { | ||
| 313 | search_time_limit = 3 * annotate_search_time_limit; | ||
| 314 | search_depth = temp[0].pathd; | ||
| 315 |             } | ||
| 316 | Print(4095, "\n Searching all legal moves."); | ||
| 317 | Print(4095, "----------------------------------\n"); | ||
| 318 | tree->status[1] = tree->status[0]; | ||
| 108 | pmbaty | 319 | InitializeHashTables(0); | 
| 33 | pmbaty | 320 | annotate_score[searches_done] = Iterate(wtm, annotate, 1); | 
| 321 | if (tree->pv[0].path[1] == move) { | ||
| 322 | player_score = annotate_score[searches_done]; | ||
| 323 | player_pv = tree->pv[0]; | ||
| 324 | search_player = 0; | ||
| 325 |             } | ||
| 326 | temp[searches_done] = tree->pv[0]; | ||
| 327 | for (i = 0; i < n_root_moves; i++) { | ||
| 328 | if (root_moves[i].move == tree->pv[0].path[1]) { | ||
| 329 | for (; i < n_root_moves; i++) | ||
| 330 | root_moves[i] = root_moves[i + 1]; | ||
| 331 |                 n_root_moves--; | ||
| 332 | break; | ||
| 333 |               } | ||
| 334 |             } | ||
| 335 | if (n_root_moves == 0 || (annotate_margin >= 0 && | ||
| 336 | player_score + annotate_margin > | ||
| 337 | annotate_score[searches_done] | ||
| 338 | && best_moves > 0)) { | ||
| 339 | if (n_root_moves == 0) | ||
| 340 |                 searches_done++; | ||
| 341 | break; | ||
| 342 |             } | ||
| 343 |           } | ||
| 344 | if (search_player) { | ||
| 345 | Print(4095, | ||
| 346 | "\n Searching only the move played in game."); | ||
| 347 | Print(4095, "--------------------\n"); | ||
| 348 | tree->status[1] = tree->status[0]; | ||
| 349 | search_move = move; | ||
| 350 | root_moves[0].move = move; | ||
| 351 | root_moves[0].status = 0; | ||
| 352 | n_root_moves = 1; | ||
| 353 | search_time_limit = 3 * annotate_search_time_limit; | ||
| 354 | search_depth = temp[0].pathd; | ||
| 355 | if (search_depth == temp_search_depth) | ||
| 356 | search_time_limit = annotate_search_time_limit; | ||
| 108 | pmbaty | 357 | InitializeHashTables(0); | 
| 33 | pmbaty | 358 | player_score = Iterate(wtm, annotate, 1); | 
| 359 | player_pv = tree->pv[0]; | ||
| 360 | search_depth = temp_search_depth; | ||
| 361 | search_time_limit = annotate_search_time_limit; | ||
| 362 | search_move = 0; | ||
| 363 |           } | ||
| 364 | /* | ||
| 365 |  ************************************************************ | ||
| 366 |  *                                                          * | ||
| 367 |  *  Output the score/pv for the move played unless it       * | ||
| 368 |  *  matches what Crafty would have played.  If it doesn't   * | ||
| 369 |  *  then output the pv for what Crafty thinks is best.      * | ||
| 370 |  *                                                          * | ||
| 371 |  ************************************************************ | ||
| 372 |  */ | ||
| 373 | thinking = 0; | ||
| 374 | if (player_pv.pathd > 1 && player_pv.pathl >= 1 && | ||
| 375 | player_score + annotate_margin < annotate_score[0] | ||
| 376 | && (temp[0].path[1] != player_pv.path[1] | ||
| 377 | || annotate_margin < 0 || best_moves != 1)) { | ||
| 378 | if (wtm) { | ||
| 379 | analysis_printed = 1; | ||
| 380 | fprintf(annotate_out, "%s\n", html_br); | ||
| 381 |             } | ||
| 382 | if (html_mode == 1) | ||
| 383 | AnnotatePositionHTML(tree, wtm, annotate_out); | ||
| 384 | if (latex == 1) { | ||
| 385 | AnnotatePositionTeX(tree, wtm, annotate_out); | ||
| 386 | fprintf(annotate_out, " \\begin{variation}\\{%d:%s\\}", | ||
| 387 | player_pv.pathd, DisplayEvaluationKibitz(player_score, | ||
| 388 | wtm)); | ||
| 389 | } else | ||
| 390 | fprintf(annotate_out, " ({%d:%s}", | ||
| 391 | player_pv.pathd, DisplayEvaluationKibitz(player_score, | ||
| 392 | wtm)); | ||
| 393 | path_len = player_pv.pathl; | ||
| 394 | fprintf(annotate_out, " %s", FormatPV(tree, wtm, player_pv)); | ||
| 395 | if (latex == 1) | ||
| 396 | fprintf(annotate_out, " %s\n \\end{variation}\n", | ||
| 397 | AnnotateVtoNAG(player_score, wtm, html_mode, latex)); | ||
| 398 |             else | ||
| 399 | fprintf(annotate_out, " %s)%s\n", AnnotateVtoNAG(player_score, | ||
| 400 | wtm, html_mode, latex), html_br); | ||
| 401 | for (move_num = 0; move_num < searches_done; move_num++) { | ||
| 402 | if (move != temp[move_num].path[1]) { | ||
| 403 | if (latex == 1) | ||
| 404 | fprintf(annotate_out, " \\begin{variation}\\{%d:%s\\}", | ||
| 405 | temp[move_num].pathd, | ||
| 406 | DisplayEvaluationKibitz(annotate_score[move_num], wtm)); | ||
| 407 |                 else | ||
| 408 | fprintf(annotate_out, " ({%d:%s}", | ||
| 409 | temp[move_num].pathd, | ||
| 410 | DisplayEvaluationKibitz(annotate_score[move_num], wtm)); | ||
| 411 | path_len = temp[move_num].pathl; | ||
| 412 | fprintf(annotate_out, " %s", FormatPV(tree, wtm, | ||
| 413 | temp[move_num])); | ||
| 414 | if (latex == 1) | ||
| 415 | fprintf(annotate_out, " %s\n \\end{variation}\n", | ||
| 416 | AnnotateVtoNAG(annotate_score[move_num], wtm, html_mode, | ||
| 417 | latex)); | ||
| 418 |                 else | ||
| 419 | fprintf(annotate_out, " %s)%s\n", | ||
| 420 | AnnotateVtoNAG(annotate_score[move_num], wtm, html_mode, | ||
| 421 | latex), html_br); | ||
| 422 |               } | ||
| 423 |             } | ||
| 424 | if (html_mode == 1) | ||
| 425 | fprintf(annotate_out, "<br>\n"); | ||
| 426 | if (line2 < 0) | ||
| 427 |               line2--; | ||
| 428 |           } | ||
| 429 |         } | ||
| 430 |       } | ||
| 431 | /* | ||
| 432 |  ************************************************************ | ||
| 433 |  *                                                          * | ||
| 434 |  *  Before going on to the next move, see if the user has   * | ||
| 435 |  *  included a set of other moves that require a search.    * | ||
| 436 |  *  If so, search them one at a time and produce the ana-   * | ||
| 437 |  *  lysis for each one.                                     * | ||
| 438 |  *                                                          * | ||
| 439 |  ************************************************************ | ||
| 440 |  */ | ||
| 441 | read_status = ReadPGN(annotate_in, 1); | ||
| 442 | while (read_status == 2) { | ||
| 108 | pmbaty | 443 | suggested = InputMove(tree, 0, wtm, 1, 0, buffer); | 
| 33 | pmbaty | 444 | if (suggested > 0) { | 
| 445 | thinking = 1; | ||
| 446 | Print(4095, "\n Searching only the move suggested."); | ||
| 447 | Print(4095, "--------------------\n"); | ||
| 448 | tree->status[1] = tree->status[0]; | ||
| 449 | search_move = suggested; | ||
| 450 | search_time_limit = 3 * annotate_search_time_limit; | ||
| 451 | search_depth = temp[0].pathd; | ||
| 108 | pmbaty | 452 | InitializeHashTables(0); | 
| 33 | pmbaty | 453 | annotate_score[0] = Iterate(wtm, annotate, 0); | 
| 454 | search_depth = temp_search_depth; | ||
| 455 | search_time_limit = annotate_search_time_limit; | ||
| 456 | search_move = 0; | ||
| 457 | thinking = 0; | ||
| 458 | twtm = wtm; | ||
| 459 | path_len = tree->pv[0].pathl; | ||
| 460 | if (tree->pv[0].pathd > 1 && path_len >= 1) { | ||
| 461 | if (wtm && !analysis_printed) { | ||
| 462 | analysis_printed = 1; | ||
| 463 | fprintf(annotate_out, "%s\n", html_br); | ||
| 464 |             } | ||
| 465 | fprintf(annotate_out, " ({suggested %d:%s}", | ||
| 466 | tree->pv[0].pathd, DisplayEvaluationKibitz(annotate_score[0], | ||
| 467 | wtm)); | ||
| 468 | for (i = 1; i <= path_len; i++) { | ||
| 108 | pmbaty | 469 | fprintf(annotate_out, " %s", OutputMove(tree, i, twtm, | 
| 470 | tree->pv[0].path[i])); | ||
| 471 | MakeMove(tree, i, twtm, tree->pv[0].path[i]); | ||
| 33 | pmbaty | 472 | twtm = Flip(twtm); | 
| 473 |             } | ||
| 474 | for (i = path_len; i > 0; i--) { | ||
| 475 | twtm = Flip(twtm); | ||
| 108 | pmbaty | 476 | UnmakeMove(tree, i, twtm, tree->pv[0].path[i]); | 
| 33 | pmbaty | 477 |             } | 
| 478 | fprintf(annotate_out, " %s)%s\n", | ||
| 479 | AnnotateVtoNAG(annotate_score[0], wtm, html_mode, latex), | ||
| 480 | html_br); | ||
| 481 |           } | ||
| 482 |         } | ||
| 483 | read_status = ReadPGN(annotate_in, 1); | ||
| 484 | if (read_status != 2) | ||
| 485 | break; | ||
| 486 |       } | ||
| 487 | if ((analysis_printed) && (latex == 0)) | ||
| 488 | fprintf(annotate_out, "%s\n", html_br); | ||
| 108 | pmbaty | 489 | MakeMoveRoot(tree, wtm, move); | 
| 33 | pmbaty | 490 | wtm = Flip(wtm); | 
| 491 | if (wtm) | ||
| 492 |         move_number++; | ||
| 493 | if (read_status != 0) | ||
| 494 | break; | ||
| 495 | if (line2 < -1) | ||
| 496 | break; | ||
| 154 | pmbaty | 497 |     } | 
| 108 | pmbaty | 498 | fprintf(annotate_out, " %s %s\n\n", pgn_result, html_br); | 
| 33 | pmbaty | 499 | if (html_mode == 1) { | 
| 500 | fprintf(annotate_out, "%s\n", html_br); | ||
| 501 | AnnotateFooterHTML(annotate_out); | ||
| 502 |     } | ||
| 503 | if (latex == 1) { | ||
| 504 | AnnotatePositionTeX(tree, wtm, annotate_out); | ||
| 505 | fprintf(annotate_out, "\\end{mainline}\n"); | ||
| 506 | if (strlen(colors) != 0) { | ||
| 507 | fprintf(annotate_out, "\\begin{flushright}{\\small "); | ||
| 508 | if (!strcmp(colors, "bw") || !strcmp(colors, "wb")) | ||
| 509 | fprintf(annotate_out, "annotating both black and white moves.%s\n", | ||
| 510 | html_br); | ||
| 511 | else if (strchr(colors, 'b')) | ||
| 512 | fprintf(annotate_out, "annotating only black moves.%s\n", html_br); | ||
| 513 | else if (strchr(colors, 'w')) | ||
| 514 | fprintf(annotate_out, "annotating only white moves.%s\n", html_br); | ||
| 515 | } else | ||
| 516 | fprintf(annotate_out, "annotating for player %s%s\n", pname, html_br); | ||
| 517 | fprintf(annotate_out, "using a scoring margin of %s pawns.%s\n", | ||
| 518 | DisplayEvaluationKibitz(annotate_margin, wtm), html_br); | ||
| 519 | fprintf(annotate_out, "search time limit is %s%s\n", | ||
| 520 | DisplayTimeKibitz(annotate_search_time_limit), html_br); | ||
| 521 | fprintf(annotate_out, " } \\end{flushright}"); | ||
| 522 | AnnotateFooterTeX(annotate_out); | ||
| 523 |     } | ||
| 524 |   } | ||
| 525 | if (annotate_out) | ||
| 526 | fclose(annotate_out); | ||
| 527 | if (annotate_in) | ||
| 528 | fclose(annotate_in); | ||
| 529 | search_time_limit = 0; | ||
| 530 | annotate_mode = 0; | ||
| 531 | swindle_mode = save_swindle_mode; | ||
| 532 | } | ||
| 533 | |||
| 534 | /* | ||
| 535 |  ******************************************************************************* | ||
| 536 |  *                                                                             * | ||
| 537 |  *   These functions provide HTML output support interfaces.                   * | ||
| 538 |  *                                                                             * | ||
| 539 |  ******************************************************************************* | ||
| 540 |  */ | ||
| 541 | void AnnotateHeaderHTML(char *title_text, FILE * annotate_out) { | ||
| 542 | fprintf(annotate_out, | ||
| 543 | "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"\n"); | ||
| 544 | fprintf(annotate_out, | ||
| 545 | " \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"); | ||
| 546 | fprintf(annotate_out, "<HTML>\n"); | ||
| 547 | fprintf(annotate_out, "<HEAD><TITLE>%s</TITLE>\n", title_text); | ||
| 548 | fprintf(annotate_out, | ||
| 549 | "<LINK rev=\"made\" href=\"hyatt@cis.uab.edu\"></HEAD>\n"); | ||
| 550 | fprintf(annotate_out, | ||
| 551 |       "<BODY BGColor=\"#ffffff\" text=\"#000000\" link=\"#0000ee\"" | ||
| 552 | " vlink=\"#551a8b\">\n"); | ||
| 553 | } | ||
| 554 | |||
| 555 | void AnnotateFooterHTML(FILE * annotate_out) { | ||
| 556 | fprintf(annotate_out, "</BODY>\n"); | ||
| 557 | fprintf(annotate_out, "</HTML>\n"); | ||
| 558 | } | ||
| 559 | void AnnotatePositionHTML(TREE * RESTRICT tree, int wtm, FILE * annotate_out) { | ||
| 560 | char filename[32], html_piece; | ||
| 561 | char alt[32]; | ||
| 562 | int rank, file; | ||
| 563 | |||
| 564 | /*  Display the board in HTML using table of images.          */ | ||
| 565 | fprintf(annotate_out, "<br>\n"); | ||
| 566 | fprintf(annotate_out, "<TABLE Border=1 CellSpacing=0 CellPadding=0>\n\n"); | ||
| 567 | for (rank = RANK8; rank >= RANK1; rank--) { | ||
| 568 | fprintf(annotate_out, "<TR>\n"); | ||
| 569 | for (file = FILEA; file <= FILEH; file++) { | ||
| 108 | pmbaty | 570 | strcpy(filename, "bitmaps/"); | 
| 33 | pmbaty | 571 | if ((rank + file) % 2) | 
| 108 | pmbaty | 572 | strcat(filename, "w"); | 
| 33 | pmbaty | 573 |       else | 
| 108 | pmbaty | 574 | strcat(filename, "b"); | 
| 33 | pmbaty | 575 | html_piece = translate[PcOnSq((rank << 3) + file) + 6]; | 
| 576 | switch (html_piece) { | ||
| 577 | case 'p': | ||
| 108 | pmbaty | 578 | strcat(filename, "bp"); | 
| 579 | strcpy(alt, "*P"); | ||
| 33 | pmbaty | 580 | break; | 
| 581 | case 'r': | ||
| 108 | pmbaty | 582 | strcat(filename, "br"); | 
| 583 | strcpy(alt, "*R"); | ||
| 33 | pmbaty | 584 | break; | 
| 585 | case 'n': | ||
| 108 | pmbaty | 586 | strcat(filename, "bn"); | 
| 587 | strcpy(alt, "*N"); | ||
| 33 | pmbaty | 588 | break; | 
| 589 | case 'b': | ||
| 108 | pmbaty | 590 | strcat(filename, "bb"); | 
| 591 | strcpy(alt, "*B"); | ||
| 33 | pmbaty | 592 | break; | 
| 593 | case 'q': | ||
| 108 | pmbaty | 594 | strcat(filename, "bq"); | 
| 595 | strcpy(alt, "*Q"); | ||
| 33 | pmbaty | 596 | break; | 
| 597 | case 'k': | ||
| 108 | pmbaty | 598 | strcat(filename, "bk"); | 
| 599 | strcpy(alt, "*K"); | ||
| 33 | pmbaty | 600 | break; | 
| 601 | case 'P': | ||
| 108 | pmbaty | 602 | strcat(filename, "wp"); | 
| 603 | strcpy(alt, "P"); | ||
| 33 | pmbaty | 604 | break; | 
| 605 | case 'R': | ||
| 108 | pmbaty | 606 | strcat(filename, "wr"); | 
| 607 | strcpy(alt, "R"); | ||
| 33 | pmbaty | 608 | break; | 
| 609 | case 'N': | ||
| 108 | pmbaty | 610 | strcat(filename, "wn"); | 
| 611 | strcpy(alt, "N"); | ||
| 33 | pmbaty | 612 | break; | 
| 613 | case 'B': | ||
| 108 | pmbaty | 614 | strcat(filename, "wb"); | 
| 615 | strcpy(alt, "B"); | ||
| 33 | pmbaty | 616 | break; | 
| 617 | case 'Q': | ||
| 108 | pmbaty | 618 | strcat(filename, "wq"); | 
| 619 | strcpy(alt, "Q"); | ||
| 33 | pmbaty | 620 | break; | 
| 621 | case 'K': | ||
| 108 | pmbaty | 622 | strcat(filename, "wk"); | 
| 623 | strcpy(alt, "K"); | ||
| 33 | pmbaty | 624 | break; | 
| 625 | default: | ||
| 108 | pmbaty | 626 | strcat(filename, "sq"); | 
| 627 | strcpy(alt, " "); | ||
| 33 | pmbaty | 628 | break; | 
| 629 |       } | ||
| 108 | pmbaty | 630 | strcat(filename, ".gif"); | 
| 33 | pmbaty | 631 | fprintf(annotate_out, "<TD><IMG ALT=\"%s\" SRC=\"%s\"></TD>\n", alt, | 
| 632 | filename); | ||
| 633 |     } | ||
| 634 | fprintf(annotate_out, "</TR>\n\n"); | ||
| 635 |   } | ||
| 636 | fprintf(annotate_out, "</TABLE>\n"); | ||
| 637 | if (wtm) | ||
| 638 | fprintf(annotate_out, "<H2>White to move.</H2>\n"); | ||
| 639 |   else | ||
| 640 | fprintf(annotate_out, "<H2>Black to move.</H2>\n"); | ||
| 641 | fprintf(annotate_out, "<BR>\n"); | ||
| 642 | } | ||
| 643 | |||
| 644 | /* | ||
| 645 |  ******************************************************************************* | ||
| 646 |  * Author         : Alexander Wagner                                           * | ||
| 108 | pmbaty | 647 |  *                  University of Michigan                                     * | 
| 33 | pmbaty | 648 |  * Date           : 03.01.04                                                   * | 
| 649 |  *                                                                             * | ||
| 650 |  * Last Modified  : 03.01.04                                                   * | ||
| 651 |  *                                                                             * | ||
| 652 |  * Based upon the HTML-Code above                                              * | ||
| 653 |  *                                                                             * | ||
| 654 |  * These functions provide LaTeX output capability to Crafty.                  * | ||
| 655 |  *                                                                             * | ||
| 656 |  ******************************************************************************* | ||
| 657 |  */ | ||
| 658 | void AnnotateHeaderTeX(FILE * annotate_out) { | ||
| 659 | fprintf(annotate_out, "\\documentclass[12pt,twocolumn]{article}\n"); | ||
| 660 | fprintf(annotate_out, "%% This is a LaTeX file generated by Crafty \n"); | ||
| 661 | fprintf(annotate_out, | ||
| 662 | "%% You must have the \"chess12\" package to typeset this file.\n"); | ||
| 663 | fprintf(annotate_out, "\n"); | ||
| 664 | fprintf(annotate_out, "\\usepackage{times}\n"); | ||
| 665 | fprintf(annotate_out, "\\usepackage{a4wide}\n"); | ||
| 666 | fprintf(annotate_out, "\\usepackage{chess}\n"); | ||
| 667 | fprintf(annotate_out, "\\usepackage{bdfchess}\n"); | ||
| 668 | fprintf(annotate_out, "\\usepackage[T1]{fontenc}\n"); | ||
| 669 | fprintf(annotate_out, "\n"); | ||
| 670 | fprintf(annotate_out, "\\setlength{\\columnsep}{7mm}\n"); | ||
| 671 | fprintf(annotate_out, "\\setlength{\\parindent}{0pt}\n"); | ||
| 672 | fprintf(annotate_out, "\n"); | ||
| 673 | fprintf(annotate_out, "%% Macros for variations and diagrams:\n"); | ||
| 674 | fprintf(annotate_out, | ||
| 675 | "\\newenvironment{mainline}[2]{\\bf\\newcommand{\\result}{#1}%%\n"); | ||
| 676 | fprintf(annotate_out, "\\newcommand{\\commentator}{#2}\\begin{chess}}%%\n"); | ||
| 677 | fprintf(annotate_out, "{\\end{chess}\\finito{\\result}{\\commentator}}\n"); | ||
| 678 | fprintf(annotate_out, | ||
| 679 | "\\newenvironment{variation}{[\\begingroup\\rm\\ignorespaces}%%\n"); | ||
| 680 | fprintf(annotate_out, "{\\endgroup]\\ignorespaces\\newline}\n"); | ||
| 681 | fprintf(annotate_out, | ||
| 682 | "\\newcommand{\\finito}[2]{{\\bf\\hfill#1\\hfill[#2]\\par}}\n"); | ||
| 683 | fprintf(annotate_out, "\\setlength{\\parindent}{0pt}\n"); | ||
| 684 | fprintf(annotate_out, | ||
| 685 |       "\\newenvironment{diagram}{\\begin{nochess}}" | ||
| 686 | "{$$\\showboard$$\\end{nochess}}\n"); | ||
| 687 | fprintf(annotate_out, "\n\n\\begin{document}\n\n"); | ||
| 688 | } | ||
| 689 | |||
| 690 | void AnnotateFooterTeX(FILE * annotate_out) { | ||
| 691 | fprintf(annotate_out, "\n\n\\end{document}\n"); | ||
| 692 | } | ||
| 693 | void AnnotatePositionTeX(TREE * tree, int wtm, FILE * annotate_out) { | ||
| 694 | char filename[32], html_piece; | ||
| 695 | int rank, file; | ||
| 696 | |||
| 697 | /*  Display the board in LaTeX using picture notation, similar to html */ | ||
| 698 | fprintf(annotate_out, "\\begin{diagram}\n\\board\n"); | ||
| 699 | for (rank = RANK8; rank >= RANK1; rank--) { | ||
| 700 | fprintf(annotate_out, " {"); | ||
| 701 | for (file = FILEA; file <= FILEH; file++) { | ||
| 702 | if ((rank + file) % 2) | ||
| 108 | pmbaty | 703 | strcpy(filename, " "); | 
| 33 | pmbaty | 704 |       else | 
| 108 | pmbaty | 705 | strcpy(filename, "*"); | 
| 33 | pmbaty | 706 | html_piece = translate[PcOnSq((rank << 3) + file) + 6]; | 
| 707 | switch (html_piece) { | ||
| 708 | case 'p': | ||
| 709 | strcpy(filename, "p"); | ||
| 710 | break; | ||
| 711 | case 'r': | ||
| 712 | strcpy(filename, "r"); | ||
| 713 | break; | ||
| 714 | case 'n': | ||
| 715 | strcpy(filename, "n"); | ||
| 716 | break; | ||
| 717 | case 'b': | ||
| 718 | strcpy(filename, "b"); | ||
| 719 | break; | ||
| 720 | case 'q': | ||
| 721 | strcpy(filename, "q"); | ||
| 722 | break; | ||
| 723 | case 'k': | ||
| 724 | strcpy(filename, "k"); | ||
| 725 | break; | ||
| 726 | case 'P': | ||
| 727 | strcpy(filename, "P"); | ||
| 728 | break; | ||
| 729 | case 'R': | ||
| 730 | strcpy(filename, "R"); | ||
| 731 | break; | ||
| 732 | case 'N': | ||
| 733 | strcpy(filename, "N"); | ||
| 734 | break; | ||
| 735 | case 'B': | ||
| 736 | strcpy(filename, "B"); | ||
| 737 | break; | ||
| 738 | case 'Q': | ||
| 739 | strcpy(filename, "Q"); | ||
| 740 | break; | ||
| 741 | case 'K': | ||
| 742 | strcpy(filename, "K"); | ||
| 743 | break; | ||
| 744 | default: | ||
| 745 | break; | ||
| 746 |       } | ||
| 747 | fprintf(annotate_out, "%s", filename); | ||
| 748 |     } | ||
| 749 | fprintf(annotate_out, "}\n"); | ||
| 750 |   } | ||
| 751 | fprintf(annotate_out, "\\end{diagram}\n"); | ||
| 752 | fprintf(annotate_out, "\\begin{center} \\begin{nochess}\n {\\small "); | ||
| 753 | if (wtm) | ||
| 754 | fprintf(annotate_out, "White to move.\n"); | ||
| 755 |   else | ||
| 756 | fprintf(annotate_out, "Black to move.\n"); | ||
| 757 | fprintf(annotate_out, "}\n \\end{nochess}\\end{center} \n\n"); | ||
| 758 | fprintf(annotate_out, "\n"); | ||
| 759 | } | ||
| 760 | char *AnnotateVtoNAG(int value, int wtm, int html_mode, int latex) { | ||
| 761 | static char buf[64]; | ||
| 762 | |||
| 763 | if (!wtm) | ||
| 764 | value = -value; | ||
| 765 | if (value > MIN_DECISIVE_ADV) | ||
| 766 | strcpy(buf, html_mode ? "+-" : "$18"); | ||
| 767 | else if (value > MIN_MODERATE_ADV) | ||
| 768 | strcpy(buf, html_mode ? "+/-" : "$16"); | ||
| 769 | else if (value > MIN_SLIGHT_ADV) | ||
| 770 | strcpy(buf, html_mode ? "+=" : "$14"); | ||
| 771 | else if (value < -MIN_DECISIVE_ADV) | ||
| 772 | strcpy(buf, html_mode ? "-+" : "$19"); | ||
| 773 | else if (value < -MIN_MODERATE_ADV) | ||
| 774 | strcpy(buf, html_mode ? "-/+" : "$17"); | ||
| 775 | else if (value < -MIN_SLIGHT_ADV) | ||
| 776 | strcpy(buf, html_mode ? "=+" : "$15"); | ||
| 777 |   else | ||
| 778 | strcpy(buf, html_mode ? "=" : "$10"); | ||
| 779 | if (latex == 1) { | ||
| 780 | if (value > MIN_DECISIVE_ADV) | ||
| 781 | strcpy(buf, "\\wdecisive"); | ||
| 782 | else if (value > MIN_MODERATE_ADV) | ||
| 783 | strcpy(buf, "\\wupperhand"); | ||
| 784 | else if (value > MIN_SLIGHT_ADV) | ||
| 785 | strcpy(buf, "\\wbetter"); | ||
| 786 | else if (value < -MIN_DECISIVE_ADV) | ||
| 787 | strcpy(buf, "\\bdecisive"); | ||
| 788 | else if (value < -MIN_MODERATE_ADV) | ||
| 789 | strcpy(buf, "\\bupperhand"); | ||
| 790 | else if (value < -MIN_SLIGHT_ADV) | ||
| 791 | strcpy(buf, "\\bbetter"); | ||
| 792 |     else | ||
| 793 | strcpy(buf, "\\equal"); | ||
| 794 |   } | ||
| 795 | return buf; | ||
| 796 | } |