Rev 108 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
33 | pmbaty | 1 | #include <math.h> |
2 | #include "chess.h" |
||
3 | #include "data.h" |
||
4 | #if defined(UNIX) |
||
5 | # include <unistd.h> |
||
6 | #endif |
||
7 | /* last modified 05/08/14 */ |
||
8 | /* |
||
9 | ******************************************************************************* |
||
10 | * * |
||
11 | * Book() is used to determine if the current position is in the book data- * |
||
12 | * base. It simply takes the set of moves produced by root_moves() and then * |
||
13 | * tries each position's hash key to see if it can be found in the data- * |
||
14 | * base. If so, such a move represents a "book move." The set of flags is * |
||
15 | * used to decide on a sub-set of moves to be used as the "book move pool" * |
||
16 | * from which a move is chosen randomly. * |
||
17 | * * |
||
18 | * The format of a book position is as follows: * |
||
19 | * * |
||
20 | * 64 bits: hash key for this position. * |
||
21 | * * |
||
22 | * 8 bits: flag bits defined as follows: * |
||
23 | * * |
||
24 | * 0000 0001 ?? flagged move (0x01) * |
||
25 | * 0000 0010 ? flagged move (0x02) * |
||
26 | * 0000 0100 = flagged move (0x04) * |
||
27 | * 0000 1000 ! flagged move (0x08) * |
||
28 | * 0001 0000 !! flagged move (0x10) * |
||
29 | * 0010 0000 black won at least 1 game (0x20) * |
||
30 | * 0100 0000 at least one game was drawn (0x40) * |
||
31 | * 1000 0000 white won at least 1 game (0x80) * |
||
32 | * * |
||
33 | * 24 bits: number of games this move was played. * |
||
34 | * * |
||
35 | * 32 bits: learned value (floating point). * |
||
36 | * * |
||
37 | * (Note: counts are normalized to a max of 255. * |
||
38 | * * |
||
39 | ******************************************************************************* |
||
40 | */ |
||
41 | #define BAD_MOVE 0x02 |
||
42 | #define GOOD_MOVE 0x08 |
||
43 | int Book(TREE * RESTRICT tree, int wtm, int root_list_done) { |
||
44 | static int book_moves[200]; |
||
45 | static BOOK_POSITION start_moves[200]; |
||
46 | static uint64_t selected_key[200]; |
||
47 | static int selected[200]; |
||
48 | static int selected_order_played[200]; |
||
49 | static float selected_value[200]; // Pierre-Marie Baty -- fixed type |
||
50 | static int selected_status[200], selected_percent[200], |
||
51 | book_development[200]; |
||
52 | static int bs_played[200], bs_percent[200]; |
||
53 | static int book_status[200], evaluations[200], bs_learn[200]; |
||
54 | static float bs_value[200], total_value; |
||
55 | static uint64_t book_key[200], bs_key[200]; |
||
56 | int m1_status, forced = 0, total_percent, play_percentage = 0; |
||
57 | float tempr; |
||
58 | int done, i, j, last_move, temp, which, minlv = 999999, maxlv = -999999; |
||
59 | int maxp = -999999, minev = 999999, maxev = -999999; |
||
60 | int nflagged, im, value, np, book_ponder_move; |
||
61 | int cluster, scluster, test; |
||
62 | unsigned char buf32[4]; |
||
63 | uint64_t temp_hash_key, common, tempk; |
||
64 | int key, nmoves, num_selected, st; |
||
65 | int percent_played, total_played, total_moves, smoves; |
||
66 | int distribution; |
||
67 | int initial_development; |
||
68 | char *kibitz_p; |
||
69 | |||
70 | /* |
||
71 | ************************************************************ |
||
72 | * * |
||
73 | * If we have been out of book for several moves, return * |
||
74 | * and start the normal tree search. * |
||
75 | * * |
||
76 | ************************************************************ |
||
77 | */ |
||
78 | if (moves_out_of_book > 6) |
||
79 | return 0; |
||
80 | /* |
||
81 | ************************************************************ |
||
82 | * * |
||
83 | * Position is known, read the start book file and save * |
||
84 | * each move found. These will be used later to augment * |
||
85 | * the flags in the normal book to offer better control. * |
||
86 | * * |
||
87 | ************************************************************ |
||
88 | */ |
||
89 | if (!root_list_done) |
||
90 | RootMoveList(wtm); |
||
91 | test = HashKey >> 49; |
||
92 | smoves = 0; |
||
93 | if (books_file) { |
||
94 | fseek(books_file, test * sizeof(int), SEEK_SET); |
||
95 | fread(buf32, 4, 1, books_file); |
||
96 | key = BookIn32(buf32); |
||
97 | if (key > 0) { |
||
98 | fseek(books_file, key, SEEK_SET); |
||
99 | fread(buf32, 4, 1, books_file); |
||
100 | scluster = BookIn32(buf32); |
||
101 | BookClusterIn(books_file, scluster, book_buffer); |
||
102 | for (im = 0; im < n_root_moves; im++) { |
||
103 | common = HashKey & ((uint64_t) 65535 << 48); |
||
104 | MakeMove(tree, 1, root_moves[im].move, wtm); |
||
105 | if (Repeat(tree, 2)) { |
||
106 | UnmakeMove(tree, 1, root_moves[im].move, wtm); |
||
107 | return 0; |
||
108 | } |
||
109 | temp_hash_key = (wtm) ? HashKey : ~HashKey; |
||
110 | temp_hash_key = (temp_hash_key & ~((uint64_t) 65535 << 48)) | common; |
||
111 | for (i = 0; i < scluster; i++) |
||
112 | if (!(temp_hash_key ^ book_buffer[i].position)) { |
||
113 | start_moves[smoves++] = book_buffer[i]; |
||
114 | break; |
||
115 | } |
||
116 | UnmakeMove(tree, 1, root_moves[im].move, wtm); |
||
117 | } |
||
118 | } |
||
119 | } |
||
120 | /* |
||
121 | ************************************************************ |
||
122 | * * |
||
123 | * Position is known, read in the appropriate cluster. * |
||
124 | * Note that this cluster will have all possible book * |
||
125 | * moves from current position in it (as well as others of * |
||
126 | * course.) * |
||
127 | * * |
||
128 | ************************************************************ |
||
129 | */ |
||
130 | test = HashKey >> 49; |
||
131 | if (book_file) { |
||
132 | fseek(book_file, test * sizeof(int), SEEK_SET); |
||
133 | fread(buf32, 4, 1, book_file); |
||
134 | key = BookIn32(buf32); |
||
135 | if (key > 0) { |
||
136 | book_learn_seekto = key; |
||
137 | fseek(book_file, key, SEEK_SET); |
||
138 | fread(buf32, 4, 1, book_file); |
||
139 | cluster = BookIn32(buf32); |
||
140 | BookClusterIn(book_file, cluster, book_buffer); |
||
141 | } else |
||
142 | cluster = 0; |
||
143 | if (!cluster && !smoves) |
||
144 | return 0; |
||
145 | /* |
||
146 | ************************************************************ |
||
147 | * * |
||
148 | * Now add any moves from books.bin to the end of the * |
||
149 | * cluster so that they will be played even if not in the * |
||
150 | * regular database of moves. * |
||
151 | * * |
||
152 | ************************************************************ |
||
153 | */ |
||
154 | for (i = 0; i < smoves; i++) { |
||
155 | for (j = 0; j < cluster; j++) |
||
156 | if (!(book_buffer[j].position ^ start_moves[i].position)) |
||
157 | break; |
||
158 | if (j >= cluster) { |
||
159 | book_buffer[cluster] = start_moves[i]; |
||
160 | book_buffer[cluster].status_played = |
||
161 | book_buffer[cluster].status_played & 037700000000; |
||
162 | cluster++; |
||
163 | } |
||
164 | } |
||
165 | /* |
||
166 | ************************************************************ |
||
167 | * * |
||
168 | * First cycle through the root move list, make each move, * |
||
169 | * and see if the resulting hash key is in the book * |
||
170 | * database. * |
||
171 | * * |
||
172 | ************************************************************ |
||
173 | */ |
||
174 | initial_development = tree->score_mg; |
||
175 | EvaluateDevelopment(tree, 1, wtm); |
||
176 | initial_development = tree->score_mg - initial_development; |
||
177 | total_moves = 0; |
||
178 | nmoves = 0; |
||
179 | for (im = 0; im < n_root_moves; im++) { |
||
180 | common = HashKey & ((uint64_t) 65535 << 48); |
||
181 | MakeMove(tree, 1, root_moves[im].move, wtm); |
||
182 | if (Repeat(tree, 2)) { |
||
183 | UnmakeMove(tree, 1, root_moves[im].move, wtm); |
||
184 | return 0; |
||
185 | } |
||
186 | temp_hash_key = (wtm) ? HashKey : ~HashKey; |
||
187 | temp_hash_key = (temp_hash_key & ~((uint64_t) 65535 << 48)) | common; |
||
188 | for (i = 0; i < cluster; i++) { |
||
189 | if (!(temp_hash_key ^ book_buffer[i].position)) { |
||
190 | book_status[nmoves] = book_buffer[i].status_played >> 24; |
||
191 | bs_played[nmoves] = book_buffer[i].status_played & 077777777; |
||
192 | bs_learn[nmoves] = (int) (book_buffer[i].learn * 100.0); |
||
193 | if (puzzling) |
||
194 | bs_played[nmoves] += 1; |
||
195 | tree->curmv[1] = root_moves[im].move; |
||
196 | if (!Captured(root_moves[im].move)) { |
||
197 | book_development[nmoves] = tree->score_mg; |
||
198 | EvaluateDevelopment(tree, 2, wtm); |
||
199 | book_development[nmoves] = |
||
200 | tree->score_mg - book_development[nmoves]; |
||
201 | } else |
||
202 | book_development[nmoves] = 0; |
||
203 | total_moves += bs_played[nmoves]; |
||
204 | evaluations[nmoves] = Evaluate(tree, 2, wtm, -99999, 99999); |
||
205 | evaluations[nmoves] -= MaterialSTM(wtm); |
||
206 | bs_percent[nmoves] = 0; |
||
207 | for (j = 0; j < smoves; j++) { |
||
208 | if (!(book_buffer[i].position ^ start_moves[j].position)) { |
||
209 | book_status[nmoves] |= start_moves[j].status_played >> 24; |
||
210 | bs_percent[nmoves] = start_moves[j].status_played & 077777777; |
||
211 | break; |
||
212 | } |
||
213 | } |
||
214 | book_moves[nmoves] = root_moves[im].move; |
||
215 | book_key[nmoves] = temp_hash_key; |
||
216 | nmoves++; |
||
217 | break; |
||
218 | } |
||
219 | } |
||
220 | UnmakeMove(tree, 1, root_moves[im].move, wtm); |
||
221 | } |
||
222 | if (!nmoves) |
||
223 | return 0; |
||
224 | book_learn_nmoves = nmoves; |
||
225 | /* |
||
226 | ************************************************************ |
||
227 | * * |
||
228 | * If any moves have a very bad or a very good learn * |
||
229 | * value, set the appropriate ? or ! flag so the move be * |
||
230 | * played or avoided as appropriate. * |
||
231 | * * |
||
232 | ************************************************************ |
||
233 | */ |
||
234 | for (i = 0; i < nmoves; i++) |
||
235 | if (!(book_status[i] & BAD_MOVE)) |
||
236 | maxp = Max(maxp, bs_played[i]); |
||
237 | for (i = 0; i < nmoves; i++) { |
||
238 | if (bs_learn[i] <= LEARN_COUNTER_BAD && !bs_percent[i] |
||
239 | && !(book_status[i] & 0x18)) |
||
240 | book_status[i] |= BAD_MOVE; |
||
241 | if (wtm && !(book_status[i] & 0x80) && !bs_percent[i] |
||
242 | && !(book_status[i] & 0x18)) |
||
243 | book_status[i] |= BAD_MOVE; |
||
244 | if (!wtm && !(book_status[i] & 0x20) && !bs_percent[i] |
||
245 | && !(book_status[i] & 0x18)) |
||
246 | book_status[i] |= BAD_MOVE; |
||
247 | if (bs_played[i] < maxp / 10 && !bs_percent[i] && book_random && |
||
248 | !(book_status[i] & 0x18)) |
||
249 | book_status[i] |= BAD_MOVE; |
||
250 | if (bs_learn[i] >= LEARN_COUNTER_GOOD && !(book_status[i] & 0x03)) |
||
251 | book_status[i] |= GOOD_MOVE; |
||
252 | if (bs_percent[i]) |
||
253 | book_status[i] |= GOOD_MOVE; |
||
254 | } |
||
255 | /* |
||
256 | ************************************************************ |
||
257 | * * |
||
258 | * We have the book moves, now it's time to decide how * |
||
259 | * they are supposed to be sorted and compute the sort * |
||
260 | * index. * |
||
261 | * * |
||
262 | ************************************************************ |
||
263 | */ |
||
264 | for (i = 0; i < nmoves; i++) { |
||
265 | if (!(book_status[i] & BAD_MOVE)) { |
||
266 | minlv = Min(minlv, bs_learn[i]); |
||
267 | maxlv = Max(maxlv, bs_learn[i]); |
||
268 | minev = Min(minev, evaluations[i]); |
||
269 | maxev = Max(maxev, evaluations[i]); |
||
270 | maxp = Max(maxp, bs_played[i]); |
||
271 | } |
||
272 | } |
||
273 | maxp++; |
||
274 | for (i = 0; i < nmoves; i++) { |
||
275 | bs_value[i] = 1; |
||
276 | bs_value[i] += bs_played[i] / (float) maxp * 1000.0f * book_weight_freq; // Pierre-Marie Baty -- added float suffix |
||
277 | |||
278 | if (minlv < maxlv) |
||
279 | bs_value[i] += |
||
280 | (bs_learn[i] - minlv) / (float) (maxlv - |
||
281 | minlv) * 1000.0f * book_weight_learn; // Pierre-Marie Baty -- added float suffix |
||
282 | if (minev < maxev) |
||
283 | bs_value[i] += |
||
284 | (evaluations[i] - minev) / (float) (Max(maxev - minev, |
||
285 | 50)) * 1000.0f * book_weight_eval; // Pierre-Marie Baty -- added float suffix |
||
286 | } |
||
287 | total_played = total_moves; |
||
288 | /* |
||
289 | ************************************************************ |
||
290 | * * |
||
291 | * If there are any ! moves, make their popularity count * |
||
292 | * huge since they have to be considered. * |
||
293 | * * |
||
294 | ************************************************************ |
||
295 | */ |
||
296 | for (i = 0; i < nmoves; i++) |
||
297 | if (book_status[i] & 0x18) |
||
298 | break; |
||
299 | if (i < nmoves) { |
||
300 | for (i = 0; i < nmoves; i++) { |
||
301 | if (book_status[i] & 0x18) |
||
302 | bs_value[i] += 8000.0; |
||
303 | if (!(book_status[i] & 0x03)) |
||
304 | bs_value[i] += 4000.0; |
||
305 | } |
||
306 | } |
||
307 | /* |
||
308 | ************************************************************ |
||
309 | * * |
||
310 | * Now sort the moves based on the complete sort value. * |
||
311 | * * |
||
312 | ************************************************************ |
||
313 | */ |
||
314 | if (nmoves) |
||
315 | do { |
||
316 | done = 1; |
||
317 | for (i = 0; i < nmoves - 1; i++) { |
||
318 | if (bs_percent[i] < bs_percent[i + 1] |
||
319 | || (bs_percent[i] == bs_percent[i + 1] |
||
320 | && bs_value[i] < bs_value[i + 1])) { |
||
321 | temp = bs_played[i]; // Pierre-Marie Baty -- the wrong variable type was used |
||
322 | bs_played[i] = bs_played[i + 1]; |
||
323 | bs_played[i + 1] = temp; |
||
324 | tempr = bs_value[i]; |
||
325 | bs_value[i] = bs_value[i + 1]; |
||
326 | bs_value[i + 1] = tempr; |
||
327 | temp = evaluations[i]; |
||
328 | evaluations[i] = evaluations[i + 1]; |
||
329 | evaluations[i + 1] = temp; |
||
330 | temp = bs_learn[i]; |
||
331 | bs_learn[i] = bs_learn[i + 1]; |
||
332 | bs_learn[i + 1] = temp; |
||
333 | temp = book_development[i]; |
||
334 | book_development[i] = book_development[i + 1]; |
||
335 | book_development[i + 1] = temp; |
||
336 | temp = book_moves[i]; |
||
337 | book_moves[i] = book_moves[i + 1]; |
||
338 | book_moves[i + 1] = temp; |
||
339 | temp = book_status[i]; |
||
340 | book_status[i] = book_status[i + 1]; |
||
341 | book_status[i + 1] = temp; |
||
342 | temp = bs_percent[i]; |
||
343 | bs_percent[i] = bs_percent[i + 1]; |
||
344 | bs_percent[i + 1] = temp; |
||
345 | tempk = book_key[i]; |
||
346 | book_key[i] = book_key[i + 1]; |
||
347 | book_key[i + 1] = tempk; |
||
348 | done = 0; |
||
349 | } |
||
350 | } |
||
351 | } while (!done); |
||
352 | /* |
||
353 | ************************************************************ |
||
354 | * * |
||
355 | * Display the book moves, and total counts, etc. if the * |
||
356 | * operator has requested it. * |
||
357 | * * |
||
358 | ************************************************************ |
||
359 | */ |
||
360 | if (show_book) { |
||
361 | Print(128, " after screening, the following moves can be played\n"); |
||
362 | Print(128, |
||
363 | " move played %% score learn " "sortv P%% P\n"); |
||
364 | for (i = 0; i < nmoves; i++) { |
||
365 | Print(128, "%6s", OutputMove(tree, book_moves[i], 1, wtm)); |
||
366 | st = book_status[i]; |
||
367 | if (st & 0x1f) { |
||
368 | if (st & 0x01) |
||
369 | Print(128, "??"); |
||
370 | else if (st & 0x02) |
||
371 | Print(128, "? "); |
||
372 | else if (st & 0x04) |
||
373 | Print(128, "= "); |
||
374 | else if (st & 0x08) |
||
375 | Print(128, "! "); |
||
376 | else if (st & 0x10) |
||
377 | Print(128, "!!"); |
||
378 | } else |
||
379 | Print(128, " "); |
||
380 | Print(128, " %6d", bs_played[i]); |
||
381 | Print(128, " %3d", 100 * bs_played[i] / Max(total_moves, 1)); |
||
382 | Print(128, "%s", DisplayEvaluation(evaluations[i], wtm)); |
||
383 | Print(128, "%9.2f", (float) bs_learn[i] / 100.0); |
||
384 | Print(128, " %9.1f", bs_value[i]); |
||
385 | Print(128, " %3d", bs_percent[i]); |
||
386 | if ((book_status[i] & book_accept_mask && |
||
387 | !(book_status[i] & book_reject_mask)) |
||
388 | || (!(book_status[i] & book_reject_mask) && (bs_percent[i] |
||
389 | || book_status[i] & 0x18 || (wtm && book_status[i] & 0x80) |
||
390 | || (!wtm && book_status[i] & 0x20)))) |
||
391 | Print(128, " Y"); |
||
392 | else |
||
393 | Print(128, " N"); |
||
394 | Print(128, "\n"); |
||
395 | } |
||
396 | } |
||
397 | /* |
||
398 | ************************************************************ |
||
399 | * * |
||
400 | * Check for book moves with the play % value set. if * |
||
401 | * there are any such moves, then exclude all moves that * |
||
402 | * do not have a play % or a !/!! flag set. * |
||
403 | * * |
||
404 | ************************************************************ |
||
405 | */ |
||
406 | for (i = 0; i < nmoves; i++) |
||
407 | if (bs_percent[i]) |
||
408 | play_percentage = 1; |
||
409 | /* |
||
410 | ************************************************************ |
||
411 | * * |
||
412 | * Delete ? and ?? moves first, which includes those moves * |
||
413 | * with bad learned results. Here is where we also * |
||
414 | * exclude moves with no play % if we find at least one * |
||
415 | * with a non-zero value. * |
||
416 | * * |
||
417 | ************************************************************ |
||
418 | */ |
||
419 | num_selected = 0; |
||
420 | if (!play_percentage) { |
||
421 | for (i = 0; i < nmoves; i++) |
||
422 | if (!(book_status[i] & 0x03) || bs_percent[i]) { |
||
423 | selected_status[num_selected] = book_status[i]; |
||
424 | selected_order_played[num_selected] = bs_played[i]; |
||
425 | selected_value[num_selected] = bs_value[i]; |
||
426 | selected_percent[num_selected] = bs_percent[i]; |
||
427 | selected_key[num_selected] = book_key[i]; |
||
428 | selected[num_selected++] = book_moves[i]; |
||
429 | } |
||
430 | } else { |
||
431 | for (i = 0; i < nmoves; i++) |
||
432 | if (bs_percent[i]) { |
||
433 | selected_status[num_selected] = book_status[i]; |
||
434 | selected_order_played[num_selected] = bs_played[i]; |
||
435 | selected_value[num_selected] = bs_value[i]; |
||
436 | selected_percent[num_selected] = bs_percent[i]; |
||
437 | selected_key[num_selected] = book_key[i]; |
||
438 | selected[num_selected++] = book_moves[i]; |
||
439 | } |
||
440 | } |
||
441 | for (i = 0; i < num_selected; i++) { |
||
442 | book_status[i] = selected_status[i]; |
||
443 | bs_played[i] = selected_order_played[i]; |
||
444 | bs_value[i] = selected_value[i]; |
||
445 | bs_percent[i] = selected_percent[i]; |
||
446 | book_moves[i] = selected[i]; |
||
447 | } |
||
448 | nmoves = num_selected; |
||
449 | /* |
||
450 | ************************************************************ |
||
451 | * * |
||
452 | * If this is a real search (not a puzzling search to * |
||
453 | * find a move by the opponent to ponder) then we need to * |
||
454 | * set up the whisper info for later. * |
||
455 | * * |
||
456 | ************************************************************ |
||
457 | */ |
||
458 | if (!puzzling) |
||
459 | do { |
||
460 | kibitz_text[0] = '\0'; |
||
461 | if (!nmoves) |
||
462 | break; |
||
463 | sprintf(kibitz_text, "book moves ("); |
||
464 | kibitz_p = kibitz_text + strlen(kibitz_text); |
||
465 | for (i = 0; i < nmoves; i++) { |
||
466 | sprintf(kibitz_p, "%s %d%%", OutputMove(tree, book_moves[i], 1, |
||
467 | wtm), 100 * bs_played[i] / Max(total_played, 1)); |
||
468 | kibitz_p = kibitz_text + strlen(kibitz_text); |
||
469 | if (i < nmoves - 1) { |
||
470 | sprintf(kibitz_p, ", "); |
||
471 | kibitz_p = kibitz_text + strlen(kibitz_text); |
||
472 | } |
||
473 | } |
||
474 | sprintf(kibitz_p, ")\n"); |
||
475 | } while (0); |
||
476 | /* |
||
477 | ************************************************************ |
||
478 | * * |
||
479 | * Now select a move from the set of moves just found. Do * |
||
480 | * this in three distinct passes: (1) look for !! moves; * |
||
481 | * (2) look for ! moves; (3) look for any other moves. * |
||
482 | * Note: book_accept_mask *should* have a bit set for any * |
||
483 | * move that is selected, including !! and ! type moves so * |
||
484 | * that they *can* be excluded if desired. Note also that * |
||
485 | * book_reject_mask should have ?? and ? set (at a * |
||
486 | * minimum) to exclude these types of moves. * |
||
487 | * * |
||
488 | ************************************************************ |
||
489 | */ |
||
490 | num_selected = 0; |
||
491 | if (!num_selected && !puzzling) |
||
492 | if (book_accept_mask & 16) |
||
493 | for (i = 0; i < nmoves; i++) |
||
494 | if (book_status[i] & 16) { |
||
495 | forced = 1; |
||
496 | selected_status[num_selected] = book_status[i]; |
||
497 | selected_order_played[num_selected] = bs_played[i]; |
||
498 | selected_value[num_selected] = bs_value[i]; |
||
499 | selected_key[num_selected] = book_key[i]; |
||
500 | selected[num_selected++] = book_moves[i]; |
||
501 | } |
||
502 | if (!num_selected && !puzzling) |
||
503 | if (book_accept_mask & 8) |
||
504 | for (i = 0; i < nmoves; i++) |
||
505 | if (book_status[i] & 8) { |
||
506 | forced = 1; |
||
507 | selected_status[num_selected] = book_status[i]; |
||
508 | selected_order_played[num_selected] = bs_played[i]; |
||
509 | selected_value[num_selected] = bs_value[i]; |
||
510 | selected_key[num_selected] = book_key[i]; |
||
511 | selected[num_selected++] = book_moves[i]; |
||
512 | } |
||
513 | if (!num_selected && !puzzling) |
||
514 | if (book_accept_mask & 4) |
||
515 | for (i = 0; i < nmoves; i++) |
||
516 | if (book_status[i] & 4) { |
||
517 | selected_status[num_selected] = book_status[i]; |
||
518 | selected_order_played[num_selected] = bs_played[i]; |
||
519 | selected_value[num_selected] = bs_value[i]; |
||
520 | selected_key[num_selected] = book_key[i]; |
||
521 | selected[num_selected++] = book_moves[i]; |
||
522 | } |
||
523 | if (!num_selected && !puzzling) |
||
524 | for (i = 0; i < nmoves; i++) |
||
525 | if (book_status[i] & book_accept_mask) { |
||
526 | selected_status[num_selected] = book_status[i]; |
||
527 | selected_order_played[num_selected] = bs_played[i]; |
||
528 | selected_value[num_selected] = bs_value[i]; |
||
529 | selected_key[num_selected] = book_key[i]; |
||
530 | selected[num_selected++] = book_moves[i]; |
||
531 | } |
||
532 | if (!num_selected) |
||
533 | for (i = 0; i < nmoves; i++) { |
||
534 | selected_status[num_selected] = book_status[i]; |
||
535 | selected_order_played[num_selected] = bs_played[i]; |
||
536 | selected_value[num_selected] = bs_value[i]; |
||
537 | selected_key[num_selected] = book_key[i]; |
||
538 | selected[num_selected++] = book_moves[i]; |
||
539 | } |
||
540 | if (!num_selected) |
||
541 | return 0; |
||
542 | for (i = 0; i < num_selected; i++) { |
||
543 | book_status[i] = selected_status[i]; |
||
544 | book_moves[i] = selected[i]; |
||
545 | bs_played[i] = selected_order_played[i]; |
||
546 | bs_value[i] = selected_value[i]; |
||
547 | bs_key[i] = selected_key[i]; |
||
548 | } |
||
549 | nmoves = num_selected; |
||
550 | if (nmoves == 0) |
||
551 | return 0; |
||
552 | Print(128, " book moves {"); |
||
553 | for (i = 0; i < nmoves; i++) { |
||
554 | Print(128, "%s", OutputMove(tree, book_moves[i], 1, wtm)); |
||
555 | if (i < nmoves - 1) |
||
556 | Print(128, ", "); |
||
557 | } |
||
558 | Print(128, "}\n"); |
||
559 | nflagged = 0; |
||
560 | for (i = 0; i < nmoves; i++) |
||
561 | if (book_status[i] & 8) |
||
562 | nflagged++; |
||
563 | nmoves = Max(Min(nmoves, book_selection_width), nflagged); |
||
564 | if (show_book) { |
||
565 | Print(128, " moves considered {"); |
||
566 | for (i = 0; i < nmoves; i++) { |
||
567 | Print(128, "%s", OutputMove(tree, book_moves[i], 1, wtm)); |
||
568 | if (i < nmoves - 1) |
||
569 | Print(128, ", "); |
||
570 | } |
||
571 | Print(128, "}\n"); |
||
572 | } |
||
573 | /* |
||
574 | ************************************************************ |
||
575 | * * |
||
576 | * We have the book moves, if any have specified percents * |
||
577 | * for play, then adjust the bs_value[] to reflect this * |
||
578 | * percentage. * |
||
579 | * * |
||
580 | ************************************************************ |
||
581 | */ |
||
582 | total_value = 0.0; |
||
583 | total_percent = 0; |
||
584 | for (i = 0; i < nmoves; i++) { |
||
585 | if (!bs_percent[i]) |
||
586 | total_value += bs_value[i]; |
||
587 | total_percent += bs_percent[i]; |
||
588 | } |
||
589 | if (fabs(total_value) < 0.0001) |
||
590 | total_value = 1000.0; |
||
591 | total_percent = (total_percent > 99) ? 99 : total_percent; |
||
592 | for (i = 0; i < nmoves; i++) |
||
593 | if (bs_percent[i]) |
||
594 | bs_value[i] = |
||
595 | total_value / (1.0f - |
||
596 | (float) total_percent / 100.0f) * (float) bs_percent[i] / 100.0f; // Pierre-Marie Baty -- added float suffixes |
||
597 | /* |
||
598 | ************************************************************ |
||
599 | * * |
||
600 | * Display the book moves, and total counts, etc. if the * |
||
601 | * operator has requested it. * |
||
602 | * * |
||
603 | ************************************************************ |
||
604 | */ |
||
605 | if (show_book) { |
||
606 | Print(128, " move played %% score sortv P%% P\n"); |
||
607 | for (i = 0; i < nmoves; i++) { |
||
608 | Print(128, "%6s", OutputMove(tree, book_moves[i], 1, wtm)); |
||
609 | st = book_status[i]; |
||
610 | if (st & 0x1f) { |
||
611 | if (st & 0x01) |
||
612 | Print(128, "??"); |
||
613 | else if (st & 0x02) |
||
614 | Print(128, "? "); |
||
615 | else if (st & 0x04) |
||
616 | Print(128, "= "); |
||
617 | else if (st & 0x08) |
||
618 | Print(128, "! "); |
||
619 | else if (st & 0x10) |
||
620 | Print(128, "!!"); |
||
621 | } else |
||
622 | Print(128, " "); |
||
623 | Print(128, " %6d", bs_played[i]); |
||
624 | Print(128, " %3d", 100 * bs_played[i] / Max(total_moves, 1)); |
||
625 | Print(128, "%s", DisplayEvaluation(evaluations[i], wtm)); |
||
626 | Print(128, " %9.1f", bs_value[i]); |
||
627 | Print(128, " %3d", bs_percent[i]); |
||
628 | if ((book_status[i] & book_accept_mask && |
||
629 | !(book_status[i] & book_reject_mask)) |
||
630 | || (!(book_status[i] & book_reject_mask) && ((wtm && |
||
631 | book_status[i] & 0x80) || (!wtm && |
||
632 | book_status[i] & 0x20)))) |
||
633 | Print(128, " Y"); |
||
634 | else |
||
635 | Print(128, " N"); |
||
636 | Print(128, "\n"); |
||
637 | } |
||
638 | } |
||
639 | /* |
||
640 | ************************************************************ |
||
641 | * * |
||
642 | * If random=0, then we search the set of legal book moves * |
||
643 | * with the normal search engine (but with a short time * |
||
644 | * limit) to choose among them. * |
||
645 | * * |
||
646 | ************************************************************ |
||
647 | */ |
||
648 | if (nmoves && (!puzzling || mode != tournament_mode)) { |
||
649 | np = bs_played[nmoves - 1]; |
||
650 | if (!puzzling && (!book_random || (mode == tournament_mode && |
||
651 | np < book_search_trigger))) { |
||
652 | if (!forced) { |
||
653 | n_root_moves = nmoves; |
||
654 | for (i = 0; i < n_root_moves; i++) { |
||
655 | root_moves[i].move = book_moves[i]; |
||
656 | root_moves[i].status = 0; |
||
657 | } |
||
658 | last_pv.pathd = 0; |
||
659 | booking = 1; |
||
660 | value = Iterate(wtm, booking, 1); |
||
661 | booking = 0; |
||
662 | if (value < -50) { |
||
663 | last_pv.pathd = 0; |
||
664 | return 0; |
||
665 | } |
||
666 | } else { |
||
667 | tree->pv[1].path[1] = book_moves[0]; |
||
668 | tree->pv[1].pathl = 2; |
||
669 | tree->pv[1].pathd = 0; |
||
670 | } |
||
671 | return 1; |
||
672 | } |
||
673 | } |
||
674 | /* |
||
675 | ************************************************************ |
||
676 | * * |
||
677 | * If puzzling, in tournament mode we try to find the best * |
||
678 | * non-book move, because a book move will produce a quick * |
||
679 | * move anyway. We therefore would rather search for a * |
||
680 | * non-book move, just in case the opponent goes out of * |
||
681 | * book here. * |
||
682 | * * |
||
683 | ************************************************************ |
||
684 | */ |
||
685 | else if (mode == tournament_mode && puzzling) { |
||
686 | RootMoveList(wtm); |
||
687 | for (i = 0; i < n_root_moves; i++) |
||
688 | for (j = 0; j < nmoves; j++) |
||
689 | if (root_moves[i].move == book_moves[j]) |
||
690 | root_moves[i].move = 0; |
||
691 | for (i = 0, j = 0; i < n_root_moves; i++) |
||
692 | if (root_moves[i].move != 0) |
||
693 | root_moves[j++] = root_moves[i]; |
||
694 | n_root_moves = j; |
||
695 | Print(128, " moves considered {only non-book moves}\n"); |
||
696 | nmoves = j; |
||
697 | if (nmoves > 1) { |
||
698 | last_pv.pathd = 0; |
||
699 | booking = 1; |
||
700 | (void) Iterate(wtm, booking, 1); |
||
701 | booking = 0; |
||
702 | } else { |
||
703 | tree->pv[1].path[1] = book_moves[0]; |
||
704 | tree->pv[1].pathl = 2; |
||
705 | tree->pv[1].pathd = 0; |
||
706 | } |
||
707 | return 1; |
||
708 | } |
||
709 | last_move = nmoves; |
||
710 | /* |
||
711 | ************************************************************ |
||
712 | * * |
||
713 | * Compute a random value and use this to generate a book * |
||
714 | * move based on a probability distribution of the number * |
||
715 | * of games won by each book move. * |
||
716 | * * |
||
717 | ************************************************************ |
||
718 | */ |
||
719 | which = Random32(); |
||
720 | j = ReadClock() / 100 % 13; |
||
721 | for (i = 0; i < j; i++) |
||
722 | which = Random32(); |
||
723 | total_moves = 0; |
||
724 | for (i = 0; i < last_move; i++) { |
||
725 | if (bs_percent[0]) |
||
726 | total_moves += (int) bs_value[i]; // Pierre-Marie Baty -- added type cast |
||
727 | else |
||
728 | total_moves += (int) (bs_value[i] * bs_value[i]); // Pierre-Marie Baty -- added type cast |
||
729 | } |
||
730 | distribution = Abs(which) % Max(total_moves, 1); |
||
731 | for (which = 0; which < last_move; which++) { |
||
732 | if (bs_percent[0]) |
||
733 | distribution -= (int) bs_value[which]; // Pierre-Marie Baty -- added type cast |
||
734 | else |
||
735 | distribution -= (int) (bs_value[which] * bs_value[which]); // Pierre-Marie Baty -- added type cast |
||
736 | if (distribution < 0) |
||
737 | break; |
||
738 | } |
||
739 | which = Min(which, last_move - 1); |
||
740 | tree->pv[1].path[1] = book_moves[which]; |
||
741 | percent_played = 100 * bs_played[which] / Max(total_played, 1); |
||
742 | total_played = bs_played[which]; |
||
743 | m1_status = book_status[which]; |
||
744 | tree->pv[1].pathl = 2; |
||
745 | tree->pv[1].pathd = 0; |
||
746 | if (mode != tournament_mode) { |
||
747 | MakeMove(tree, 1, book_moves[which], wtm); |
||
748 | if ((book_ponder_move = BookPonderMove(tree, Flip(wtm)))) { |
||
749 | tree->pv[1].path[2] = book_ponder_move; |
||
750 | tree->pv[1].pathl = 3; |
||
751 | } |
||
752 | UnmakeMove(tree, 1, book_moves[which], wtm); |
||
753 | } |
||
754 | book_learn_key = bs_key[which]; |
||
755 | Print(128, " book 0.0s %3d%% ", percent_played); |
||
756 | Print(128, " %s", OutputMove(tree, tree->pv[1].path[1], 1, wtm)); |
||
757 | st = m1_status & book_accept_mask & (~224); |
||
758 | if (st) { |
||
759 | if (st & 1) |
||
760 | Print(128, "??"); |
||
761 | else if (st & 2) |
||
762 | Print(128, "?"); |
||
763 | else if (st & 4) |
||
764 | Print(128, "="); |
||
765 | else if (st & 8) |
||
766 | Print(128, "!"); |
||
767 | else if (st & 16) |
||
768 | Print(128, "!!"); |
||
769 | } |
||
770 | MakeMove(tree, 1, tree->pv[1].path[1], wtm); |
||
771 | if (tree->pv[1].pathl > 2) |
||
772 | Print(128, " %s", OutputMove(tree, tree->pv[1].path[2], 2, Flip(wtm))); |
||
773 | UnmakeMove(tree, 1, tree->pv[1].path[1], wtm); |
||
774 | Print(128, "\n"); |
||
775 | return 1; |
||
776 | } |
||
777 | return 0; |
||
778 | } |
||
779 | |||
780 | /* last modified 02/23/14 */ |
||
781 | /* |
||
782 | ******************************************************************************* |
||
783 | * * |
||
784 | * BookPonderMove() is used to find a move to ponder, to avoid the overhead * |
||
785 | * of a "puzzling" search early in the game (unless there are no book moves * |
||
786 | * found, of course.) The algorithm is much simpler than the normal book * |
||
787 | * move code... just find the move with the largest frequency counter and * |
||
788 | * assume that will be played. * |
||
789 | * * |
||
790 | ******************************************************************************* |
||
791 | */ |
||
792 | int BookPonderMove(TREE * RESTRICT tree, int wtm) { |
||
793 | uint64_t temp_hash_key, common; |
||
794 | static int book_moves[200]; |
||
795 | int i, key, *lastm, cluster, n_moves, im, played, tplayed; |
||
796 | int book_ponder_move = 0, test; |
||
797 | unsigned char buf32[4]; |
||
798 | |||
799 | /* |
||
800 | ************************************************************ |
||
801 | * * |
||
802 | * position is known, read in the appropriate cluster. * |
||
803 | * note that this cluster will have all possible book * |
||
804 | * moves from current position in it (as well as others of * |
||
805 | * course.) * |
||
806 | * * |
||
807 | ************************************************************ |
||
808 | */ |
||
809 | if (book_file) { |
||
810 | test = HashKey >> 49; |
||
811 | fseek(book_file, test * sizeof(int), SEEK_SET); |
||
812 | fread(buf32, 4, 1, book_file); |
||
813 | key = BookIn32(buf32); |
||
814 | if (key > 0) { |
||
815 | fseek(book_file, key, SEEK_SET); |
||
816 | fread(buf32, 4, 1, book_file); |
||
817 | cluster = BookIn32(buf32); |
||
818 | BookClusterIn(book_file, cluster, book_buffer); |
||
819 | } else |
||
820 | cluster = 0; |
||
821 | if (!cluster) |
||
822 | return 0; |
||
823 | lastm = GenerateCaptures(tree, 2, wtm, book_moves); |
||
824 | lastm = GenerateNoncaptures(tree, 2, wtm, lastm); |
||
825 | n_moves = lastm - book_moves; |
||
826 | /* |
||
827 | ************************************************************ |
||
828 | * * |
||
829 | * First cycle through the root move list, make each move, * |
||
830 | * and see if the resulting hash key is in the book * |
||
831 | * database. * |
||
832 | * * |
||
833 | ************************************************************ |
||
834 | */ |
||
835 | played = -1; |
||
836 | for (im = 0; im < n_moves; im++) { |
||
837 | common = HashKey & ((uint64_t) 65535 << 48); |
||
838 | MakeMove(tree, 2, book_moves[im], wtm); |
||
839 | temp_hash_key = (wtm) ? HashKey : ~HashKey; |
||
840 | temp_hash_key = (temp_hash_key & ~((uint64_t) 65535 << 48)) | common; |
||
841 | for (i = 0; i < cluster; i++) { |
||
842 | if (!(temp_hash_key ^ book_buffer[i].position)) { |
||
843 | tplayed = book_buffer[i].status_played & 077777777; |
||
844 | if (tplayed > played) { |
||
845 | played = tplayed; |
||
846 | book_ponder_move = book_moves[im]; |
||
847 | } |
||
848 | break; |
||
849 | } |
||
850 | } |
||
851 | UnmakeMove(tree, 2, book_moves[im], wtm); |
||
852 | } |
||
853 | } |
||
854 | return book_ponder_move; |
||
855 | } |
||
856 | |||
857 | /* last modified 05/08/14 */ |
||
858 | /* |
||
859 | ******************************************************************************* |
||
860 | * * |
||
861 | * BookUp() is used to create/add to the opening book file. typing "<file> * |
||
862 | * create" will erase the old book file and start from scratch, * |
||
863 | * * |
||
864 | * The format of the input data is a left bracket ("[") followed by any title* |
||
865 | * information desired, followed by a right bracket ("]") followed by a * |
||
866 | * sequence of moves. The sequence of moves is assumed to start at ply=1, * |
||
867 | * with white-to-move (normal opening position) and can contain as many moves* |
||
868 | * as desired (no limit on the depth of each variation.) The file *must* be * |
||
869 | * terminated with a line that begins with "end", since handling the EOF * |
||
870 | * condition makes portable code difficult. * |
||
871 | * * |
||
872 | * Book moves can either be typed in by hand, directly into book_add(), by * |
||
873 | * using the "book create/add" command. Using the command "book add/create * |
||
874 | * filename" will cause book_add() to read its opening text moves from * |
||
875 | * filename rather than from the keyboard * |
||
876 | * * |
||
877 | * In addition to the normal text for a move (reduced or full algebraic is * |
||
878 | * accepted, ie, e4, ed, exd4, e3d4, etc. are all acceptable) some special * |
||
879 | * characters can be appended to a move. * |
||
880 | * * |
||
881 | * ?? -> Never play this move. since the same book is used for both * |
||
882 | * black and white, you can enter moves in that white might play, * |
||
883 | * but prevent the program from choosing them on its own. * |
||
884 | * ? -> Avoid this move except for non-important games. These openings * |
||
885 | * are historically those that the program doesn't play very well, * |
||
886 | * but which aren't outright losing. * |
||
887 | * = -> Drawish move, only play this move if drawish moves are allowed * |
||
888 | * by the operator. This is used to encourage the program to play * |
||
889 | * drawish openings (Petrov's comes to mind) when the program needs* |
||
890 | * to draw or is facing a formidable opponent (deep thought comes * |
||
891 | * to mind.) * |
||
892 | * ! -> Always play this move, if there isn't a move with the !! flag * |
||
893 | * set also. This is a strong move, but not as strong as a !! * |
||
894 | * move. * |
||
895 | * !! -> Always play this move. This can be used to make the program * |
||
896 | * favor particular lines, or to mark a strong move for certain * |
||
897 | * opening traps. * |
||
898 | * * |
||
899 | * {Play nn%} is used to force this specific book move to be played a specific* |
||
900 | * percentage of the time, and override the frequency of play that * |
||
901 | * comes from the large pgn database. * |
||
902 | * * |
||
903 | ******************************************************************************* |
||
904 | */ |
||
905 | void BookUp(TREE * RESTRICT tree, int nargs, char **args) { |
||
906 | BB_POSITION *bbuffer; |
||
907 | uint64_t temp_hash_key, common; |
||
908 | FILE *book_input; |
||
909 | char fname[128], start, *ch, output_filename[128]; |
||
910 | static char schar[2] = { "." }; |
||
911 | int result = 0, played, i, mask_word, total_moves; |
||
912 | int move, move_num, wtm, book_positions, major, minor; |
||
913 | int cluster, max_cluster, discarded = 0, discarded_mp = 0, discarded_lose = |
||
914 | 0; |
||
915 | int errors, data_read; |
||
916 | int start_elapsed_time, ply, max_ply = 256; |
||
917 | int stat, files = 0, buffered = 0, min_played = 0, games_parsed = 0; |
||
918 | int wins, losses; |
||
919 | BOOK_POSITION current, next; |
||
920 | BB_POSITION temp; |
||
921 | int last, cluster_seek, next_cluster; |
||
922 | int counter, *index, max_search_depth; |
||
923 | double wl_percent = 0.0; |
||
924 | |||
925 | /* |
||
926 | ************************************************************ |
||
927 | * * |
||
928 | * Open the correct book file for writing/reading * |
||
929 | * * |
||
930 | ************************************************************ |
||
931 | */ |
||
932 | #if defined(POSITIONS) |
||
933 | unsigned int output_pos, output_wtm; |
||
934 | FILE *pout = fopen("positions", "w"); |
||
935 | #endif |
||
936 | if (!strcmp(args[1], "create")) { |
||
937 | if (nargs < 4) { |
||
938 | Print(4095, "usage: <binfile> create <pgn-filename> "); |
||
939 | Print(4095, "maxply [minplay] [win/lose %%]\n"); |
||
940 | return; |
||
941 | } |
||
942 | max_ply = atoi(args[3]); |
||
943 | if (nargs >= 5) { |
||
944 | min_played = atoi(args[4]); |
||
945 | } |
||
946 | if (nargs > 5) { |
||
947 | wl_percent = atof(args[5]) / 100.0; |
||
948 | } |
||
949 | strcpy(output_filename, args[0]); |
||
950 | if (!strstr(output_filename, ".bin")) { |
||
951 | strcat(output_filename, ".bin"); |
||
952 | } |
||
953 | } else if (!strcmp(args[1], "off")) { |
||
954 | if (book_file) |
||
955 | fclose(book_file); |
||
956 | if (books_file) |
||
957 | fclose(normal_bs_file); |
||
958 | if (computer_bs_file) |
||
959 | fclose(computer_bs_file); |
||
960 | book_file = 0; |
||
961 | books_file = 0; |
||
962 | computer_bs_file = 0; |
||
963 | normal_bs_file = 0; |
||
964 | Print(4095, "book file disabled.\n"); |
||
965 | return; |
||
966 | } else if (!strcmp(args[1], "on")) { |
||
967 | if (!book_file) { |
||
968 | sprintf(fname, "%s/book.bin", book_path); |
||
969 | book_file = fopen(fname, "rb+"); |
||
970 | sprintf(fname, "%s/books.bin", book_path); |
||
971 | books_file = fopen(fname, "rb+"); |
||
972 | Print(4095, "book file enabled.\n"); |
||
973 | } |
||
974 | return; |
||
975 | } else if (!strcmp(args[1], "mask")) { |
||
976 | if (nargs < 4) { |
||
977 | Print(4095, "usage: book mask accept|reject value\n"); |
||
978 | return; |
||
979 | } else if (!strcmp(args[2], "accept")) { |
||
980 | book_accept_mask = BookMask(args[3]); |
||
981 | book_reject_mask = book_reject_mask & ~book_accept_mask; |
||
982 | return; |
||
983 | } else if (!strcmp(args[2], "reject")) { |
||
984 | book_reject_mask = BookMask(args[3]); |
||
985 | book_accept_mask = book_accept_mask & ~book_reject_mask; |
||
986 | return; |
||
987 | } |
||
988 | } else if (!strcmp(args[1], "random")) { |
||
989 | if (nargs < 3) { |
||
990 | Print(4095, "usage: book random <n>\n"); |
||
991 | return; |
||
992 | } |
||
993 | book_random = atoi(args[2]); |
||
994 | switch (book_random) { |
||
995 | case 0: |
||
996 | Print(4095, "play best book line after search.\n"); |
||
997 | Print(4095, " ..book selection width set to 99.\n"); |
||
998 | book_selection_width = 99; |
||
999 | break; |
||
1000 | case 1: |
||
1001 | Print(4095, "choose from book moves randomly (using weights.)\n"); |
||
1002 | break; |
||
1003 | default: |
||
1004 | Print(4095, "valid options are 0-1.\n"); |
||
1005 | break; |
||
1006 | } |
||
1007 | return; |
||
1008 | } else if (!strcmp(args[1], "trigger")) { |
||
1009 | if (nargs < 3) { |
||
1010 | Print(4095, "usage: book trigger <n>\n"); |
||
1011 | return; |
||
1012 | } |
||
1013 | book_search_trigger = atoi(args[2]); |
||
1014 | Print(4095, "search book moves if the most popular was not played\n"); |
||
1015 | Print(4095, "at least %d times.\n", book_search_trigger); |
||
1016 | return; |
||
1017 | } else if (!strcmp(args[1], "width")) { |
||
1018 | if (nargs < 3) { |
||
1019 | Print(4095, "usage: book width <n>\n"); |
||
1020 | return; |
||
1021 | } |
||
1022 | book_selection_width = atoi(args[2]); |
||
1023 | book_random = 1; |
||
1024 | Print(4095, "choose from %d best moves.\n", book_selection_width); |
||
1025 | Print(4095, " ..book random set to 1.\n"); |
||
1026 | return; |
||
1027 | } else { |
||
1028 | Print(4095, "usage: book [option] [filename] [maxply] [minplay]\n"); |
||
1029 | return; |
||
1030 | } |
||
1031 | if (!(book_input = fopen(args[2], "r"))) { |
||
1032 | printf("file %s does not exist.\n", args[2]); |
||
1033 | return; |
||
1034 | } |
||
1035 | ReadPGN(0, 0); |
||
1036 | if (book_file) |
||
1037 | fclose(book_file); |
||
1038 | book_file = fopen(output_filename, "wb+"); |
||
1039 | bbuffer = (BB_POSITION *) malloc(sizeof(BB_POSITION) * SORT_BLOCK); |
||
1040 | if (!bbuffer) { |
||
1041 | Print(4095, "Unable to malloc() sort buffer, aborting\n"); |
||
1042 | CraftyExit(1); |
||
1043 | } |
||
1044 | fseek(book_file, 0, SEEK_SET); |
||
1045 | /* |
||
1046 | ************************************************************ |
||
1047 | * * |
||
1048 | * Now, read in a series of moves (terminated by the "[" * |
||
1049 | * of the next title or by "end" for end of the file) and * |
||
1050 | * make them. After each MakeMove(), we can grab the hash * |
||
1051 | * key, and use it to access the book data file to add * |
||
1052 | * this position. Note that we have to check the last * |
||
1053 | * character of a move for the special flags and set the * |
||
1054 | * correct bit in the status for this position. When we * |
||
1055 | * reach the end of a book line, we back up to the * |
||
1056 | * starting position and start over. * |
||
1057 | * * |
||
1058 | ************************************************************ |
||
1059 | */ |
||
1060 | major = atoi(version); |
||
1061 | minor = atoi(strchr(version, '.') + 1); |
||
1062 | major = (major << 16) + minor; |
||
1063 | start = !strstr(output_filename, "book.bin"); |
||
1064 | printf("parsing pgn move file (100k moves/dot)\n"); |
||
1065 | start_elapsed_time = ReadClock(); |
||
1066 | if (book_file) { |
||
1067 | total_moves = 0; |
||
1068 | max_search_depth = 0; |
||
1069 | errors = 0; |
||
1070 | do { |
||
1071 | data_read = ReadPGN(book_input, 0); |
||
1072 | if (data_read == -1) |
||
1073 | Print(4095, "end-of-file reached\n"); |
||
1074 | } while (data_read == 0); |
||
1075 | do { |
||
1076 | if (data_read < 0) { |
||
1077 | Print(4095, "end-of-file reached\n"); |
||
1078 | break; |
||
1079 | } |
||
1080 | if (data_read == 1) { |
||
1081 | if (strstr(buffer, "Site")) { |
||
1082 | games_parsed++; |
||
1083 | result = 3; |
||
1084 | } else if (strstr(buffer, "esult")) { |
||
1085 | if (strstr(buffer, "1-0")) |
||
1086 | result = 2; |
||
1087 | else if (strstr(buffer, "0-1")) |
||
1088 | result = 1; |
||
1089 | else if (strstr(buffer, "1/2-1/2")) |
||
1090 | result = 0; |
||
1091 | else if (strstr(buffer, "*")) |
||
1092 | result = 3; |
||
1093 | } |
||
1094 | data_read = ReadPGN(book_input, 0); |
||
1095 | } else |
||
1096 | do { |
||
1097 | wtm = 1; |
||
1098 | InitializeChessBoard(tree); |
||
1099 | tree->status[1] = tree->status[0]; |
||
1100 | move_num = 1; |
||
1101 | tree->status[2] = tree->status[1]; |
||
1102 | ply = 0; |
||
1103 | data_read = 0; |
||
1104 | #if defined(POSITIONS) |
||
1105 | output_pos = Random32(); |
||
1106 | output_pos = (output_pos | (output_pos >> 16)) & 65535; |
||
1107 | output_pos = output_pos % 20 + 8; |
||
1108 | output_wtm = Random32() & 1; |
||
1109 | #endif |
||
1110 | while (data_read == 0) { |
||
1111 | mask_word = 0; |
||
1112 | if ((ch = strpbrk(buffer, "?!"))) { |
||
1113 | mask_word = BookMask(ch); |
||
1114 | *ch = 0; |
||
1115 | } |
||
1116 | if (!strchr(buffer, '$') && !strchr(buffer, '*')) { |
||
1117 | if (ply < max_ply) |
||
1118 | move = ReadNextMove(tree, buffer, 2, wtm); |
||
1119 | else { |
||
1120 | move = 0; |
||
1121 | discarded++; |
||
1122 | } |
||
1123 | if (move) { |
||
1124 | ply++; |
||
1125 | max_search_depth = Max(max_search_depth, ply); |
||
1126 | total_moves++; |
||
1127 | common = HashKey & ((uint64_t) 65535 << 48); |
||
1128 | MakeMove(tree, 2, move, wtm); |
||
1129 | tree->status[2] = tree->status[3]; |
||
1130 | if (ply <= max_ply) { |
||
1131 | temp_hash_key = (wtm) ? HashKey : ~HashKey; |
||
1132 | temp_hash_key = |
||
1133 | (temp_hash_key & ~((uint64_t) 65535 << 48)) | common; |
||
1134 | memcpy(bbuffer[buffered].position, (char *) &temp_hash_key, |
||
1135 | 8); |
||
1136 | if (result & 1) |
||
1137 | mask_word |= 0x20; |
||
1138 | if (result == 0) |
||
1139 | mask_word |= 0x40; |
||
1140 | if (result & 2) |
||
1141 | mask_word |= 0x80; |
||
1142 | bbuffer[buffered].status = mask_word; |
||
1143 | bbuffer[buffered++].percent_play = |
||
1144 | pgn_suggested_percent + (wtm << 7); |
||
1145 | if (buffered >= SORT_BLOCK) { |
||
1146 | BookSort(bbuffer, buffered, ++files); |
||
1147 | buffered = 0; |
||
1148 | strcpy(schar, "S"); |
||
1149 | } |
||
1150 | } |
||
1151 | if (!(total_moves % 100000)) { |
||
1152 | printf("%s", schar); |
||
1153 | strcpy(schar, "."); |
||
1154 | if (!(total_moves % 6000000)) |
||
1155 | printf(" (%dk)\n", total_moves / 1000); |
||
1156 | fflush(stdout); |
||
1157 | } |
||
1158 | wtm = Flip(wtm); |
||
1159 | if (wtm) |
||
1160 | move_num++; |
||
1161 | #if defined(POSITIONS) |
||
1162 | if (wtm == output_wtm && move_num == output_pos) { |
||
1163 | SEARCH_POSITION temp_pos; |
||
1164 | int twtm; |
||
1165 | char t_initial_position[256]; |
||
1166 | |||
1167 | strcpy(t_initial_position, initial_position); |
||
1168 | temp_pos = tree->status[0]; |
||
1169 | tree->status[0] = tree->status[3]; |
||
1170 | if (Castle(0, white) < 0) |
||
1171 | Castle(0, white) = 0; |
||
1172 | if (Castle(0, black) < 0) |
||
1173 | Castle(0, black) = 0; |
||
1174 | strcpy(buffer, "savepos *"); |
||
1175 | twtm = game_wtm; |
||
1176 | game_wtm = wtm; |
||
1177 | (void) Option(tree); |
||
1178 | game_wtm = twtm; |
||
1179 | fprintf(pout, "%s\n", initial_position); |
||
1180 | strcpy(initial_position, t_initial_position); |
||
1181 | tree->status[0] = temp_pos; |
||
1182 | } |
||
1183 | #endif |
||
1184 | } else if (strspn(buffer, "0123456789/-.*") != strlen(buffer) |
||
1185 | && ply < max_ply) { |
||
1186 | errors++; |
||
1187 | Print(4095, "ERROR! move %d: %s is illegal (line %d)\n", |
||
1188 | move_num, buffer, ReadPGN(book_input, -2)); |
||
1189 | ReadPGN(book_input, -1); |
||
1190 | DisplayChessBoard(stdout, tree->position); |
||
1191 | do { |
||
1192 | data_read = ReadPGN(book_input, 0); |
||
1193 | if (data_read == -1) |
||
1194 | Print(4095, "end-of-file reached\n"); |
||
1195 | } while (data_read == 0); |
||
1196 | break; |
||
1197 | } |
||
1198 | } |
||
1199 | data_read = ReadPGN(book_input, 0); |
||
1200 | } |
||
1201 | } while (0); |
||
1202 | } while (strcmp(buffer, "end") && data_read != -1); |
||
1203 | if (book_input != stdin) |
||
1204 | fclose(book_input); |
||
1205 | if (buffered) |
||
1206 | BookSort(bbuffer, buffered, ++files); |
||
1207 | free(bbuffer); |
||
1208 | printf("S <done>\n"); |
||
1209 | if (total_moves == 0) { |
||
1210 | Print(4095, "ERROR - empty input PGN file\n"); |
||
1211 | return; |
||
1212 | } |
||
1213 | /* |
||
1214 | ************************************************************ |
||
1215 | * * |
||
1216 | * Now merge these "chunks" into book.bin, keeping all of * |
||
1217 | * the "flags" as well as counting the number of times * |
||
1218 | * that each move was played. * |
||
1219 | * * |
||
1220 | ************************************************************ |
||
1221 | */ |
||
1222 | printf("merging sorted files (%d) (100k/dot)\n", files); |
||
1223 | counter = 0; |
||
1224 | index = (int *) malloc(32768 * sizeof(int)); |
||
1225 | if (!index) { |
||
1226 | Print(4095, "Unable to malloc() index block, aborting\n"); |
||
1227 | CraftyExit(1); |
||
1228 | } |
||
1229 | for (i = 0; i < 32768; i++) |
||
1230 | index[i] = -1; |
||
1231 | temp = BookUpNextPosition(files, 1); |
||
1232 | memcpy((char *) ¤t.position, temp.position, 8); |
||
1233 | current.status_played = temp.status << 24; |
||
1234 | if (start) |
||
1235 | current.status_played += temp.percent_play & 127; |
||
1236 | current.learn = 0.0; |
||
1237 | played = 1; |
||
1238 | fclose(book_file); |
||
1239 | book_file = fopen(output_filename, "wb+"); |
||
1240 | fseek(book_file, sizeof(int) * 32768, SEEK_SET); |
||
1241 | last = current.position >> 49; |
||
1242 | index[last] = ftell(book_file); |
||
1243 | book_positions = 0; |
||
1244 | cluster = 0; |
||
1245 | cluster_seek = sizeof(int) * 32768; |
||
1246 | fseek(book_file, cluster_seek + sizeof(int), SEEK_SET); |
||
1247 | max_cluster = 0; |
||
1248 | wins = 0; |
||
1249 | losses = 0; |
||
1250 | if (temp.status & 128 && temp.percent_play & 128) |
||
1251 | wins++; |
||
1252 | if (temp.status & 128 && !(temp.percent_play & 128)) |
||
1253 | losses++; |
||
1254 | if (temp.status & 32 && !(temp.percent_play & 128)) |
||
1255 | wins++; |
||
1256 | if (temp.status & 32 && temp.percent_play & 128) |
||
1257 | losses++; |
||
1258 | while (1) { |
||
1259 | temp = BookUpNextPosition(files, 0); |
||
1260 | memcpy((char *) &next.position, temp.position, 8); |
||
1261 | next.status_played = temp.status << 24; |
||
1262 | if (start) |
||
1263 | next.status_played += temp.percent_play & 127; |
||
1264 | next.learn = 0.0; |
||
1265 | counter++; |
||
1266 | if (counter % 100000 == 0) { |
||
1267 | printf("."); |
||
1268 | if (counter % 6000000 == 0) |
||
1269 | printf(" (%dk)\n", counter / 1000); |
||
1270 | fflush(stdout); |
||
1271 | } |
||
1272 | if (current.position == next.position) { |
||
1273 | current.status_played = current.status_played | next.status_played; |
||
1274 | played++; |
||
1275 | if (temp.status & 128 && temp.percent_play & 128) |
||
1276 | wins++; |
||
1277 | if (temp.status & 128 && !(temp.percent_play & 128)) |
||
1278 | losses++; |
||
1279 | if (temp.status & 32 && !(temp.percent_play & 128)) |
||
1280 | wins++; |
||
1281 | if (temp.status & 32 && temp.percent_play & 128) |
||
1282 | losses++; |
||
1283 | } else { |
||
1284 | if (played >= min_played && wins >= (losses * wl_percent)) { |
||
1285 | book_positions++; |
||
1286 | cluster++; |
||
1287 | max_cluster = Max(max_cluster, cluster); |
||
1288 | if (!start) |
||
1289 | current.status_played += played; |
||
1290 | current.learn = 0.0; |
||
1291 | memcpy((void *) &book_buffer_char[0].position, |
||
1292 | (void *) BookOut64(current.position), 8); |
||
1293 | memcpy((void *) &book_buffer_char[0].status_played, |
||
1294 | (void *) BookOut32(current.status_played), 4); |
||
1295 | memcpy((void *) &book_buffer_char[0].learn, |
||
1296 | (void *) BookOut32((int) current.learn), 4); // Pierre-Marie Baty -- added type cast |
||
1297 | stat = fwrite(book_buffer_char, BOOK_POSITION_SIZE, 1, book_file); |
||
1298 | if (stat != 1) |
||
1299 | Print(4095, "ERROR! write failed, disk probably full.\n"); |
||
1300 | } else if (played < min_played) |
||
1301 | discarded_mp++; |
||
1302 | else |
||
1303 | discarded_lose++; |
||
1304 | if (last != (int) (next.position >> 49)) { |
||
1305 | next_cluster = ftell(book_file); |
||
1306 | fseek(book_file, cluster_seek, SEEK_SET); |
||
1307 | memcpy((void *) &cluster, BookOut32(cluster), 4); |
||
1308 | stat = fwrite(&cluster, sizeof(int), 1, book_file); |
||
1309 | if (stat != 1) |
||
1310 | Print(4095, "ERROR! write failed, disk probably full.\n"); |
||
1311 | if (next.position == 0) |
||
1312 | break; |
||
1313 | fseek(book_file, next_cluster + sizeof(int), SEEK_SET); |
||
1314 | cluster_seek = next_cluster; |
||
1315 | last = next.position >> 49; |
||
1316 | index[last] = next_cluster; |
||
1317 | cluster = 0; |
||
1318 | } |
||
1319 | wins = 0; |
||
1320 | losses = 0; |
||
1321 | if (temp.status & 128 && temp.percent_play & 128) |
||
1322 | wins++; |
||
1323 | if (temp.status & 128 && !(temp.percent_play & 128)) |
||
1324 | losses++; |
||
1325 | if (temp.status & 32 && !(temp.percent_play & 128)) |
||
1326 | wins++; |
||
1327 | if (temp.status & 32 && temp.percent_play & 128) |
||
1328 | losses++; |
||
1329 | current = next; |
||
1330 | played = 1; |
||
1331 | if (next.position == 0) |
||
1332 | break; |
||
1333 | } |
||
1334 | } |
||
1335 | fseek(book_file, 0, SEEK_SET); |
||
1336 | for (i = 0; i < 32768; i++) { |
||
1337 | memcpy((void *) &cluster, (void *) BookOut32(index[i]), 4); |
||
1338 | fwrite(&cluster, 4, 1, book_file); |
||
1339 | } |
||
1340 | fseek(book_file, 0, SEEK_END); |
||
1341 | memcpy((void *) &cluster, (void *) BookOut32(major), 4); |
||
1342 | fwrite(&cluster, 4, 1, book_file); |
||
1343 | /* |
||
1344 | ************************************************************ |
||
1345 | * * |
||
1346 | * Now clean up, remove the sort.n files, and print the * |
||
1347 | * statistics for building the book. * |
||
1348 | * * |
||
1349 | ************************************************************ |
||
1350 | */ |
||
1351 | for (i = 1; i <= files; i++) { |
||
1352 | sprintf(fname, "sort.%d", i); |
||
1353 | remove(fname); |
||
1354 | } |
||
1355 | free(index); |
||
1356 | start_elapsed_time = ReadClock() - start_elapsed_time; |
||
1357 | Print(4095, "\n\nparsed %d moves (%d games).\n", total_moves, |
||
1358 | games_parsed); |
||
1359 | Print(4095, "found %d errors during parsing.\n", errors); |
||
1360 | Print(4095, "discarded %d moves (maxply=%d).\n", discarded, max_ply); |
||
1361 | Print(4095, "discarded %d moves (minplayed=%d).\n", discarded_mp, |
||
1362 | min_played); |
||
1363 | Print(4095, "discarded %d moves (win/lose=%.1f%%).\n", discarded_lose, |
||
1364 | wl_percent * 100); |
||
1365 | Print(4095, "book contains %d unique positions.\n", book_positions); |
||
1366 | Print(4095, "deepest book line was %d plies.\n", max_search_depth); |
||
1367 | Print(4095, "longest cluster of moves was %d.\n", max_cluster); |
||
1368 | Print(4095, "time used: %s elapsed.\n", DisplayTime(start_elapsed_time)); |
||
1369 | } |
||
1370 | strcpy(initial_position, ""); |
||
1371 | InitializeChessBoard(tree); |
||
1372 | } |
||
1373 | |||
1374 | /* last modified 02/23/14 */ |
||
1375 | /* |
||
1376 | ******************************************************************************* |
||
1377 | * * |
||
1378 | * BookMask() is used to convert the flags for a book move into an 8 bit * |
||
1379 | * mask that is either kept in the file, or is set by the operator to select * |
||
1380 | * which opening(s) the program is allowed to play. * |
||
1381 | * * |
||
1382 | ******************************************************************************* |
||
1383 | */ |
||
1384 | int BookMask(char *flags) { |
||
1385 | int i, mask; |
||
1386 | |||
1387 | mask = 0; |
||
1388 | for (i = 0; i < (int) strlen(flags); i++) { |
||
1389 | if (flags[i] == '?') { |
||
1390 | if (flags[i + 1] == '?') { |
||
1391 | mask = mask | 1; |
||
1392 | i++; |
||
1393 | } else |
||
1394 | mask = mask | 2; |
||
1395 | } else if (flags[i] == '=') { |
||
1396 | mask = mask | 4; |
||
1397 | } else if (flags[i] == '!') { |
||
1398 | if (flags[i + 1] == '!') { |
||
1399 | mask = mask | 16; |
||
1400 | i++; |
||
1401 | } else |
||
1402 | mask = mask | 8; |
||
1403 | } |
||
1404 | } |
||
1405 | return mask; |
||
1406 | } |
||
1407 | |||
1408 | /* last modified 02/23/14 */ |
||
1409 | /* |
||
1410 | ******************************************************************************* |
||
1411 | * * |
||
1412 | * BookSort() is called to sort a block of moves after they have been parsed * |
||
1413 | * and converted to hash keys. * |
||
1414 | * * |
||
1415 | ******************************************************************************* |
||
1416 | */ |
||
1417 | void BookSort(BB_POSITION * buffer, int number, int fileno) { |
||
1418 | char fname[16]; |
||
1419 | FILE *output_file; |
||
1420 | int stat; |
||
1421 | |||
1422 | qsort((char *) buffer, number, sizeof(BB_POSITION), BookUpCompare); |
||
1423 | sprintf(fname, "sort.%d", fileno); |
||
1424 | if (!(output_file = fopen(fname, "wb+"))) |
||
1425 | printf("ERROR. unable to open sort output file\n"); |
||
1426 | stat = fwrite(buffer, sizeof(BB_POSITION), number, output_file); |
||
1427 | if (stat != number) |
||
1428 | Print(4095, "ERROR! write failed, disk probably full.\n"); |
||
1429 | fclose(output_file); |
||
1430 | } |
||
1431 | |||
1432 | /* last modified 02/23/14 */ |
||
1433 | /* |
||
1434 | ******************************************************************************* |
||
1435 | * * |
||
1436 | * BookUpNextPosition() is the heart of the "merge" operation that is done * |
||
1437 | * after the chunks of the parsed/hashed move file are sorted. This code * |
||
1438 | * opens the sort.n files, and returns the least (lexically) position key to * |
||
1439 | * counted/merged into the main book database. * |
||
1440 | * * |
||
1441 | ******************************************************************************* |
||
1442 | */ |
||
1443 | BB_POSITION BookUpNextPosition(int files, int init) { |
||
1444 | char fname[20]; |
||
1445 | static FILE *input_file[100]; |
||
1446 | static BB_POSITION *buffer[100]; |
||
1447 | static int data_read[100], next[100]; |
||
1448 | int i, used; |
||
1449 | BB_POSITION least; |
||
1450 | |||
1451 | if (init) { |
||
1452 | for (i = 1; i <= files; i++) { |
||
1453 | sprintf(fname, "sort.%d", i); |
||
1454 | if (!(input_file[i] = fopen(fname, "rb"))) { |
||
1455 | printf("unable to open sort.%d file, may be too many files open.\n", |
||
1456 | i); |
||
1457 | CraftyExit(1); |
||
1458 | } |
||
1459 | buffer[i] = (BB_POSITION *) malloc(sizeof(BB_POSITION) * MERGE_BLOCK); |
||
1460 | if (!buffer[i]) { |
||
1461 | printf("out of memory. aborting. \n"); |
||
1462 | CraftyExit(1); |
||
1463 | } |
||
1464 | fseek(input_file[i], 0, SEEK_SET); |
||
1465 | data_read[i] = |
||
1466 | fread(buffer[i], sizeof(BB_POSITION), MERGE_BLOCK, input_file[i]); |
||
1467 | next[i] = 0; |
||
1468 | } |
||
1469 | } |
||
1470 | for (i = 0; i < 8; i++) |
||
1471 | least.position[i] = 0; |
||
1472 | least.status = 0; |
||
1473 | least.percent_play = 0; |
||
1474 | used = -1; |
||
1475 | for (i = 1; i <= files; i++) |
||
1476 | if (data_read[i]) { |
||
1477 | least = buffer[i][next[i]]; |
||
1478 | used = i; |
||
1479 | break; |
||
1480 | } |
||
1481 | if (i > files) { |
||
1482 | for (i = 1; i <= files; i++) |
||
1483 | fclose(input_file[i]); |
||
1484 | return least; |
||
1485 | } |
||
1486 | for (i++; i <= files; i++) { |
||
1487 | if (data_read[i]) { |
||
1488 | uint64_t p1, p2; |
||
1489 | |||
1490 | memcpy((char *) &p1, least.position, 8); |
||
1491 | memcpy((char *) &p2, buffer[i][next[i]].position, 8); |
||
1492 | if (p1 > p2) { |
||
1493 | least = buffer[i][next[i]]; |
||
1494 | used = i; |
||
1495 | } |
||
1496 | } |
||
1497 | } |
||
1498 | if (--data_read[used] == 0) { |
||
1499 | data_read[used] = |
||
1500 | fread(buffer[used], sizeof(BB_POSITION), MERGE_BLOCK, |
||
1501 | input_file[used]); |
||
1502 | next[used] = 0; |
||
1503 | } else |
||
1504 | next[used]++; |
||
1505 | return least; |
||
1506 | } |
||
1507 | |||
1508 | int BookUpCompare(const void *pos1, const void *pos2) { |
||
1509 | static uint64_t p1, p2; |
||
1510 | |||
1511 | memcpy((char *) &p1, ((BB_POSITION *) pos1)->position, 8); |
||
1512 | memcpy((char *) &p2, ((BB_POSITION *) pos2)->position, 8); |
||
1513 | if (p1 < p2) |
||
1514 | return -1; |
||
1515 | if (p1 > p2) |
||
1516 | return +1; |
||
1517 | return 0; |
||
1518 | } |