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 *) &current.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