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