Subversion Repositories Games.Chess Giants

Rev

Rev 33 | Rev 154 | Go to most recent revision | Details | Compare with Previous | 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
 *                                                                             *
108 pmbaty 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)            *
33 pmbaty 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
108 pmbaty 43
int Book(TREE * RESTRICT tree, int wtm) {
33 pmbaty 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];
108 pmbaty 48
  static int selected_order_played[200], selected_value[200];
33 pmbaty 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;
108 pmbaty 60
  int cluster, scluster, test, v;
33 pmbaty 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);
108 pmbaty 92
    v = fread(buf32, 4, 1, books_file);
93
    if (v <= 0)
94
      perror("Book() fread error: ");
33 pmbaty 95
    key = BookIn32(buf32);
96
    if (key > 0) {
97
      fseek(books_file, key, SEEK_SET);
108 pmbaty 98
      v = fread(buf32, 4, 1, books_file);
99
      if (v <= 0)
100
        perror("Book() fread error: ");
33 pmbaty 101
      scluster = BookIn32(buf32);
108 pmbaty 102
      if (scluster)
103
        BookClusterIn(books_file, scluster, book_buffer);
33 pmbaty 104
      for (im = 0; im < n_root_moves; im++) {
105
        common = HashKey & ((uint64_t) 65535 << 48);
108 pmbaty 106
        MakeMove(tree, 1, wtm, root_moves[im].move);
33 pmbaty 107
        if (Repeat(tree, 2)) {
108 pmbaty 108
          UnmakeMove(tree, 1, wtm, root_moves[im].move);
33 pmbaty 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
          }
108 pmbaty 118
        UnmakeMove(tree, 1, wtm, root_moves[im].move);
33 pmbaty 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);
108 pmbaty 135
    v = fread(buf32, 4, 1, book_file);
136
    if (v <= 0)
137
      perror("Book() fread error: ");
33 pmbaty 138
    key = BookIn32(buf32);
139
    if (key > 0) {
140
      book_learn_seekto = key;
141
      fseek(book_file, key, SEEK_SET);
108 pmbaty 142
      v = fread(buf32, 4, 1, book_file);
143
      if (v <= 0)
144
        perror("Book() fread error: ");
33 pmbaty 145
      cluster = BookIn32(buf32);
108 pmbaty 146
      if (cluster)
147
        BookClusterIn(book_file, cluster, book_buffer);
33 pmbaty 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;
108 pmbaty 182
    EvaluateCastling(tree, 1, wtm);
33 pmbaty 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);
108 pmbaty 188
      MakeMove(tree, 1, wtm, root_moves[im].move);
33 pmbaty 189
      if (Repeat(tree, 2)) {
108 pmbaty 190
        UnmakeMove(tree, 1, wtm, root_moves[im].move);
33 pmbaty 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;
108 pmbaty 205
            EvaluateCastling(tree, 2, wtm);
33 pmbaty 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
      }
108 pmbaty 227
      UnmakeMove(tree, 1, wtm, root_moves[im].move);
33 pmbaty 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;
108 pmbaty 283
      bs_value[i] += bs_played[i] / (float) maxp *1000.0f * book_weight_freq; // Pierre-Marie Baty -- added type cast
33 pmbaty 284
 
285
      if (minlv < maxlv)
286
        bs_value[i] +=
287
            (bs_learn[i] - minlv) / (float) (maxlv -
108 pmbaty 288
            minlv) * 1000.0f * book_weight_learn; // Pierre-Marie Baty -- added type cast
33 pmbaty 289
      if (minev < maxev)
290
        bs_value[i] +=
291
            (evaluations[i] - minev) / (float) (Max(maxev - minev,
108 pmbaty 292
                50)) * 1000.0f * book_weight_eval; // Pierre-Marie Baty -- added type cast
33 pmbaty 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])) {
108 pmbaty 328
            tempr = (float) bs_played[i]; // Pierre-Marie Baty -- added type cast (FIXME: ugly!)
33 pmbaty 329
            bs_played[i] = bs_played[i + 1];
108 pmbaty 330
            bs_played[i + 1] = (int) tempr; // Pierre-Marie Baty -- added type cast (FIXME: ugly!)
33 pmbaty 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) {
108 pmbaty 368
      Print(32, "  after screening, the following moves can be played\n");
369
      Print(32,
33 pmbaty 370
          "  move     played    %%  score    learn    " "sortv   P%%  P\n");
371
      for (i = 0; i < nmoves; i++) {
108 pmbaty 372
        Print(32, "%6s", OutputMove(tree, 1, wtm, book_moves[i]));
33 pmbaty 373
        st = book_status[i];
374
        if (st & 0x1f) {
375
          if (st & 0x01)
108 pmbaty 376
            Print(32, "??");
33 pmbaty 377
          else if (st & 0x02)
108 pmbaty 378
            Print(32, "? ");
33 pmbaty 379
          else if (st & 0x04)
108 pmbaty 380
            Print(32, "= ");
33 pmbaty 381
          else if (st & 0x08)
108 pmbaty 382
            Print(32, "! ");
33 pmbaty 383
          else if (st & 0x10)
108 pmbaty 384
            Print(32, "!!");
33 pmbaty 385
        } else
108 pmbaty 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]);
33 pmbaty 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))))
108 pmbaty 398
          Print(32, "  Y");
33 pmbaty 399
        else
108 pmbaty 400
          Print(32, "  N");
401
        Print(32, "\n");
33 pmbaty 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];
108 pmbaty 432
          selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
33 pmbaty 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];
108 pmbaty 442
          selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
33 pmbaty 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];
108 pmbaty 451
      bs_value[i] = (float) selected_value[i]; // Pierre-Marie Baty -- added type cast
