home *** CD-ROM | disk | FTP | other *** search
/ Dream 57 / Amiga_Dream_57.iso / Amiga / Jeux / Reflexion / Crafty-15.19.lha / crafty-15.19 / src / book.c < prev    next >
C/C++ Source or Header  |  1998-09-16  |  50KB  |  1,252 lines

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