Subversion Repositories Games.Chess Giants

Rev

Rev 108 | Go to most recent revision | Details | Last modification | View Log | RSS feed

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