33 pmbaty 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++) {
108 pmbaty 473
          sprintf(kibitz_p, "%s %d%%", OutputMove(tree, 1, wtm,
474
                  book_moves[i]), 100 * bs_played[i] / Max(total_played, 1));
33 pmbaty 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];
108 pmbaty 505
            selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
33 pmbaty 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];
108 pmbaty 516
            selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
33 pmbaty 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];
108 pmbaty 526
            selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
33 pmbaty 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];
108 pmbaty 535
          selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
33 pmbaty 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];
108 pmbaty 543
        selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
33 pmbaty 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];
108 pmbaty 553
      bs_value[i] = (float) selected_value[i]; // Pierre-Marie Baty -- added type cast
33 pmbaty 554
      bs_key[i] = selected_key[i];
555
    }
556
    nmoves = num_selected;
557
    if (nmoves == 0)
558
      return 0;
108 pmbaty 559
    Print(32, "               book moves {");
33 pmbaty 560
    for (i = 0; i < nmoves; i++) {
108 pmbaty 561
      Print(32, "%s", OutputMove(tree, 1, wtm, book_moves[i]));
33 pmbaty 562
      if (i < nmoves - 1)
108 pmbaty 563
        Print(32, ", ");
33 pmbaty 564
    }
108 pmbaty 565
    Print(32, "}\n");
33 pmbaty 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) {
108 pmbaty 572
      Print(32, "               moves considered {");
33 pmbaty 573
      for (i = 0; i < nmoves; i++) {
108 pmbaty 574
        Print(32, "%s", OutputMove(tree, 1, wtm, book_moves[i]));
33 pmbaty 575
        if (i < nmoves - 1)
108 pmbaty 576
          Print(32, ", ");
33 pmbaty 577
      }
108 pmbaty 578
      Print(32, "}\n");
33 pmbaty 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] =
108 pmbaty 602
            total_value / (1.0f - // Pierre-Marie Baty -- added type cast
603
            (float) total_percent / 100.0f) * (float) bs_percent[i] / 100.0f; // Pierre-Marie Baty -- added type casts
33 pmbaty 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) {
108 pmbaty 613
      Print(32, "  move     played    %%  score     sortv  P%%  P\n");
33 pmbaty 614
      for (i = 0; i < nmoves; i++) {
108 pmbaty 615
        Print(32, "%6s", OutputMove(tree, 1, wtm, book_moves[i]));
33 pmbaty 616
        st = book_status[i];
617
        if (st & 0x1f) {
618
          if (st & 0x01)
108 pmbaty 619
            Print(32, "??");
33 pmbaty 620
          else if (st & 0x02)
108 pmbaty 621
            Print(32, "? ");
33 pmbaty 622
          else if (st & 0x04)
108 pmbaty 623
            Print(32, "= ");
33 pmbaty 624
          else if (st & 0x08)
108 pmbaty 625
            Print(32, "! ");
33 pmbaty 626
          else if (st & 0x10)
108 pmbaty 627
            Print(32, "!!");
33 pmbaty 628
        } else
108 pmbaty 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]);
33 pmbaty 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))))
108 pmbaty 640
          Print(32, "  Y");
33 pmbaty 641
        else
108 pmbaty 642
          Print(32, "  N");
643
        Print(32, "\n");
33 pmbaty 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 {
108 pmbaty 674
          tree->pv[0].path[1] = book_moves[0];
675
          tree->pv[0].pathl = 2;
676
          tree->pv[0].pathd = 0;
33 pmbaty 677
        }
678
        return 1;
679
      }
680
    }
681
/*
682
 ************************************************************
683
 *                                                          *
684
 *  If puzzling, in tournament mode we try to find the best *
685
 *  non-book move, because a book move will produce a quick *
686
 *  move anyway.  We therefore would rather search for a    *
687
 *  non-book move, just in case the opponent goes out of    *
688
 *  book here.                                              *
689
 *                                                          *
690
 ************************************************************
691
 */
692
    else if (mode == tournament_mode && puzzling) {
693
      RootMoveList(wtm);
694
      for (i = 0; i < n_root_moves; i++)
695
        for (j = 0; j < nmoves; j++)
696
          if (root_moves[i].move == book_moves[j])
697
            root_moves[i].move = 0;
698
      for (i = 0, j = 0; i < n_root_moves; i++)
699
        if (root_moves[i].move != 0)
700
          root_moves[j++] = root_moves[i];
701
      n_root_moves = j;
108 pmbaty 702
      Print(32, "               moves considered {only non-book moves}\n");
33 pmbaty 703
      nmoves = j;
704
      if (nmoves > 1) {
705
        last_pv.pathd = 0;
706
        booking = 1;
108 pmbaty 707
        Iterate(wtm, booking, 1);
33 pmbaty 708
        booking = 0;
709
      } else {
108 pmbaty 710
        tree->pv[0].path[1] = book_moves[0];
711
        tree->pv[0].pathl = 2;
712
        tree->pv[0].pathd = 0;
33 pmbaty 713
      }
714
      return 1;
715
    }
716
    last_move = nmoves;
717
/*
718
 ************************************************************
719
 *                                                          *
720
 *  Compute a random value and use this to generate a book  *
721
 *  move based on a probability distribution of the number  *
722
 *  of games won by each book move.                         *
723
 *                                                          *
724
 ************************************************************
725
 */
726
    which = Random32();
727
    j = ReadClock() / 100 % 13;
728
    for (i = 0; i < j; i++)
729
      which = Random32();
730
    total_moves = 0;
