Subversion Repositories Games.Chess Giants

Rev

Rev 108 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "chess.h"
  2. #include "data.h"
  3. /* last modified 08/28/16 */
  4. /*
  5.  *******************************************************************************
  6.  *                                                                             *
  7.  *   HashProbe() is used to retrieve entries from the transposition table so   *
  8.  *   this sub-tree won't have to be searched again if we reach a position that *
  9.  *   has been searched previously.  A transposition table position contains    *
  10.  *   the following data packed into 128 bits with each item taking the number  *
  11.  *   of bits given in the table below:                                         *
  12.  *                                                                             *
  13.  *     shr  bits   name   description                                          *
  14.  *      55    9    age    search id to identify old trans/ref entries.         *
  15.  *      53    2    type   0->value is worthless; 1-> value represents a        *
  16.  *                        fail-low bound; 2-> value represents a fail-high     *
  17.  *                        bound; 3-> value is an exact score.                  *
  18.  *      32   21    move   best move from the current position, according to    *
  19.  *                        the search at the time this position was stored.     *
  20.  *      17   15    draft  the depth of the search below this position, which   *
  21.  *                        is used to see if we can use this entry at the       *
  22.  *                        current position.                                    *
  23.  *       0   17    value  unsigned integer value of this position + 65536.     *
  24.  *                        this might be a good score or search bound.          *
  25.  *       0   64    key    64 bit hash signature, used to verify that this      *
  26.  *                        entry goes with the current board position.          *
  27.  *                                                                             *
  28.  *   The underlying scheme here is that we use a "bucket" of N entries.  In    *
  29.  *   HashProbe() we simply compare against each of the four entries for a      *
  30.  *   match.  Each "bucket" is carefully aligned to a 64-byte boundary so that  *
  31.  *   the bucket fits into a single cache line for efficiency.  The bucket size *
  32.  *   (N) is currently set to 4.                                                *
  33.  *                                                                             *
  34.  *   Crafty uses the lockless hashing approach to avoid lock overhead in the   *
  35.  *   hash table accessing (reading or writing).  What we do is store the key   *
  36.  *   and the information in two successive writes to memory.  But since there  *
  37.  *   is nothing that prevents another CPU from interlacing its writes with     *
  38.  *   ours, we want to make sure that the bound/draft/etc really goes with the  *
  39.  *   key.  Consider thread 1 trying to store A1 and A2 in two successive 64    *
  40.  *   words, while thread 2 is trying to store B1 and B2.  Since the two cpus   *
  41.  *   are fully independent, we could end up with {A1,A2}, {A1,B2}, {B1,A2} or  *
  42.  *   {B1,B2}.  The two cases with one word of entry A and one word of entry B  *
  43.  *   are problematic since the information part does not belong with the       *
  44.  *   signature part, and a hash hit (signature match) will retrieve data that  *
  45.  *   does not match the position.  Let's assume that the first word is the     *
  46.  *   signature (A1 or B1) and the second word is the data (A2 or B2).  What we *
  47.  *   do is store A1^A2 (exclusive-or the two parts) in the 1 (key) slot of the *
  48.  *   entry, and store A2 in the data part.  Now, before we try to compare the  *
  49.  *   signatures, we have to "un-corrupt" the stored signature by again using   *
  50.  *   xor, since A1^A2^A2 gives us the original A1 signature again.  But if we  *
  51.  *   store A1^A2, and the data part gets replaced by B2, then we try to match  *
  52.  *   against A1^A2^B2 and that won't match unless we are lucky and A2 == B2    *
  53.  *   which means the match is OK anyway.  This eliminates the need to lock the *
  54.  *   hash table while storing the two values, which would be a big performance *
  55.  *   hit since hash entries are probed/stored in almost every node of the tree *
  56.  *   except for the quiescence search.                                         *
  57.  *                                                                             *
  58.  *******************************************************************************
  59.  */
  60. int HashProbe(TREE * RESTRICT tree, int ply, int depth, int side, int alpha,
  61.     int beta, int *value) {
  62.   HASH_ENTRY *htable;
  63.   HPATH_ENTRY *ptable;
  64.   uint64_t word1, word2, temp_hashkey;
  65.   int type, draft, avoid_null = 0, val, entry, i;
  66.  
  67. /*
  68.  ************************************************************
  69.  *                                                          *
  70.  *  All we have to do is loop through four entries to see   *
  71.  *  if there is a signature match.  There can only be one   *
  72.  *  instance of any single signature, so the first match is *
  73.  *  all we need.                                            *
  74.  *                                                          *
  75.  ************************************************************
  76.  */
  77.   tree->hash_move[ply] = 0;
  78.   temp_hashkey = (side) ? HashKey : ~HashKey;
  79.   htable = hash_table + (temp_hashkey & hash_mask);
  80.   for (entry = 0; entry < 4; entry++) {
  81.     word1 = htable[entry].word1;
  82.     word2 = htable[entry].word2 ^ word1;
  83.     if (word2 == temp_hashkey)
  84.       break;
  85.   }
  86. /*
  87.  ************************************************************
  88.  *                                                          *
  89.  *  If we found a match, we have to verify that the draft   *
  90.  *  is at least equal to the current depth, if not higher,  *
  91.  *  and that the bound/score will let us terminate the      *
  92.  *  search early.                                           *
  93.  *                                                          *
  94.  *  We also return an "avoid_null" status if the matched    *
  95.  *  entry does not have enough draft to terminate the       *
  96.  *  current search but does have enough draft to prove that *
  97.  *  a null-move search would not fail high.  This avoids    *
  98.  *  the null-move search overhead in positions where it is  *
  99.  *  simply a waste of time to try it.                       *
  100.  *                                                          *
  101.  *  If this is an EXACT entry, we are going to store the PV *
  102.  *  in a safe place so that if we get a hit on this entry,  *
  103.  *  we can recover the PV and see the complete path rather  *
  104.  *  rather than one that is incomplete.                     *
  105.  *                                                          *
  106.  *  One other issue is to update the age field if we get a  *
  107.  *  hit on an old position, so that it won't be replaced    *
  108.  *  just because it came from a previous search.            *
  109.  *                                                          *
  110.  ************************************************************
  111.  */
  112.   if (entry < 4) {
  113.     val = (word1 & 0x1ffff) - 65536;
  114.     draft = (word1 >> 17) & 0x7fff;
  115.     tree->hash_move[ply] = (word1 >> 32) & 0x1fffff;
  116.     type = (word1 >> 53) & 3;
  117.     if ((type & UPPER) &&
  118.         depth - null_depth - depth / null_divisor - 1 <= draft && val < beta)
  119.       avoid_null = AVOID_NULL_MOVE;
  120.     if (depth <= draft) {
  121.       if (val > 32000)
  122.         val -= ply - 1;
  123.       else if (val < -32000)
  124.         val += ply - 1;
  125.       *value = val;
  126. /*
  127.  ************************************************************
  128.  *                                                          *
  129.  *  We have three types of results.  An EXACT entry was     *
  130.  *  stored when val > alpha and val < beta, and represents  *
  131.  *  an exact score.  An UPPER entry was stored when val <   *
  132.  *  alpha, which represents an upper bound with the score   *
  133.  *  likely being even lower.  A LOWER entry was stored when *
  134.  *  val > beta, which represents alower bound with the      *
  135.  *  score likely being even higher.                         *
  136.  *                                                          *
  137.  *  For EXACT entries, we save the path from the position   *
  138.  *  to the terminal node that produced the backed-up score  *
  139.  *  so that we can complete the PV if we get a hash hit on  *
  140.  *  this entry.                                             *
  141.  *                                                          *
  142.  ************************************************************
  143.  */
  144.       switch (type) {
  145.         case EXACT:
  146.           if (val > alpha && val < beta) {
  147.             if (word1 >> 55 != transposition_age) {
  148.               word1 = (word1 & 0x007fffffffffffffull) | ((uint64_t)
  149.                   transposition_age << 55);
  150.               htable[entry].word1 = word1;
  151.               htable[entry].word2 = word1 ^ word2;
  152.             }
  153.             SavePV(tree, ply, 1);
  154.             ptable = hash_path + (temp_hashkey & hash_path_mask);
  155.             for (entry = 0; entry < 16; entry++)
  156.               if (ptable[entry].path_sig == temp_hashkey) {
  157.                 for (i = ply;
  158.                     i < Min(MAXPLY - 1, ptable[entry].hash_pathl + ply); i++)
  159.                   tree->pv[ply - 1].path[i] =
  160.                       ptable[entry].hash_path_moves[i - ply];
  161.                 if (ptable[entry].hash_pathl + ply < MAXPLY - 1)
  162.                   tree->pv[ply - 1].pathh = 0;
  163.                 tree->pv[ply - 1].pathl =
  164.                     Min(MAXPLY - 1, ply + ptable[entry].hash_pathl);
  165.                 ptable[entry].hash_path_age = transposition_age;
  166.                 break;
  167.               }
  168.           }
  169.           return HASH_HIT;
  170.         case UPPER:
  171.           if (val <= alpha) {
  172.             if (word1 >> 55 != transposition_age) {
  173.               word1 = (word1 & 0x007fffffffffffffull) | ((uint64_t)
  174.                   transposition_age << 55);
  175.               htable[entry].word1 = word1;
  176.               htable[entry].word2 = word1 ^ word2;
  177.             }
  178.             return HASH_HIT;
  179.           }
  180.           break;
  181.         case LOWER:
  182.           if (val >= beta) {
  183.             if (word1 >> 55 != transposition_age) {
  184.               word1 = (word1 & 0x007fffffffffffffull) | ((uint64_t)
  185.                   transposition_age << 55);
  186.               htable[entry].word1 = word1;
  187.               htable[entry].word2 = word1 ^ word2;
  188.             }
  189.             return HASH_HIT;
  190.           }
  191.           break;
  192.       }
  193.     }
  194.     return avoid_null;
  195.   }
  196.   return HASH_MISS;
  197. }
  198.  
  199. /* last modified 02/12/16 */
  200. /*
  201.  *******************************************************************************
  202.  *                                                                             *
  203.  *   HashStore() is used to store entries into the transposition table so that *
  204.  *   this sub-tree won't have to be searched again if the same position is     *
  205.  *   reached.  We basically store three types of entries:                      *
  206.  *                                                                             *
  207.  *     (1) EXACT.  This entry is stored when we complete a search at some ply  *
  208.  *          and end up with a score that is greater than alpha and less than   *
  209.  *          beta, which is an exact score, which also has a best move to try   *
  210.  *          if we encounter this position again.                               *
  211.  *                                                                             *
  212.  *     (2) LOWER.  This entry is stored when we complete a search at some ply  *
  213.  *          and end up with a score that is greater than or equal to beta.  We *
  214.  *          know know that this score should be at least equal to beta and may *
  215.  *          well be even higher.  So this entry represents a lower bound on    *
  216.  *          the score for this node, and we also have a good move to try since *
  217.  *          it caused the cutoff, although we do not know if it is the best    *
  218.  *          move or not since not all moves were search.                       *
  219.  *                                                                             *
  220.  *     (3) UPPER.  This entry is stored when we complete a search at some ply  *
  221.  *          and end up with a score that is less than or equal to alpha.  We   *
  222.  *          know know that this score should be at least equal to alpha and    *
  223.  *          may well be even lower.  So this entry represents an upper bound   *
  224.  *          on the score for this node.  We have no idea about which move is   *
  225.  *          best in this position since they all failed low, so we store a     *
  226.  *          best move of zero.                                                 *
  227.  *                                                                             *
  228.  *   For storing, we may require three passes.  We make our first pass looking *
  229.  *   for an entry that matches the current hash signature.  If we find a match *
  230.  *   then we are constrained to overwrite that entry regardless of any other   *
  231.  *   considerations.  The second pass looks for entries stored in previous     *
  232.  *   searches (not iterations) and chooses the one with the shallowest draft,  *
  233.  *   if one is found;  Otherwise we make a final pass over the bucket and      *
  234.  *   choose the entry with the shallowest draft, period.                       *
  235.  *                                                                             *
  236.  *******************************************************************************
  237.  */
  238. void HashStore(TREE * RESTRICT tree, int ply, int depth, int side, int type,
  239.     int value, int bestmove) {
  240.   HASH_ENTRY *htable, *replace = 0;
  241.   HPATH_ENTRY *ptable;
  242.   uint64_t word1, temp_hashkey;
  243.   int entry, draft, age, replace_draft, i, j;
  244.  
  245. /*
  246.  ************************************************************
  247.  *                                                          *
  248.  *  "Fill in the blank" and build a table entry from        *
  249.  *  current search information.                             *
  250.  *                                                          *
  251.  ************************************************************
  252.  */
  253.   word1 = transposition_age;
  254.   word1 = (word1 << 2) | type;
  255.   if (value > 32000)
  256.     value += ply - 1;
  257.   else if (value < -32000)
  258.     value -= ply - 1;
  259.   word1 = (word1 << 21) | bestmove;
  260.   word1 = (word1 << 15) | depth;
  261.   word1 = (word1 << 17) | (value + 65536);
  262.   temp_hashkey = (side) ? HashKey : ~HashKey;
  263. /*
  264.  ************************************************************
  265.  *                                                          *
  266.  *  Now we search for an entry to overwrite in three        *
  267.  *  passes.                                                 *
  268.  *                                                          *
  269.  *  Pass 1:  If any signature in the table matches the      *
  270.  *    current signature, we are going to overwrite this     *
  271.  *    entry, period.  It might seem worthwhile to check the *
  272.  *    draft and not overwrite if the table draft is greater *
  273.  *    than the current remaining depth, but after you think *
  274.  *    about it, this is a bad idea.  If the draft is        *
  275.  *    greater than or equal the current remaining depth,    *
  276.  *    then we should never get here unless the stored bound *
  277.  *    or score is unusable because of the current alpha/    *
  278.  *    beta window.  So we are overwriting to avoid losing   *
  279.  *    the current result.                                   *
  280.  *                                                          *
  281.  *  Pass 2:  If any of the entries come from a previous     *
  282.  *    search (not iteration) then we choose the entry from  *
  283.  *    this set that has the smallest draft, since it is the *
  284.  *    least potentially usable result.                      *
  285.  *                                                          *
  286.  *  Pass 3:  If neither of the above two found an entry to  *
  287.  *    overwrite, we simply choose the entry from the bucket *
  288.  *    with the smallest draft and overwrite that.           *
  289.  *                                                          *
  290.  ************************************************************
  291.  */
  292.   htable = hash_table + (temp_hashkey & hash_mask);
  293.   for (entry = 0; entry < 4; entry++) {
  294.     if (temp_hashkey == (htable[entry].word1 ^ htable[entry].word2)) {
  295.       replace = htable + entry;
  296.       break;
  297.     }
  298.   }
  299.   if (!replace) {
  300.     replace_draft = 99999;
  301.     for (entry = 0; entry < 4; entry++) {
  302.       age = htable[entry].word1 >> 55;
  303.       draft = (htable[entry].word1 >> 17) & 0x7fff;
  304.       if (age != transposition_age && replace_draft > draft) {
  305.         replace = htable + entry;
  306.         replace_draft = draft;
  307.       }
  308.     }
  309.     if (!replace) {
  310.       for (entry = 0; entry < 4; entry++) {
  311.         draft = (htable[entry].word1 >> 17) & 0x7fff;
  312.         if (replace_draft > draft) {
  313.           replace = htable + entry;
  314.           replace_draft = draft;
  315.         }
  316.       }
  317.     }
  318.   }
  319. /*
  320.  ************************************************************
  321.  *                                                          *
  322.  *  Now that we know which entry to replace, we simply      *
  323.  *  stuff the values and exit.  Note that the two 64 bit    *
  324.  *  words are xor'ed together and stored as the signature   *
  325.  *  for the "lockless-hash" approach.                       *
  326.  *                                                          *
  327.  ************************************************************
  328.  */
  329.   replace->word1 = word1;
  330.   replace->word2 = temp_hashkey ^ word1;
  331. /*
  332.  ************************************************************
  333.  *                                                          *
  334.  *  If this is an EXACT entry, we are going to store the PV *
  335.  *  in a safe place so that if we get a hit on this entry,  *
  336.  *  we can recover the PV and see the complete path rather  *
  337.  *  rather than one that is incomplete.                     *
  338.  *                                                          *
  339.  ************************************************************
  340.  */
  341.   if (type == EXACT) {
  342.     ptable = hash_path + (temp_hashkey & hash_path_mask);
  343.     for (i = 0; i < 16; i++, ptable++) {
  344.       if (ptable->path_sig == temp_hashkey ||
  345.           transposition_age != ptable->hash_path_age) {
  346.         for (j = ply; j < tree->pv[ply - 1].pathl; j++)
  347.           ptable->hash_path_moves[j - ply] = tree->pv[ply - 1].path[j];
  348.         ptable->hash_pathl = tree->pv[ply - 1].pathl - ply;
  349.         ptable->path_sig = temp_hashkey;
  350.         ptable->hash_path_age = transposition_age;
  351.         break;
  352.       }
  353.     }
  354.   }
  355. }
  356.  
  357. /* last modified 09/16/14 */
  358. /*
  359.  *******************************************************************************
  360.  *                                                                             *
  361.  *   HashStorePV() is called by Iterate() to insert the PV moves so they will  *
  362.  *   be searched before any other moves.  Normally the PV moves would be in    *
  363.  *   the table, but on occasion they can be overwritten, particularly the ones *
  364.  *   that are a significant distance from the root since those table entries   *
  365.  *   will have a low draft.                                                    *
  366.  *                                                                             *
  367.  *******************************************************************************
  368.  */
  369. void HashStorePV(TREE * RESTRICT tree, int side, int ply) {
  370.   HASH_ENTRY *htable, *replace;
  371.   uint64_t temp_hashkey, word1;
  372.   int entry, draft, replace_draft, age;
  373.  
  374. /*
  375.  ************************************************************
  376.  *                                                          *
  377.  *  First, compute the initial hash address and the fake    *
  378.  *  entry we will store if we don't find a valid match      *
  379.  *  already in the table.                                   *
  380.  *                                                          *
  381.  ************************************************************
  382.  */
  383.   temp_hashkey = (side) ? HashKey : ~HashKey;
  384.   word1 = transposition_age;
  385.   word1 = (word1 << 2) | WORTHLESS;
  386.   word1 = (word1 << 21) | tree->pv[0].path[ply];
  387.   word1 = (word1 << 32) | 65536;
  388. /*
  389.  ************************************************************
  390.  *                                                          *
  391.  *  Now we search for an entry to overwrite in three        *
  392.  *  passes.                                                 *
  393.  *                                                          *
  394.  *  Pass 1:  If any signature in the table matches the      *
  395.  *    current signature, we are going to overwrite this     *
  396.  *    entry, period.  It might seem worthwhile to check the *
  397.  *    draft and not overwrite if the table draft is greater *
  398.  *    than the current remaining depth, but after you think *
  399.  *    about it, this is a bad idea.  If the draft is        *
  400.  *    greater than or equal the current remaining depth,    *
  401.  *    then we should never get here unless the stored bound *
  402.  *    or score is unusable because of the current alpha/    *
  403.  *    beta window.  So we are overwriting to avoid losing   *
  404.  *    the current result.                                   *
  405.  *                                                          *
  406.  *  Pass 2:  If any of the entries come from a previous     *
  407.  *    search (not iteration) then we choose the entry from  *
  408.  *    this set that has the smallest draft, since it is the *
  409.  *    least potentially usable result.                      *
  410.  *                                                          *
  411.  *  Pass 3:  If neither of the above two found an entry to  *
  412.  *    overwrite, we simply choose the entry from the bucket *
  413.  *    with the smallest draft and overwrite that.           *
  414.  *                                                          *
  415.  ************************************************************
  416.  */
  417.   htable = hash_table + (temp_hashkey & hash_mask);
  418.   for (entry = 0; entry < 4; entry++) {
  419.     if ((htable[entry].word2 ^ htable[entry].word1) == temp_hashkey) {
  420.       htable[entry].word1 &= ~((uint64_t) 0x1fffff << 32);
  421.       htable[entry].word1 |= (uint64_t) tree->pv[0].path[ply] << 32;
  422.       htable[entry].word2 = temp_hashkey ^ htable[entry].word1;
  423.       break;
  424.     }
  425.   }
  426.   if (entry == 4) {
  427.     replace = 0;
  428.     replace_draft = 99999;
  429.     for (entry = 0; entry < 4; entry++) {
  430.       age = htable[entry].word1 >> 55;
  431.       draft = (htable[entry].word1 >> 17) & 0x7fff;
  432.       if (age != transposition_age && replace_draft > draft) {
  433.         replace = htable + entry;
  434.         replace_draft = draft;
  435.       }
  436.     }
  437.     if (!replace) {
  438.       for (entry = 0; entry < 4; entry++) {
  439.         draft = (htable[entry].word1 >> 17) & 0x7fff;
  440.         if (replace_draft > draft) {
  441.           replace = htable + entry;
  442.           replace_draft = draft;
  443.         }
  444.       }
  445.     }
  446.     replace->word1 = word1;
  447.     replace->word2 = temp_hashkey ^ word1;
  448.   }
  449. }
  450.