Subversion Repositories Games.Chess Giants

Rev

Rev 33 | Rev 154 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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) {
  44.   static int book_moves[200];
  45.   static BOOK_POSITION start_moves[200];
  46.   static uint64_t selected_key[200];
  47.   static int selected[200];
  48.   static int selected_order_played[200], selected_value[200];
  49.   static int selected_status[200], selected_percent[200],
  50.       book_development[200];
  51.   static int bs_played[200], bs_percent[200];
  52.   static int book_status[200], evaluations[200], bs_learn[200];
  53.   static float bs_value[200], total_value;
  54.   static uint64_t book_key[200], bs_key[200];
  55.   int m1_status, forced = 0, total_percent, play_percentage = 0;
  56.   float tempr;
  57.   int done, i, j, last_move, temp, which, minlv = 999999, maxlv = -999999;
  58.   int maxp = -999999, minev = 999999, maxev = -999999;
  59.   int nflagged, im, value, np, book_ponder_move;
  60.   int cluster, scluster, test, v;
  61.   unsigned char buf32[4];
  62.   uint64_t temp_hash_key, common, tempk;
  63.   int key, nmoves, num_selected, st;
  64.   int percent_played, total_played, total_moves, smoves;
  65.   int distribution;
  66.   int initial_development;
  67.   char *kibitz_p;
  68.  
  69. /*
  70.  ************************************************************
  71.  *                                                          *
  72.  *  If we have been out of book for several moves, return   *
  73.  *  and start the normal tree search.                       *
  74.  *                                                          *
  75.  ************************************************************
  76.  */
  77.   if (moves_out_of_book > 6)
  78.     return 0;
  79. /*
  80.  ************************************************************
  81.  *                                                          *
  82.  *  Position is known, read the start book file and save    *
  83.  *  each move found.  These will be used later to augment   *
  84.  *  the flags in the normal book to offer better control.   *
  85.  *                                                          *
  86.  ************************************************************
  87.  */
  88.   test = HashKey >> 49;
  89.   smoves = 0;
  90.   if (books_file) {
  91.     fseek(books_file, test * sizeof(int), SEEK_SET);
  92.     v = fread(buf32, 4, 1, books_file);
  93.     if (v <= 0)
  94.       perror("Book() fread error: ");
  95.     key = BookIn32(buf32);
  96.     if (key > 0) {
  97.       fseek(books_file, key, SEEK_SET);
  98.       v = fread(buf32, 4, 1, books_file);
  99.       if (v <= 0)
  100.         perror("Book() fread error: ");
  101.       scluster = BookIn32(buf32);
  102.       if (scluster)
  103.         BookClusterIn(books_file, scluster, book_buffer);
  104.       for (im = 0; im < n_root_moves; im++) {
  105.         common = HashKey & ((uint64_t) 65535 << 48);
  106.         MakeMove(tree, 1, wtm, root_moves[im].move);
  107.         if (Repeat(tree, 2)) {
  108.           UnmakeMove(tree, 1, wtm, root_moves[im].move);
  109.           return 0;
  110.         }
  111.         temp_hash_key = (wtm) ? HashKey : ~HashKey;
  112.         temp_hash_key = (temp_hash_key & ~((uint64_t) 65535 << 48)) | common;
  113.         for (i = 0; i < scluster; i++)
  114.           if (!(temp_hash_key ^ book_buffer[i].position)) {
  115.             start_moves[smoves++] = book_buffer[i];
  116.             break;
  117.           }
  118.         UnmakeMove(tree, 1, wtm, root_moves[im].move);
  119.       }
  120.     }
  121.   }
  122. /*
  123.  ************************************************************
  124.  *                                                          *
  125.  *  Position is known, read in the appropriate cluster.     *
  126.  *  Note that this cluster will have all possible book      *
  127.  *  moves from current position in it (as well as others of *
  128.  *  course.)                                                *
  129.  *                                                          *
  130.  ************************************************************
  131.  */
  132.   test = HashKey >> 49;
  133.   if (book_file) {
  134.     fseek(book_file, test * sizeof(int), SEEK_SET);
  135.     v = fread(buf32, 4, 1, book_file);
  136.     if (v <= 0)
  137.       perror("Book() fread error: ");
  138.     key = BookIn32(buf32);
  139.     if (key > 0) {
  140.       book_learn_seekto = key;
  141.       fseek(book_file, key, SEEK_SET);
  142.       v = fread(buf32, 4, 1, book_file);
  143.       if (v <= 0)
  144.         perror("Book() fread error: ");
  145.       cluster = BookIn32(buf32);
  146.       if (cluster)
  147.         BookClusterIn(book_file, cluster, book_buffer);
  148.     } else
  149.       cluster = 0;
  150.     if (!cluster && !smoves)
  151.       return 0;
  152. /*
  153.  ************************************************************
  154.  *                                                          *
  155.  *  Now add any moves from books.bin to the end of the      *
  156.  *  cluster so that they will be played even if not in the  *
  157.  *  regular database of moves.                              *
  158.  *                                                          *
  159.  ************************************************************
  160.  */
  161.     for (i = 0; i < smoves; i++) {
  162.       for (j = 0; j < cluster; j++)
  163.         if (!(book_buffer[j].position ^ start_moves[i].position))
  164.           break;
  165.       if (j >= cluster) {
  166.         book_buffer[cluster] = start_moves[i];
  167.         book_buffer[cluster].status_played =
  168.             book_buffer[cluster].status_played & 037700000000;
  169.         cluster++;
  170.       }
  171.     }
  172. /*
  173.  ************************************************************
  174.  *                                                          *
  175.  *  First cycle through the root move list, make each move, *
  176.  *  and see if the resulting hash key is in the book        *
  177.  *  database.                                               *
  178.  *                                                          *
  179.  ************************************************************
  180.  */
  181.     initial_development = tree->score_mg;
  182.     EvaluateCastling(tree, 1, wtm);
  183.     initial_development = tree->score_mg - initial_development;
  184.     total_moves = 0;
  185.     nmoves = 0;
  186.     for (im = 0; im < n_root_moves; im++) {
  187.       common = HashKey & ((uint64_t) 65535 << 48);
  188.       MakeMove(tree, 1, wtm, root_moves[im].move);
  189.       if (Repeat(tree, 2)) {
  190.         UnmakeMove(tree, 1, wtm, root_moves[im].move);
  191.         return 0;
  192.       }
  193.       temp_hash_key = (wtm) ? HashKey : ~HashKey;
  194.       temp_hash_key = (temp_hash_key & ~((uint64_t) 65535 << 48)) | common;
  195.       for (i = 0; i < cluster; i++) {
  196.         if (!(temp_hash_key ^ book_buffer[i].position)) {
  197.           book_status[nmoves] = book_buffer[i].status_played >> 24;
  198.           bs_played[nmoves] = book_buffer[i].status_played & 077777777;
  199.           bs_learn[nmoves] = (int) (book_buffer[i].learn * 100.0);
  200.           if (puzzling)
  201.             bs_played[nmoves] += 1;
  202.           tree->curmv[1] = root_moves[im].move;
  203.           if (!Captured(root_moves[im].move)) {
  204.             book_development[nmoves] = tree->score_mg;
  205.             EvaluateCastling(tree, 2, wtm);
  206.             book_development[nmoves] =
  207.                 tree->score_mg - book_development[nmoves];
  208.           } else
  209.             book_development[nmoves] = 0;
  210.           total_moves += bs_played[nmoves];
  211.           evaluations[nmoves] = Evaluate(tree, 2, wtm, -99999, 99999);
  212.           evaluations[nmoves] -= MaterialSTM(wtm);
  213.           bs_percent[nmoves] = 0;
  214.           for (j = 0; j < smoves; j++) {
  215.             if (!(book_buffer[i].position ^ start_moves[j].position)) {
  216.               book_status[nmoves] |= start_moves[j].status_played >> 24;
  217.               bs_percent[nmoves] = start_moves[j].status_played & 077777777;
  218.               break;
  219.             }
  220.           }
  221.           book_moves[nmoves] = root_moves[im].move;
  222.           book_key[nmoves] = temp_hash_key;
  223.           nmoves++;
  224.           break;
  225.         }
  226.       }
  227.       UnmakeMove(tree, 1, wtm, root_moves[im].move);
  228.     }
  229.     if (!nmoves)
  230.       return 0;
  231.     book_learn_nmoves = nmoves;
  232. /*
  233.  ************************************************************
  234.  *                                                          *
  235.  *  If any moves have a very bad or a very good learn       *
  236.  *  value, set the appropriate ? or ! flag so the move be   *
  237.  *  played or avoided as appropriate.                       *
  238.  *                                                          *
  239.  ************************************************************
  240.  */
  241.     for (i = 0; i < nmoves; i++)
  242.       if (!(book_status[i] & BAD_MOVE))
  243.         maxp = Max(maxp, bs_played[i]);
  244.     for (i = 0; i < nmoves; i++) {
  245.       if (bs_learn[i] <= LEARN_COUNTER_BAD && !bs_percent[i]
  246.           && !(book_status[i] & 0x18))
  247.         book_status[i] |= BAD_MOVE;
  248.       if (wtm && !(book_status[i] & 0x80) && !bs_percent[i]
  249.           && !(book_status[i] & 0x18))
  250.         book_status[i] |= BAD_MOVE;
  251.       if (!wtm && !(book_status[i] & 0x20) && !bs_percent[i]
  252.           && !(book_status[i] & 0x18))
  253.         book_status[i] |= BAD_MOVE;
  254.       if (bs_played[i] < maxp / 10 && !bs_percent[i] && book_random &&
  255.           !(book_status[i] & 0x18))
  256.         book_status[i] |= BAD_MOVE;
  257.       if (bs_learn[i] >= LEARN_COUNTER_GOOD && !(book_status[i] & 0x03))
  258.         book_status[i] |= GOOD_MOVE;
  259.       if (bs_percent[i])
  260.         book_status[i] |= GOOD_MOVE;
  261.     }
  262. /*
  263.  ************************************************************
  264.  *                                                          *
  265.  *  We have the book moves, now it's time to decide how     *
  266.  *  they are supposed to be sorted and compute the sort     *
  267.  *  index.                                                  *
  268.  *                                                          *
  269.  ************************************************************
  270.  */
  271.     for (i = 0; i < nmoves; i++) {
  272.       if (!(book_status[i] & BAD_MOVE)) {
  273.         minlv = Min(minlv, bs_learn[i]);
  274.         maxlv = Max(maxlv, bs_learn[i]);
  275.         minev = Min(minev, evaluations[i]);
  276.         maxev = Max(maxev, evaluations[i]);
  277.         maxp = Max(maxp, bs_played[i]);
  278.       }
  279.     }
  280.     maxp++;
  281.     for (i = 0; i < nmoves; i++) {
  282.       bs_value[i] = 1;
  283.       bs_value[i] += bs_played[i] / (float) maxp *1000.0f * book_weight_freq; // Pierre-Marie Baty -- added type cast
  284.  
  285.       if (minlv < maxlv)
  286.         bs_value[i] +=
  287.             (bs_learn[i] - minlv) / (float) (maxlv -
  288.             minlv) * 1000.0f * book_weight_learn; // Pierre-Marie Baty -- added type cast
  289.       if (minev < maxev)
  290.         bs_value[i] +=
  291.             (evaluations[i] - minev) / (float) (Max(maxev - minev,
  292.                 50)) * 1000.0f * book_weight_eval; // Pierre-Marie Baty -- added type cast
  293.     }
  294.     total_played = total_moves;
  295. /*
  296.  ************************************************************
  297.  *                                                          *
  298.  *  If there are any ! moves, make their popularity count   *
  299.  *  huge since they have to be considered.                  *
  300.  *                                                          *
  301.  ************************************************************
  302.  */
  303.     for (i = 0; i < nmoves; i++)
  304.       if (book_status[i] & 0x18)
  305.         break;
  306.     if (i < nmoves) {
  307.       for (i = 0; i < nmoves; i++) {
  308.         if (book_status[i] & 0x18)
  309.           bs_value[i] += 8000.0;
  310.         if (!(book_status[i] & 0x03))
  311.           bs_value[i] += 4000.0;
  312.       }
  313.     }
  314. /*
  315.  ************************************************************
  316.  *                                                          *
  317.  *  Now sort the moves based on the complete sort value.    *
  318.  *                                                          *
  319.  ************************************************************
  320.  */
  321.     if (nmoves)
  322.       do {
  323.         done = 1;
  324.         for (i = 0; i < nmoves - 1; i++) {
  325.           if (bs_percent[i] < bs_percent[i + 1]
  326.               || (bs_percent[i] == bs_percent[i + 1]
  327.                   && bs_value[i] < bs_value[i + 1])) {
  328.             tempr = (float) bs_played[i]; // Pierre-Marie Baty -- added type cast (FIXME: ugly!)
  329.             bs_played[i] = bs_played[i + 1];
  330.             bs_played[i + 1] = (int) tempr; // Pierre-Marie Baty -- added type cast (FIXME: ugly!)
  331.             tempr = bs_value[i];
  332.             bs_value[i] = bs_value[i + 1];
  333.             bs_value[i + 1] = tempr;
  334.             temp = evaluations[i];
  335.             evaluations[i] = evaluations[i + 1];
  336.             evaluations[i + 1] = temp;
  337.             temp = bs_learn[i];
  338.             bs_learn[i] = bs_learn[i + 1];
  339.             bs_learn[i + 1] = temp;
  340.             temp = book_development[i];
  341.             book_development[i] = book_development[i + 1];
  342.             book_development[i + 1] = temp;
  343.             temp = book_moves[i];
  344.             book_moves[i] = book_moves[i + 1];
  345.             book_moves[i + 1] = temp;
  346.             temp = book_status[i];
  347.             book_status[i] = book_status[i + 1];
  348.             book_status[i + 1] = temp;
  349.             temp = bs_percent[i];
  350.             bs_percent[i] = bs_percent[i + 1];
  351.             bs_percent[i + 1] = temp;
  352.             tempk = book_key[i];
  353.             book_key[i] = book_key[i + 1];
  354.             book_key[i + 1] = tempk;
  355.             done = 0;
  356.           }
  357.         }
  358.       } while (!done);
  359. /*
  360.  ************************************************************
  361.  *                                                          *
  362.  *  Display the book moves, and total counts, etc. if the   *
  363.  *  operator has requested it.                              *
  364.  *                                                          *
  365.  ************************************************************
  366.  */
  367.     if (show_book) {
  368.       Print(32, "  after screening, the following moves can be played\n");
  369.       Print(32,
  370.           "  move     played    %%  score    learn    " "sortv   P%%  P\n");
  371.       for (i = 0; i < nmoves; i++) {
  372.         Print(32, "%6s", OutputMove(tree, 1, wtm, book_moves[i]));
  373.         st = book_status[i];
  374.         if (st & 0x1f) {
  375.           if (st & 0x01)
  376.             Print(32, "??");
  377.           else if (st & 0x02)
  378.             Print(32, "? ");
  379.           else if (st & 0x04)
  380.             Print(32, "= ");
  381.           else if (st & 0x08)
  382.             Print(32, "! ");
  383.           else if (st & 0x10)
  384.             Print(32, "!!");
  385.         } else
  386.           Print(32, "  ");
  387.         Print(32, "   %6d", bs_played[i]);
  388.         Print(32, "  %3d", 100 * bs_played[i] / Max(total_moves, 1));
  389.         Print(32, "%s", DisplayEvaluation(evaluations[i], wtm));
  390.         Print(32, "%9.2f", (float) bs_learn[i] / 100.0);
  391.         Print(32, " %9.1f", bs_value[i]);
  392.         Print(32, " %3d", bs_percent[i]);
  393.         if ((book_status[i] & book_accept_mask &&
  394.                 !(book_status[i] & book_reject_mask))
  395.             || (!(book_status[i] & book_reject_mask) && (bs_percent[i]
  396.                     || book_status[i] & 0x18 || (wtm && book_status[i] & 0x80)
  397.                     || (!wtm && book_status[i] & 0x20))))
  398.           Print(32, "  Y");
  399.         else
  400.           Print(32, "  N");
  401.         Print(32, "\n");
  402.       }
  403.     }
  404. /*
  405.  ************************************************************
  406.  *                                                          *
  407.  *  Check for book moves with the play % value set.  if     *
  408.  *  there are any such moves, then exclude all moves that   *
  409.  *  do not have a play % or a !/!! flag set.                *
  410.  *                                                          *
  411.  ************************************************************
  412.  */
  413.     for (i = 0; i < nmoves; i++)
  414.       if (bs_percent[i])
  415.         play_percentage = 1;
  416. /*
  417.  ************************************************************
  418.  *                                                          *
  419.  *  Delete ? and ?? moves first, which includes those moves *
  420.  *  with bad learned results.  Here is where we also        *
  421.  *  exclude moves with no play % if we find at least one    *
  422.  *  with a non-zero value.                                  *
  423.  *                                                          *
  424.  ************************************************************
  425.  */
  426.     num_selected = 0;
  427.     if (!play_percentage) {
  428.       for (i = 0; i < nmoves; i++)
  429.         if (!(book_status[i] & 0x03) || bs_percent[i]) {
  430.           selected_status[num_selected] = book_status[i];
  431.           selected_order_played[num_selected] = bs_played[i];
  432.           selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
  433.           selected_percent[num_selected] = bs_percent[i];
  434.           selected_key[num_selected] = book_key[i];
  435.           selected[num_selected++] = book_moves[i];
  436.         }
  437.     } else {
  438.       for (i = 0; i < nmoves; i++)
  439.         if (bs_percent[i]) {
  440.           selected_status[num_selected] = book_status[i];
  441.           selected_order_played[num_selected] = bs_played[i];
  442.           selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
  443.           selected_percent[num_selected] = bs_percent[i];
  444.           selected_key[num_selected] = book_key[i];
  445.           selected[num_selected++] = book_moves[i];
  446.         }
  447.     }
  448.     for (i = 0; i < num_selected; i++) {
  449.       book_status[i] = selected_status[i];
  450.       bs_played[i] = selected_order_played[i];
  451.       bs_value[i] = (float) selected_value[i]; // Pierre-Marie Baty -- added type cast
  452.       bs_percent[i] = selected_percent[i];
  453.       book_moves[i] = selected[i];
  454.     }
  455.     nmoves = num_selected;
  456. /*
  457.  ************************************************************
  458.  *                                                          *
  459.  *  If this is a real search (not a puzzling search to      *
  460.  *  find a move by the opponent to ponder) then we need to  *
  461.  *  set up the whisper info for later.                      *
  462.  *                                                          *
  463.  ************************************************************
  464.  */
  465.     if (!puzzling)
  466.       do {
  467.         kibitz_text[0] = '\0';
  468.         if (!nmoves)
  469.           break;
  470.         sprintf(kibitz_text, "book moves (");
  471.         kibitz_p = kibitz_text + strlen(kibitz_text);
  472.         for (i = 0; i < nmoves; i++) {
  473.           sprintf(kibitz_p, "%s %d%%", OutputMove(tree, 1, wtm,
  474.                   book_moves[i]), 100 * bs_played[i] / Max(total_played, 1));
  475.           kibitz_p = kibitz_text + strlen(kibitz_text);
  476.           if (i < nmoves - 1) {
  477.             sprintf(kibitz_p, ", ");
  478.             kibitz_p = kibitz_text + strlen(kibitz_text);
  479.           }
  480.         }
  481.         sprintf(kibitz_p, ")\n");
  482.       } while (0);
  483. /*
  484.  ************************************************************
  485.  *                                                          *
  486.  *  Now select a move from the set of moves just found. Do  *
  487.  *  this in three distinct passes:  (1) look for !! moves;  *
  488.  *  (2) look for ! moves;  (3) look for any other moves.    *
  489.  *  Note: book_accept_mask *should* have a bit set for any  *
  490.  *  move that is selected, including !! and ! type moves so *
  491.  *  that they *can* be excluded if desired.  Note also that *
  492.  *  book_reject_mask should have ?? and ? set (at a         *
  493.  *  minimum) to exclude these types of moves.               *
  494.  *                                                          *
  495.  ************************************************************
  496.  */
  497.     num_selected = 0;
  498.     if (!num_selected && !puzzling)
  499.       if (book_accept_mask & 16)
  500.         for (i = 0; i < nmoves; i++)
  501.           if (book_status[i] & 16) {
  502.             forced = 1;
  503.             selected_status[num_selected] = book_status[i];
  504.             selected_order_played[num_selected] = bs_played[i];
  505.             selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
  506.             selected_key[num_selected] = book_key[i];
  507.             selected[num_selected++] = book_moves[i];
  508.           }
  509.     if (!num_selected && !puzzling)
  510.       if (book_accept_mask & 8)
  511.         for (i = 0; i < nmoves; i++)
  512.           if (book_status[i] & 8) {
  513.             forced = 1;
  514.             selected_status[num_selected] = book_status[i];
  515.             selected_order_played[num_selected] = bs_played[i];
  516.             selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
  517.             selected_key[num_selected] = book_key[i];
  518.             selected[num_selected++] = book_moves[i];
  519.           }
  520.     if (!num_selected && !puzzling)
  521.       if (book_accept_mask & 4)
  522.         for (i = 0; i < nmoves; i++)
  523.           if (book_status[i] & 4) {
  524.             selected_status[num_selected] = book_status[i];
  525.             selected_order_played[num_selected] = bs_played[i];
  526.             selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
  527.             selected_key[num_selected] = book_key[i];
  528.             selected[num_selected++] = book_moves[i];
  529.           }
  530.     if (!num_selected && !puzzling)
  531.       for (i = 0; i < nmoves; i++)
  532.         if (book_status[i] & book_accept_mask) {
  533.           selected_status[num_selected] = book_status[i];
  534.           selected_order_played[num_selected] = bs_played[i];
  535.           selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
  536.           selected_key[num_selected] = book_key[i];
  537.           selected[num_selected++] = book_moves[i];
  538.         }
  539.     if (!num_selected)
  540.       for (i = 0; i < nmoves; i++) {
  541.         selected_status[num_selected] = book_status[i];
  542.         selected_order_played[num_selected] = bs_played[i];
  543.         selected_value[num_selected] = (int) bs_value[i]; // Pierre-Marie Baty -- added type cast
  544.         selected_key[num_selected] = book_key[i];
  545.         selected[num_selected++] = book_moves[i];
  546.       }
  547.     if (!num_selected)
  548.       return 0;
  549.     for (i = 0; i < num_selected; i++) {
  550.       book_status[i] = selected_status[i];
  551.       book_moves[i] = selected[i];
  552.       bs_played[i] = selected_order_played[i];
  553.       bs_value[i] = (float) selected_value[i]; // Pierre-Marie Baty -- added type cast
  554.       bs_key[i] = selected_key[i];
  555.     }
  556.     nmoves = num_selected;
  557.     if (nmoves == 0)
  558.       return 0;
  559.     Print(32, "               book moves {");
  560.     for (i = 0; i < nmoves; i++) {
  561.       Print(32, "%s", OutputMove(tree, 1, wtm, book_moves[i]));
  562.       if (i < nmoves - 1)
  563.         Print(32, ", ");
  564.     }
  565.     Print(32, "}\n");
  566.     nflagged = 0;
  567.     for (i = 0; i < nmoves; i++)
  568.       if (book_status[i] & 8)
  569.         nflagged++;
  570.     nmoves = Max(Min(nmoves, book_selection_width), nflagged);
  571.     if (show_book) {
  572.       Print(32, "               moves considered {");
  573.       for (i = 0; i < nmoves; i++) {
  574.         Print(32, "%s", OutputMove(tree, 1, wtm, book_moves[i]));
  575.         if (i < nmoves - 1)
  576.           Print(32, ", ");
  577.       }
  578.       Print(32, "}\n");
  579.     }
  580. /*
  581.  ************************************************************
  582.  *                                                          *
  583.  *  We have the book moves, if any have specified percents  *
  584.  *  for play, then adjust the bs_value[] to reflect this    *
  585.  *  percentage.                                             *
  586.  *                                                          *
  587.  ************************************************************
  588.  */
  589.     total_value = 0.0;
  590.     total_percent = 0;
  591.     for (i = 0; i < nmoves; i++) {
  592.       if (!bs_percent[i])
  593.         total_value += bs_value[i];
  594.       total_percent += bs_percent[i];
  595.     }
  596.     if (fabs(total_value) < 0.0001)
  597.       total_value = 1000.0;
  598.     total_percent = (total_percent > 99) ? 99 : total_percent;
  599.     for (i = 0; i < nmoves; i++)
  600.       if (bs_percent[i])
  601.         bs_value[i] =
  602.             total_value / (1.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
  604. /*
  605.  ************************************************************
  606.  *                                                          *
  607.  *  Display the book moves, and total counts, etc. if the   *
  608.  *  operator has requested it.                              *
  609.  *                                                          *
  610.  ************************************************************
  611.  */
  612.     if (show_book) {
  613.       Print(32, "  move     played    %%  score     sortv  P%%  P\n");
  614.       for (i = 0; i < nmoves; i++) {
  615.         Print(32, "%6s", OutputMove(tree, 1, wtm, book_moves[i]));
  616.         st = book_status[i];
  617.         if (st & 0x1f) {
  618.           if (st & 0x01)
  619.             Print(32, "??");
  620.           else if (st & 0x02)
  621.             Print(32, "? ");
  622.           else if (st & 0x04)
  623.             Print(32, "= ");
  624.           else if (st & 0x08)
  625.             Print(32, "! ");
  626.           else if (st & 0x10)
  627.             Print(32, "!!");
  628.         } else
  629.           Print(32, "  ");
  630.         Print(32, "   %6d", bs_played[i]);
  631.         Print(32, "  %3d", 100 * bs_played[i] / Max(total_moves, 1));
  632.         Print(32, "%s", DisplayEvaluation(evaluations[i], wtm));
  633.         Print(32, " %9.1f", bs_value[i]);
  634.         Print(32, " %3d", bs_percent[i]);
  635.         if ((book_status[i] & book_accept_mask &&
  636.                 !(book_status[i] & book_reject_mask))
  637.             || (!(book_status[i] & book_reject_mask) && ((wtm &&
  638.                         book_status[i] & 0x80) || (!wtm &&
  639.                         book_status[i] & 0x20))))
  640.           Print(32, "  Y");
  641.         else
  642.           Print(32, "  N");
  643.         Print(32, "\n");
  644.       }
  645.     }
  646. /*
  647.  ************************************************************
  648.  *                                                          *
  649.  *  If random=0, then we search the set of legal book moves *
  650.  *  with the normal search engine (but with a short time    *
  651.  *  limit) to choose among them.                            *
  652.  *                                                          *
  653.  ************************************************************
  654.  */
  655.     if (nmoves && (!puzzling || mode != tournament_mode)) {
  656.       np = bs_played[nmoves - 1];
  657.       if (!puzzling && (!book_random || (mode == tournament_mode &&
  658.                   np < book_search_trigger))) {
  659.         if (!forced) {
  660.           n_root_moves = nmoves;
  661.           for (i = 0; i < n_root_moves; i++) {
  662.             root_moves[i].move = book_moves[i];
  663.             root_moves[i].status = 0;
  664.           }
  665.           last_pv.pathd = 0;
  666.           booking = 1;
  667.           value = Iterate(wtm, booking, 1);
  668.           booking = 0;
  669.           if (value < -50) {
  670.             last_pv.pathd = 0;
  671.             return 0;
  672.           }
  673.         } else {
  674.           tree->pv[0].path[1] = book_moves[0];
  675.           tree->pv[0].pathl = 2;
  676.           tree->pv[0].pathd = 0;
  677.         }
  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;
  702.       Print(32, "               moves considered {only non-book moves}\n");
  703.       nmoves = j;
  704.       if (nmoves > 1) {
  705.         last_pv.pathd = 0;
  706.         booking = 1;
  707.         Iterate(wtm, booking, 1);
  708.         booking = 0;
  709.       } else {
  710.         tree->pv[0].path[1] = book_moves[0];
  711.         tree->pv[0].pathl = 2;
  712.         tree->pv[0].pathd = 0;
  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);
  747.     tree->pv[0].path[1] = book_moves[which];
  748.     percent_played = 100 * bs_played[which] / Max(total_played, 1);
  749.     total_played = bs_played[which];
  750.     m1_status = book_status[which];
  751.     tree->pv[0].pathl = 2;
  752.     tree->pv[0].pathd = 0;
  753.     if (mode != tournament_mode) {
  754.       MakeMove(tree, 1, wtm, book_moves[which]);
  755.       if ((book_ponder_move = BookPonderMove(tree, Flip(wtm)))) {
  756.         tree->pv[0].path[2] = book_ponder_move;
  757.         tree->pv[0].pathl = 3;
  758.       }
  759.       UnmakeMove(tree, 1, wtm, book_moves[which]);
  760.     }
  761.     book_learn_key = bs_key[which];
  762.     Print(32, "               book   0.0s    %3d%%   ", percent_played);
  763.     Print(32, " %s", OutputMove(tree, 1, wtm, tree->pv[0].path[1]));
  764.     st = m1_status & book_accept_mask & (~224);
  765.     if (st) {
  766.       if (st & 1)
  767.         Print(32, "??");
  768.       else if (st & 2)
  769.         Print(32, "?");
  770.       else if (st & 4)
  771.         Print(32, "=");
  772.       else if (st & 8)
  773.         Print(32, "!");
  774.       else if (st & 16)
  775.         Print(32, "!!");
  776.     }
  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");
  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;
  801.   static unsigned book_moves[200];
  802.   int i, v, key, cluster, n_moves, im, played, tplayed;
  803.   unsigned *lastm;
  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);
  820.     v = fread(buf32, 4, 1, book_file);
  821.     if (v <= 0)
  822.       perror("Book() fread error: ");
  823.     key = BookIn32(buf32);
  824.     if (key > 0) {
  825.       fseek(book_file, key, SEEK_SET);
  826.       v = fread(buf32, 4, 1, book_file);
  827.       if (v <= 0)
  828.         perror("Book() fread error: ");
  829.       cluster = BookIn32(buf32);
  830.       if (cluster)
  831.         BookClusterIn(book_file, cluster, book_buffer);
  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);
  851.       MakeMove(tree, 2, wtm, book_moves[im]);
  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.       }
  864.       UnmakeMove(tree, 2, wtm, book_moves[im]);
  865.     }
  866.   }
  867.   return book_ponder_move;
  868. }
  869.  
  870. /* last modified 05/08/14 */
  871. /*
  872.  *******************************************************************************
  873.  *                                                                             *
  874.  *   Bookup() is used to create/add to the opening book file.  typing "<file>  *
  875.  *   create" will erase the old book file and start from scratch,              *
  876.  *                                                                             *
  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.                          *
  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.  *                                                                             *
  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.                                                *
  911.  *                                                                             *
  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.                            *
  915.  *                                                                             *
  916.  *******************************************************************************
  917.  */
  918. void Bookup(TREE * RESTRICT tree, int nargs, char **args) {
  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);
  1141.                 MakeMove(tree, 2, wtm, move);
  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;
  1190.                   Option(tree);
  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;
  1244.     temp = BookupNextPosition(files, 1);
  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) {
  1272.       temp = BookupNextPosition(files, 0);
  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
  1310.           stat =
  1311.               fwrite(book_buffer_char, sizeof(BOOK_POSITION), 1, book_file);
  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.  
  1436.   qsort((char *) buffer, number, sizeof(BB_POSITION), BookupCompare);
  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.  *                                                                             *
  1450.  *   BookupNextPosition() is the heart of the "merge" operation that is done   *
  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.  */
  1457. BB_POSITION BookupNextPosition(int files, int init) {
  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.  
  1522. int BookupCompare(const void *pos1, const void *pos2) {
  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. }
  1533.