731
    for (i = 0; i < last_move; i++) {
732
      if (bs_percent[0])
733
        total_moves += (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
734
      else
735
        total_moves += (int) (bs_value[i] * bs_value[i]); // Pierre-Marie Baty -- added type cast
736
    }
737
    distribution = Abs(which) % Max(total_moves, 1);
738
    for (which = 0; which < last_move; which++) {
739
      if (bs_percent[0])
740
        distribution -= (int) bs_value[which]; // Pierre-Marie Baty -- added type cast
741
      else
742
        distribution -= (int) (bs_value[which] * bs_value[which]); // Pierre-Marie Baty -- added type cast
743
      if (distribution < 0)
744
        break;
745
    }
746
    which = Min(which, last_move - 1);
108 pmbaty 747
    tree->pv[0].path[1] = book_moves[which];
33 pmbaty 748
    percent_played = 100 * bs_played[which] / Max(total_played, 1);
749
    total_played = bs_played[which];
750
    m1_status = book_status[which];
108 pmbaty 751
    tree->pv[0].pathl = 2;
752
    tree->pv[0].pathd = 0;
33 pmbaty 753
    if (mode != tournament_mode) {
108 pmbaty 754
      MakeMove(tree, 1, wtm, book_moves[which]);
33 pmbaty 755
      if ((book_ponder_move = BookPonderMove(tree, Flip(wtm)))) {
108 pmbaty 756
        tree->pv[0].path[2] = book_ponder_move;
757
        tree->pv[0].pathl = 3;
33 pmbaty 758
      }
108 pmbaty 759
      UnmakeMove(tree, 1, wtm, book_moves[which]);
33 pmbaty 760
    }
761
    book_learn_key = bs_key[which];
108 pmbaty 762
    Print(32, "               book   0.0s    %3d%%   ", percent_played);
763
    Print(32, " %s", OutputMove(tree, 1, wtm, tree->pv[0].path[1]));
33 pmbaty 764
    st = m1_status & book_accept_mask & (~224);
765
    if (st) {
766
      if (st & 1)
108 pmbaty 767
        Print(32, "??");
33 pmbaty 768
      else if (st & 2)
108 pmbaty 769
        Print(32, "?");
33 pmbaty 770
      else if (st & 4)
108 pmbaty 771
        Print(32, "=");
33 pmbaty 772
      else if (st & 8)
108 pmbaty 773
        Print(32, "!");
33 pmbaty 774
      else if (st & 16)
108 pmbaty 775
        Print(32, "!!");
33 pmbaty 776
    }
108 pmbaty 777
    MakeMove(tree, 1, wtm, tree->pv[0].path[1]);
778
    if (tree->pv[0].pathl > 2)
779
      Print(32, " %s", OutputMove(tree, 2, Flip(wtm), tree->pv[0].path[2]));
780
    UnmakeMove(tree, 1, wtm, tree->pv[0].path[1]);
781
    Print(32, "\n");
33 pmbaty 782
    return 1;
783
  }
784
  return 0;
785
}
786
 
787
/* last modified 02/23/14 */
788
/*
789
 *******************************************************************************
790
 *                                                                             *
791
 *   BookPonderMove() is used to find a move to ponder, to avoid the overhead  *
792
 *   of a "puzzling" search early in the game (unless there are no book moves  *
793
 *   found, of course.)  The algorithm is much simpler than the normal book    *
794
 *   move code...  just find the move with the largest frequency counter and   *
795
 *   assume that will be played.                                               *
796
 *                                                                             *
797
 *******************************************************************************
798
 */
799
int BookPonderMove(TREE * RESTRICT tree, int wtm) {
800
  uint64_t temp_hash_key, common;
108 pmbaty 801
  static unsigned book_moves[200];
802
  int i, v, key, cluster, n_moves, im, played, tplayed;
803
  unsigned *lastm;
33 pmbaty 804
  int book_ponder_move = 0, test;
805
  unsigned char buf32[4];
806
 
807
/*
808
 ************************************************************
809
 *                                                          *
810
 *  position is known, read in the appropriate cluster.     *
811
 *  note that this cluster will have all possible book      *
812
 *  moves from current position in it (as well as others of *
813
 *  course.)                                                *
814
 *                                                          *
815
 ************************************************************
816
 */
817
  if (book_file) {
818
    test = HashKey >> 49;
819
    fseek(book_file, test * sizeof(int), SEEK_SET);
108 pmbaty 820
    v = fread(buf32, 4, 1, book_file);
821
    if (v <= 0)
822
      perror("Book() fread error: ");
33 pmbaty 823
    key = BookIn32(buf32);
824
    if (key > 0) {
825
      fseek(book_file, key, SEEK_SET);
108 pmbaty 826
      v = fread(buf32, 4, 1, book_file);
827
      if (v <= 0)
828
        perror("Book() fread error: ");
33 pmbaty 829
      cluster = BookIn32(buf32);
108 pmbaty 830
      if (cluster)
831
        BookClusterIn(book_file, cluster, book_buffer);
33 pmbaty 832
    } else
833
      cluster = 0;
834
    if (!cluster)
835
      return 0;
836
    lastm = GenerateCaptures(tree, 2, wtm, book_moves);
837
    lastm = GenerateNoncaptures(tree, 2, wtm, lastm);
838
    n_moves = lastm - book_moves;
839
/*
840
 ************************************************************
841
 *                                                          *
842
 *  First cycle through the root move list, make each move, *
843
 *  and see if the resulting hash key is in the book        *
844
 *  database.                                               *
845
 *                                                          *
846
 ************************************************************
847
 */
848
    played = -1;
849
    for (im = 0; im < n_moves; im++) {
850
      common = HashKey & ((uint64_t) 65535 << 48);
108 pmbaty 851
      MakeMove(tree, 2, wtm, book_moves[im]);
33 pmbaty 852
      temp_hash_key = (wtm) ? HashKey : ~HashKey;
853
      temp_hash_key = (temp_hash_key & ~((uint64_t) 65535 << 48)) | common;
854
      for (i = 0; i < cluster; i++) {
855
        if (!(temp_hash_key ^ book_buffer[i].position)) {
856
          tplayed = book_buffer[i].status_played & 077777777;
857
          if (tplayed > played) {
858
            played = tplayed;
859
            book_ponder_move = book_moves[im];
860
          }
861
          break;
862
        }
863
      }
108 pmbaty 864
      UnmakeMove(tree, 2, wtm, book_moves[im]);
33 pmbaty 865
    }
866
  }
867
  return book_ponder_move;
868
}
869
 
