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 | } |