870
/* last modified 05/08/14 */
871
/*
872
 *******************************************************************************
873
 *                                                                             *
108 pmbaty 874
 *   Bookup() is used to create/add to the opening book file.  typing "<file>  *
33 pmbaty 875
 *   create" will erase the old book file and start from scratch,              *
876
 *                                                                             *
108 pmbaty 877
 *   The format of the input data is a left bracket ("[") followed by any      *
878
 *   title information desired, followed by a right bracket ("]") followed by  *
879
 *   a sequence of moves.  The sequence of moves is assumed to start at ply=1, *
880
 *   with white-to-move (normal opening position) and can contain as many      *
881
 *   moves as desired (no limit on the depth of each variation.)  The file     *
882
 *   *must* be terminated with a line that begins with "end", since handling   *
883
 *   the EOF condition makes portable code difficult.                          *
33 pmbaty 884
 *                                                                             *
885
 *   Book moves can either be typed in by hand, directly into book_add(), by   *
886
 *   using the "book create/add" command.  Using the command "book add/create  *
887
 *   filename" will cause book_add() to read its opening text moves from       *
888
 *   filename rather than from the keyboard                                    *
889
 *                                                                             *
890
 *   In addition to the normal text for a move (reduced or full algebraic is   *
891
 *   accepted, ie, e4, ed, exd4, e3d4, etc. are all acceptable) some special   *
892
 *   characters can be appended to a move.                                     *
893
 *                                                                             *
108 pmbaty 894
 *        ?? ->  Never play this move.  since the same book is used for both   *
895
 *               black and white, you can enter moves in that white might      *
896
 *               play, but prevent the program from choosing them on its own.  *
897
 *        ?  ->  Avoid this move except for non-important games.  These        *
898
 *               openings are historically those that the program doesn't play *
899
 *               very well, but which aren't outright losing.                  *
900
 *        =  ->  Drawish move, only play this move if drawish moves are        *
901
 *               allowed by the operator.  This is used to encourage the       *
902
 *               program to play drawish openings (Petrov's comes to mind)     *
903
 *               when the program needs to draw or is facing a formidable      *
904
 *               opponent (deep thought comes to mind.)                        *
905
 *        !  ->  Always play this move, if there isn't a move with the !! flag *
906
 *               set also.  This is a strong move, but not as strong as a !!   *
907
 *               move.                                                         *
908
 *        !! ->  Always play this move.  This can be used to make the program  *
909
 *               favor particular lines, or to mark a strong move for certain  *
910
 *               opening traps.                                                *
33 pmbaty 911
 *                                                                             *
108 pmbaty 912
 *  {Play nn%} is used to force this specific book move to be played a         *
913
 *  specific percentage of the time, and override the frequency of play that   *
914
 *               comes from the large pgn database.                            *
33 pmbaty 915
 *                                                                             *
916
 *******************************************************************************
917
 */
108 pmbaty 918
void Bookup(TREE * RESTRICT tree, int nargs, char **args) {
33 pmbaty 919
  BB_POSITION *bbuffer;
920
  uint64_t temp_hash_key, common;
921
  FILE *book_input;
922
  char fname[128], start, *ch, output_filename[128];
923
  static char schar[2] = { "." };
924
  int result = 0, played, i, mask_word, total_moves;
925
  int move, move_num, wtm, book_positions, major, minor;
926
  int cluster, max_cluster, discarded = 0, discarded_mp = 0, discarded_lose =
927
      0;
928
  int errors, data_read;
929
  int start_elapsed_time, ply, max_ply = 256;
930
  int stat, files = 0, buffered = 0, min_played = 0, games_parsed = 0;
931
  int wins, losses;
932
  BOOK_POSITION current, next;
933
  BB_POSITION temp;
934
  int last, cluster_seek, next_cluster;
935
  int counter, *index, max_search_depth;
936
  double wl_percent = 0.0;
937
 
938
/*
939
 ************************************************************
940
 *                                                          *
941
 *  Open the correct book file for writing/reading          *
942
 *                                                          *
943
 ************************************************************
944
 */
945
#if defined(POSITIONS)
946
  unsigned int output_pos, output_wtm;
947
  FILE *pout = fopen("positions", "w");
948
#endif
949
  if (!strcmp(args[1], "create")) {
950
    if (nargs < 4) {
951
      Print(4095, "usage:  <binfile> create <pgn-filename> ");
952
      Print(4095, "maxply [minplay] [win/lose %%]\n");
953
      return;
954
    }
955
    max_ply = atoi(args[3]);
956
    if (nargs >= 5) {
957
      min_played = atoi(args[4]);
958
    }
959
    if (nargs > 5) {
960
      wl_percent = atof(args[5]) / 100.0;
961
    }
962
    strcpy(output_filename, args[0]);
963
    if (!strstr(output_filename, ".bin")) {
964
      strcat(output_filename, ".bin");
965
    }
966
  } else if (!strcmp(args[1], "off")) {
967
    if (book_file)
968
      fclose(book_file);
969
    if (books_file)
970
      fclose(normal_bs_file);
971
    if (computer_bs_file)
972
      fclose(computer_bs_file);
973
    book_file = 0;
974
    books_file = 0;
975
    computer_bs_file = 0;
976
    normal_bs_file = 0;
977
    Print(4095, "book file disabled.\n");
978
    return;
979
  } else if (!strcmp(args[1], "on")) {
980
    if (!book_file) {
981
      sprintf(fname, "%s/book.bin", book_path);
982
      book_file = fopen(fname, "rb+");
983
      sprintf(fname, "%s/books.bin", book_path);
984
      books_file = fopen(fname, "rb+");
985
      Print(4095, "book file enabled.\n");
986
    }
987
    return;
988
  } else if (!strcmp(args[1], "mask")) {
989
    if (nargs < 4) {
990
      Print(4095, "usage:  book mask accept|reject value\n");
991
      return;
992
    } else if (!strcmp(args[2], "accept")) {
993
      book_accept_mask = BookMask(args[3]);
994
      book_reject_mask = book_reject_mask & ~book_accept_mask;
995
      return;
996
    } else if (!strcmp(args[2], "reject")) {
997
      book_reject_mask = BookMask(args[3]);
998
      book_accept_mask = book_accept_mask & ~book_reject_mask;
999
      return;
1000
    }
1001
  } else if (!strcmp(args[1], "random")) {
1002
    if (nargs < 3) {
1003
      Print(4095, "usage:  book random <n>\n");
1004
      return;
1005
    }
1006
    book_random = atoi(args[2]);
1007
    switch (book_random) {
1008
      case 0:
1009
        Print(4095, "play best book line after search.\n");
1010
        Print(4095, "  ..book selection width set to 99.\n");
1011
        book_selection_width = 99;
1012
        break;
1013
      case 1:
1014
        Print(4095, "choose from book moves randomly (using weights.)\n");
1015
        break;
1016
      default:
1017
        Print(4095, "valid options are 0-1.\n");
1018
        break;
1019
    }
1020
    return;
1021
  } else if (!strcmp(args[1], "trigger")) {
1022
    if (nargs < 3) {
1023
      Print(4095, "usage:  book trigger <n>\n");
1024
      return;
1025
    }
1026
    book_search_trigger = atoi(args[2]);
1027
    Print(4095, "search book moves if the most popular was not played\n");
1028
    Print(4095, "at least %d times.\n", book_search_trigger);
1029
    return;
1030
  } else if (!strcmp(args[1], "width")) {
1031
    if (nargs < 3) {
1032
      Print(4095, "usage:  book width <n>\n");
1033
      return;
1034
    }
1035
    book_selection_width = atoi(args[2]);
1036
    book_random = 1;
1037
    Print(4095, "choose from %d best moves.\n", book_selection_width);
1038
    Print(4095, "  ..book random set to 1.\n");
1039
    return;
1040
  } else {
1041
    Print(4095, "usage:  book [option] [filename] [maxply] [minplay]\n");
1042
    return;
1043
  }
1044
  if (!(book_input = fopen(args[2], "r"))) {
1045
    printf("file %s does not exist.\n", args[2]);
1046
    return;
1047
  }
1048
  ReadPGN(0, 0);
1049
  if (book_file)
1050
    fclose(book_file);
1051
  book_file = fopen(output_filename, "wb+");
1052
  bbuffer = (BB_POSITION *) malloc(sizeof(BB_POSITION) * SORT_BLOCK);
1053
  if (!bbuffer) {
1054
    Print(4095, "Unable to malloc() sort buffer, aborting\n");
1055
    CraftyExit(1);
1056
  }
1057
  fseek(book_file, 0, SEEK_SET);
1058
/*
1059
 ************************************************************
1060
 *                                                          *
1061
 *  Now, read in a series of moves (terminated by the "["   *
1062
 *  of the next title or by "end" for end of the file) and  *
1063
 *  make them.  After each MakeMove(), we can grab the hash *
1064
 *  key, and use it to access the book data file to add     *
1065
 *  this position.  Note that we have to check the last     *
1066
 *  character of a move for the special flags and set the   *
1067
 *  correct bit in the status for this position.  When we   *
1068
 *  reach the end of a book line, we back up to the         *
1069
 *  starting position and start over.                       *
1070
 *                                                          *
1071
 ************************************************************
1072
 */
1073
  major = atoi(version);
1074
  minor = atoi(strchr(version, '.') + 1);
1075
  major = (major << 16) + minor;
1076
  start = !strstr(output_filename, "book.bin");
1077
  printf("parsing pgn move file (100k moves/dot)\n");
1078
  start_elapsed_time = ReadClock();
1079
  if (book_file) {
1080
    total_moves = 0;
1081
    max_search_depth = 0;
1082
    errors = 0;
1083
    do {
1084
      data_read = ReadPGN(book_input, 0);
1085
      if (data_read == -1)
1086
        Print(4095, "end-of-file reached\n");
1087
    } while (data_read == 0);
1088
    do {
1089
      if (data_read < 0) {
1090
        Print(4095, "end-of-file reached\n");
1091
        break;
1092
      }
1093
      if (data_read == 1) {
1094
        if (strstr(buffer, "Site")) {
1095
          games_parsed++;
1096
          result = 3;
1097
        } else if (strstr(buffer, "esult")) {
1098
          if (strstr(buffer, "1-0"))
1099
            result = 2;
1100
          else if (strstr(buffer, "0-1"))
1101
            result = 1;
1102
          else if (strstr(buffer, "1/2-1/2"))
1103
            result = 0;
1104
          else if (strstr(buffer, "*"))
1105
            result = 3;
1106
        }
1107
        data_read = ReadPGN(book_input, 0);
1108
      } else
1109
        do {
1110
          wtm = 1;
1111
          InitializeChessBoard(tree);
1112
          tree->status[1] = tree->status[0];
1113
          move_num = 1;
1114
          tree->status[2] = tree->status[1];
1115
          ply = 0;
1116
          data_read = 0;
1117
#if defined(POSITIONS)
1118
          output_pos = Random32();
1119
          output_pos = (output_pos | (output_pos >> 16)) & 65535;
1120
          output_pos = output_pos % 20 + 8;
1121
          output_wtm = Random32() & 1;
1122
#endif
1123
          while (data_read == 0) {
1124
            mask_word = 0;
1125
            if ((ch = strpbrk(buffer, "?!"))) {
1126
              mask_word = BookMask(ch);
1127
              *ch = 0;
1128
            }
1129
            if (!strchr(buffer, '$') && !strchr(buffer, '*')) {
1130
              if (ply < max_ply)
1131
                move = ReadNextMove(tree, buffer, 2, wtm);
1132
              else {
1133
                move = 0;
1134
                discarded++;
1135
              }
1136
              if (move) {
1137
                ply++;
1138
                max_search_depth = Max(max_search_depth, ply);
1139
                total_moves++;
1140
                common = HashKey & ((uint64_t) 65535 << 48);
108 pmbaty 1141
                MakeMove(tree, 2, wtm, move);
33 pmbaty 1142
                tree->status[2] = tree->status[3];
1143
                if (ply <= max_ply) {
1144
                  temp_hash_key = (wtm) ? HashKey : ~HashKey;
1145
                  temp_hash_key =
1146
                      (temp_hash_key & ~((uint64_t) 65535 << 48)) | common;
1147
                  memcpy(bbuffer[buffered].position, (char *) &temp_hash_key,
1148
                      8);
1149
                  if (result & 1)
1150
                    mask_word |= 0x20;
1151
                  if (result == 0)
1152
                    mask_word |= 0x40;
1153
                  if (result & 2)
1154
                    mask_word |= 0x80;
1155
                  bbuffer[buffered].status = mask_word;
1156
                  bbuffer[buffered++].percent_play =
1157
                      pgn_suggested_percent + (wtm << 7);
1158
                  if (buffered >= SORT_BLOCK) {
1159
                    BookSort(bbuffer, buffered, ++files);
1160
                    buffered = 0;
1161
                    strcpy(schar, "S");
1162
                  }
1163
                }
1164
                if (!(total_moves % 100000)) {
1165
                  printf("%s", schar);
1166
                  strcpy(schar, ".");
1167
                  if (!(total_moves % 6000000))
1168
                    printf(" (%dk)\n", total_moves / 1000);
1169
                  fflush(stdout);
1170
                }
1171
                wtm = Flip(wtm);
1172
                if (wtm)
1173
                  move_num++;
1174
#if defined(POSITIONS)
1175
                if (wtm == output_wtm && move_num == output_pos) {
1176
                  SEARCH_POSITION temp_pos;
1177
                  int twtm;
1178
                  char t_initial_position[256];
1179
 
1180
                  strcpy(t_initial_position, initial_position);
1181
                  temp_pos = tree->status[0];
1182
                  tree->status[0] = tree->status[3];
1183
                  if (Castle(0, white) < 0)
1184
                    Castle(0, white) = 0;
1185
                  if (Castle(0, black) < 0)
1186
                    Castle(0, black) = 0;
1187
                  strcpy(buffer, "savepos *");
1188
                  twtm = game_wtm;
1189
                  game_wtm = wtm;
108 pmbaty 1190
                  Option(tree);
33 pmbaty 1191
                  game_wtm = twtm;
1192
                  fprintf(pout, "%s\n", initial_position);
1193
                  strcpy(initial_position, t_initial_position);
1194
                  tree->status[0] = temp_pos;
1195
                }
1196
#endif
1197
              } else if (strspn(buffer, "0123456789/-.*") != strlen(buffer)
1198
                  && ply < max_ply) {
1199
                errors++;
1200
                Print(4095, "ERROR!  move %d: %s is illegal (line %d)\n",
1201
                    move_num, buffer, ReadPGN(book_input, -2));
1202
                ReadPGN(book_input, -1);
1203
                DisplayChessBoard(stdout, tree->position);
1204
                do {
1205
                  data_read = ReadPGN(book_input, 0);
1206
                  if (data_read == -1)
1207
                    Print(4095, "end-of-file reached\n");
1208
                } while (data_read == 0);
1209
                break;
1210
              }
1211
            }
1212
            data_read = ReadPGN(book_input, 0);
1213
          }
1214
        } while (0);
1215
    } while (strcmp(buffer, "end") && data_read != -1);
1216
    if (book_input != stdin)
1217
      fclose(book_input);
1218
    if (buffered)
1219
      BookSort(bbuffer, buffered, ++files);
1220
    free(bbuffer);
1221
    printf("S  <done>\n");
1222
    if (total_moves == 0) {
1223
      Print(4095, "ERROR - empty input PGN file\n");
1224
      return;
1225
    }
1226
/*
1227
 ************************************************************
1228
 *                                                          *
1229
 *  Now merge these "chunks" into book.bin, keeping all of  *
1230
 *  the "flags" as well as counting the number of times     *
1231
 *  that each move was played.                              *
1232
 *                                                          *
1233
 ************************************************************
1234
 */
1235
    printf("merging sorted files (%d) (100k/dot)\n", files);
1236
    counter = 0;
1237
    index = (int *) malloc(32768 * sizeof(int));
1238
    if (!index) {
1239
      Print(4095, "Unable to malloc() index block, aborting\n");
1240
      CraftyExit(1);
1241
    }
1242
    for (i = 0; i < 32768; i++)
1243
      index[i] = -1;
108 pmbaty 1244
    temp = BookupNextPosition(files, 1);
33 pmbaty 1245
    memcpy((char *) &current.position, temp.position, 8);
1246
    current.status_played = temp.status << 24;
1247
    if (start)
1248
      current.status_played += temp.percent_play & 127;
1249
    current.learn = 0.0;
1250
    played = 1;
1251
    fclose(book_file);
1252
    book_file = fopen(output_filename, "wb+");
1253
    fseek(book_file, sizeof(int) * 32768, SEEK_SET);
1254
    last = current.position >> 49;
1255
    index[last] = ftell(book_file);
1256
    book_positions = 0;
1257
    cluster = 0;
1258
    cluster_seek = sizeof(int) * 32768;
1259
    fseek(book_file, cluster_seek + sizeof(int), SEEK_SET);
1260
    max_cluster = 0;
1261
    wins = 0;
1262
    losses = 0;
1263
    if (temp.status & 128 && temp.percent_play & 128)
1264
      wins++;
1265
    if (temp.status & 128 && !(temp.percent_play & 128))
1266
      losses++;
1267
    if (temp.status & 32 && !(temp.percent_play & 128))
1268
      wins++;
1269
    if (temp.status & 32 && temp.percent_play & 128)
1270
      losses++;
1271
    while (1) {
108 pmbaty 1272
      temp = BookupNextPosition(files, 0);
33 pmbaty 1273
      memcpy((char *) &next.position, temp.position, 8);
1274
      next.status_played = temp.status << 24;
1275
      if (start)
1276
        next.status_played += temp.percent_play & 127;
1277
      next.learn = 0.0;
1278
      counter++;
1279
      if (counter % 100000 == 0) {
1280
        printf(".");
1281
        if (counter % 6000000 == 0)
1282
          printf(" (%dk)\n", counter / 1000);
1283
        fflush(stdout);
1284
      }
1285
      if (current.position == next.position) {
1286
        current.status_played = current.status_played | next.status_played;
1287
        played++;
1288
        if (temp.status & 128 && temp.percent_play & 128)
1289
          wins++;
1290
        if (temp.status & 128 && !(temp.percent_play & 128))
1291
          losses++;
1292
        if (temp.status & 32 && !(temp.percent_play & 128))
1293
          wins++;
1294
        if (temp.status & 32 && temp.percent_play & 128)
1295
          losses++;
1296
      } else {
1297
        if (played >= min_played && wins >= (losses * wl_percent)) {
1298
          book_positions++;
1299
          cluster++;
1300
          max_cluster = Max(max_cluster, cluster);
1301
          if (!start)
1302
            current.status_played += played;
1303
          current.learn = 0.0;
1304
          memcpy((void *) &book_buffer_char[0].position,
1305
              (void *) BookOut64(current.position), 8);
1306
          memcpy((void *) &book_buffer_char[0].status_played,
1307
              (void *) BookOut32(current.status_played), 4);
1308
          memcpy((void *) &book_buffer_char[0].learn,
1309
              (void *) BookOut32((int) current.learn), 4); // Pierre-Marie Baty -- added type cast
108 pmbaty 1310
          stat =
1311
              fwrite(book_buffer_char, sizeof(BOOK_POSITION), 1, book_file);
33 pmbaty 1312
          if (stat != 1)
1313
            Print(4095, "ERROR!  write failed, disk probably full.\n");
1314
        } else if (played < min_played)
1315
          discarded_mp++;
1316
        else
1317
          discarded_lose++;
1318
        if (last != (int) (next.position >> 49)) {
1319
          next_cluster = ftell(book_file);
1320
          fseek(book_file, cluster_seek, SEEK_SET);
1321
          memcpy((void *) &cluster, BookOut32(cluster), 4);
1322
          stat = fwrite(&cluster, sizeof(int), 1, book_file);
1323
          if (stat != 1)
1324
            Print(4095, "ERROR!  write failed, disk probably full.\n");
1325
          if (next.position == 0)
1326
            break;
1327
          fseek(book_file, next_cluster + sizeof(int), SEEK_SET);
1328
          cluster_seek = next_cluster;
1329
          last = next.position >> 49;
1330
          index[last] = next_cluster;
1331
          cluster = 0;
1332
        }
1333
        wins = 0;
1334
        losses = 0;
1335
        if (temp.status & 128 && temp.percent_play & 128)
1336
          wins++;
1337
        if (temp.status & 128 && !(temp.percent_play & 128))
1338
          losses++;
1339
        if (temp.status & 32 && !(temp.percent_play & 128))
1340
          wins++;
1341
        if (temp.status & 32 && temp.percent_play & 128)
1342
          losses++;
1343
        current = next;
1344
        played = 1;
1345
        if (next.position == 0)
1346
          break;
1347
      }
1348
    }
1349
    fseek(book_file, 0, SEEK_SET);
1350
    for (i = 0; i < 32768; i++) {
1351
      memcpy((void *) &cluster, (void *) BookOut32(index[i]), 4);
1352
      fwrite(&cluster, 4, 1, book_file);
1353
    }
1354
    fseek(book_file, 0, SEEK_END);
1355
    memcpy((void *) &cluster, (void *) BookOut32(major), 4);
1356
    fwrite(&cluster, 4, 1, book_file);
1357
/*
1358
 ************************************************************
1359
 *                                                          *
1360
 *  Now clean up, remove the sort.n files, and print the    *
1361
 *  statistics for building the book.                       *
1362
 *                                                          *
1363
 ************************************************************
1364
 */
1365
    for (i = 1; i <= files; i++) {
1366
      sprintf(fname, "sort.%d", i);
1367
      remove(fname);
1368
    }
1369
    free(index);
1370
    start_elapsed_time = ReadClock() - start_elapsed_time;
1371
    Print(4095, "\n\nparsed %d moves (%d games).\n", total_moves,
1372
        games_parsed);
1373
    Print(4095, "found %d errors during parsing.\n", errors);
1374
    Print(4095, "discarded %d moves (maxply=%d).\n", discarded, max_ply);
1375
    Print(4095, "discarded %d moves (minplayed=%d).\n", discarded_mp,
1376
        min_played);
1377
    Print(4095, "discarded %d moves (win/lose=%.1f%%).\n", discarded_lose,
1378
        wl_percent * 100);
1379
    Print(4095, "book contains %d unique positions.\n", book_positions);
1380
    Print(4095, "deepest book line was %d plies.\n", max_search_depth);
1381
    Print(4095, "longest cluster of moves was %d.\n", max_cluster);
1382
    Print(4095, "time used:  %s elapsed.\n", DisplayTime(start_elapsed_time));
1383
  }
1384
  strcpy(initial_position, "");
1385
  InitializeChessBoard(tree);
1386
}
1387
 
1388
/* last modified 02/23/14 */
1389
/*
1390
 *******************************************************************************
1391
 *                                                                             *
1392
 *   BookMask() is used to convert the flags for a book move into an 8 bit     *
1393
 *   mask that is either kept in the file, or is set by the operator to select *
1394
 *   which opening(s) the program is allowed to play.                          *
1395
 *                                                                             *
1396
 *******************************************************************************
1397
 */
1398
int BookMask(char *flags) {
1399
  int i, mask;
1400
 
1401
  mask = 0;
1402
  for (i = 0; i < (int) strlen(flags); i++) {
1403
    if (flags[i] == '?') {
1404
      if (flags[i + 1] == '?') {
1405
        mask = mask | 1;
1406
        i++;
1407
      } else
1408
        mask = mask | 2;
1409
    } else if (flags[i] == '=') {
1410
      mask = mask | 4;
1411
    } else if (flags[i] == '!') {
1412
      if (flags[i + 1] == '!') {
1413
        mask = mask | 16;
1414
        i++;
1415
      } else
1416
        mask = mask | 8;
1417
    }
1418
  }
1419
  return mask;
1420
}
1421
 
1422
/* last modified 02/23/14 */
1423
/*
1424
 *******************************************************************************
1425
 *                                                                             *
1426
 *   BookSort() is called to sort a block of moves after they have been parsed *
1427
 *   and converted to hash keys.                                               *
1428
 *                                                                             *
1429
 *******************************************************************************
1430
 */
1431
void BookSort(BB_POSITION * buffer, int number, int fileno) {
1432
  char fname[16];
1433
  FILE *output_file;
1434
  int stat;
1435
 
108 pmbaty 1436
  qsort((char *) buffer, number, sizeof(BB_POSITION), BookupCompare);
33 pmbaty 1437
  sprintf(fname, "sort.%d", fileno);
1438
  if (!(output_file = fopen(fname, "wb+")))
1439
    printf("ERROR.  unable to open sort output file\n");
1440
  stat = fwrite(buffer, sizeof(BB_POSITION), number, output_file);
1441
  if (stat != number)
1442
    Print(4095, "ERROR!  write failed, disk probably full.\n");
1443
  fclose(output_file);
1444
}
1445
 
1446
/* last modified 02/23/14 */
1447
/*
1448
 *******************************************************************************
1449
 *                                                                             *
108 pmbaty 1450
 *   BookupNextPosition() is the heart of the "merge" operation that is done   *
33 pmbaty 1451
 *   after the chunks of the parsed/hashed move file are sorted.  This code    *
1452
 *   opens the sort.n files, and returns the least (lexically) position key to *
1453
 *   counted/merged into the main book database.                               *
1454
 *                                                                             *
1455
 *******************************************************************************
1456
 */
108 pmbaty 1457
BB_POSITION BookupNextPosition(int files, int init) {
33 pmbaty 1458
  char fname[20];
1459
  static FILE *input_file[100];
1460
  static BB_POSITION *buffer[100];
1461
  static int data_read[100], next[100];
1462
  int i, used;
1463
  BB_POSITION least;
1464
 
1465
  if (init) {
1466
    for (i = 1; i <= files; i++) {
1467
      sprintf(fname, "sort.%d", i);
1468
      if (!(input_file[i] = fopen(fname, "rb"))) {
1469
        printf("unable to open sort.%d file, may be too many files open.\n",
1470
            i);
1471
        CraftyExit(1);
1472
      }
1473
      buffer[i] = (BB_POSITION *) malloc(sizeof(BB_POSITION) * MERGE_BLOCK);
1474
      if (!buffer[i]) {
1475
        printf("out of memory.  aborting. \n");
1476
        CraftyExit(1);
1477
      }
1478
      fseek(input_file[i], 0, SEEK_SET);
1479
      data_read[i] =
1480
          fread(buffer[i], sizeof(BB_POSITION), MERGE_BLOCK, input_file[i]);
1481
      next[i] = 0;
1482
    }
1483
  }
1484
  for (i = 0; i < 8; i++)
1485
    least.position[i] = 0;
1486
  least.status = 0;
1487
  least.percent_play = 0;
1488
  used = -1;
1489
  for (i = 1; i <= files; i++)
1490
    if (data_read[i]) {
1491
      least = buffer[i][next[i]];
1492
      used = i;
1493
      break;
1494
    }
1495
  if (i > files) {
1496
    for (i = 1; i <= files; i++)
1497
      fclose(input_file[i]);
1498
    return least;
1499
  }
1500
  for (i++; i <= files; i++) {
1501
    if (data_read[i]) {
1502
      uint64_t p1, p2;
1503
 
1504
      memcpy((char *) &p1, least.position, 8);
1505
      memcpy((char *) &p2, buffer[i][next[i]].position, 8);
1506
      if (p1 > p2) {
1507
        least = buffer[i][next[i]];
1508
        used = i;
1509
      }
1510
    }
1511
  }
1512
  if (--data_read[used] == 0) {
1513
    data_read[used] =
1514
        fread(buffer[used], sizeof(BB_POSITION), MERGE_BLOCK,
1515
        input_file[used]);
1516
    next[used] = 0;
1517
  } else
1518
    next[used]++;
1519
  return least;
1520
}
1521
 
108 pmbaty 1522
int BookupCompare(const void *pos1, const void *pos2) {
33 pmbaty 1523
  static uint64_t p1, p2;
1524
 
1525
  memcpy((char *) &p1, ((BB_POSITION *) pos1)->position, 8);
1526
  memcpy((char *) &p2, ((BB_POSITION *) pos2)->position, 8);
1527
  if (p1 < p2)
1528
    return -1;
1529
  if (p1 > p2)
1530
    return +1;
1531
  return 0;
1532
}