Subversion Repositories Games.Chess Giants

Rev

Rev 108 | 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. /* last modified 02/23/14 */
  5. /*
  6.  *******************************************************************************
  7.  *                                                                             *
  8.  *   TimeAdjust() is called to adjust timing variables after each program move *
  9.  *   is made.  It simply increments the number of moves made, decrements the   *
  10.  *   amount of time used, and then makes any necessary adjustments based on    *
  11.  *   the time controls.                                                        *
  12.  *                                                                             *
  13.  *******************************************************************************
  14.  */
  15. void TimeAdjust(int time_used, int side) {
  16. /*
  17.  ************************************************************
  18.  *                                                          *
  19.  *  Decrement the number of moves remaining to the next     *
  20.  *  time control.  Then subtract the time the program took  *
  21.  *  to choose its move from the time remaining.             *
  22.  *                                                          *
  23.  ************************************************************
  24.  */
  25.   tc_moves_remaining[side]--;
  26.   tc_time_remaining[side] -=
  27.       (tc_time_remaining[side] >
  28.       time_used) ? time_used : tc_time_remaining[side];
  29.   if (!tc_moves_remaining[side]) {
  30.     if (tc_sudden_death == 2)
  31.       tc_sudden_death = 1;
  32.     tc_moves_remaining[side] += tc_secondary_moves;
  33.     tc_time_remaining[side] += tc_secondary_time;
  34.     Print(4095, "time control reached (%s)\n", (side) ? "white" : "black");
  35.   }
  36.   if (tc_increment)
  37.     tc_time_remaining[side] += tc_increment;
  38. }
  39.  
  40. /* last modified 02/23/14 */
  41. /*
  42.  *******************************************************************************
  43.  *                                                                             *
  44.  *   TimeCheck() is used to determine when the search should stop.  It uses    *
  45.  *   several conditions to make this determination:  (1) The search time has   *
  46.  *   exceeded the time per move limit;  (2) The value at the root of the tree  *
  47.  *   has not dropped too low.                                                  *
  48.  *                                                                             *
  49.  *   We use one additional trick here to avoid stopping the search just before *
  50.  *   we change to a better move.  We simply do our best to complete the        *
  51.  *   iteration which means we have searched every move to this same depth.     *
  52.  *                                                                             *
  53.  *   This is implemented by having Search() call TimeCheck() passing it a      *
  54.  *   value of one (1) for the parameter busy.  TimeCheck() will only end the   *
  55.  *   search if we have exceeded the max time limit.  Otherwise, we continue.   *
  56.  *   Iterate() calls TimeCheck() passing it a value of "0" for busy, which     *
  57.  *   simply says "now, if we have used the target time limit (which can be     *
  58.  *   modified by the "difficulty value), we will stop and not try another      *
  59.  *   iteration."                                                               *
  60.  *                                                                             *
  61.  *   The "difficulty" value is used to implement the concept of an "easy move" *
  62.  *   or a "hard move".  With an easy move, we want to spend less time since    *
  63.  *   the easy move is obvious.  The opposite idea is a hard move, where we     *
  64.  *   actually want to spend more time to be sure we don't make a mistake by`   *
  65.  *   moving too quickly.                                                       *
  66.  *                                                                             *
  67.  *   The basic methodology revolves around how many times we change our mind   *
  68.  *   on the best move at the root of the tree.                                 *
  69.  *                                                                             *
  70.  *   The "difficulty" variable is initially set to 100, which represents a     *
  71.  *   percentage of the actual target time we should spend on this move.  If    *
  72.  *   we end an iteration without having changed our mind at all, difficulty    *
  73.  *   is reduced by multiplying by .9, with a lower bound of 60%.               *
  74.  *                                                                             *
  75.  *   If we change our mind during an iteration, there are two cases.  (1) If   *
  76.  *   difficulty is < 100%, we set it back to 100% +20% for each time we        *
  77.  *   changed the best move.  (2) if difficulty is already at 100% or higher,   *
  78.  *   we multiply difficulty by .80, then add 20% for each root move change.    *
  79.  *   For example, suppose we are at difficulty=60%, and we suddenly change our *
  80.  *   mind twice this iteration (3 different best moves).                       *
  81.  *                                                                             *
  82.  *   difficulty = 100 + 3*20 = 160% of the actual target time will be used.    *
  83.  *                                                                             *
  84.  *   Suppose we change back and forth between two best moves multiple times,   *
  85.  *   with difficulty currently at 100%.  The first time:                       *
  86.  *                                                                             *
  87.  *   difficulty = .80 * 100 + 2*20 = 120%                                      *
  88.  *                                                                             *
  89.  *   The next iteration:                                                       *
  90.  *                                                                             *
  91.  *   difficulty = .80 * 120 + 2 * 20 = 96% _ 40% = 136%                        *
  92.  *                                                                             *
  93.  *   The next iteration:                                                       *
  94.  *                                                                             *
  95.  *   difficulty = .80 * 136% + 40% = 149%                                      *
  96.  *                                                                             *
  97.  *   If we stop changing our mind, then difficulty starts on a downward trend. *
  98.  *   The basic idea is that if we are locked in on a move, we can make it a    *
  99.  *   bit quicker, but if we are changing back and forth, we are going to spend *
  100.  *   more time to try to choose the best move.                                 *
  101.  *                                                                             *
  102.  *******************************************************************************
  103.  */
  104. int TimeCheck(TREE * RESTRICT tree, int busy) {
  105.   int time_used;
  106.   int i, ndone;
  107.  
  108. /*
  109.  ************************************************************
  110.  *                                                          *
  111.  *  Check to see if we need to "burp" the time to let the   *
  112.  *  operator know the search is progressing and how much    *
  113.  *  time has been used so far.                              *
  114.  *                                                          *
  115.  ************************************************************
  116.  */
  117.   time_used = (ReadClock() - start_time);
  118.   if (tree->nodes_searched > noise_level && display_options & 32 &&
  119.       time_used > burp) {
  120.     Lock(lock_io);
  121.     if (pondering)
  122.       printf("         %2i   %s%7s?  ", iteration_depth,
  123.           Display2Times(time_used), tree->remaining_moves_text);
  124.     else
  125.       printf("         %2i   %s%7s*  ", iteration_depth,
  126.           Display2Times(time_used), tree->remaining_moves_text);
  127.     if (display_options & 32 && display_options & 64)
  128.       printf("%d. ", move_number);
  129.     if ((display_options & 32) && (display_options & 64) && Flip(root_wtm))
  130.       printf("... ");
  131.     printf("%s(%snps)             \r", tree->root_move_text,
  132.         DisplayKMB(nodes_per_second));
  133.     burp = (time_used / 1500) * 1500 + 1500;
  134.     fflush(stdout);
  135.     Unlock(lock_io);
  136.   }
  137. /*
  138.  ************************************************************
  139.  *                                                          *
  140.  *  First, check to see if there is only one root move.  If *
  141.  *  so, and we are not pondering, searching a book move or  *
  142.  *  or annotating a game, we can return and make this move  *
  143.  *  instantly.  We do need to finish iteration 1 so that we *
  144.  *  actually back up a move to play.                        *
  145.  *                                                          *
  146.  ************************************************************
  147.  */
  148.   if (n_root_moves == 1 && !booking && !annotate_mode && !pondering &&
  149.       iteration_depth > 1)
  150.     return 1;
  151.   if (iteration_depth <= 2)
  152.     return 0;
  153. /*
  154.  ************************************************************
  155.  *                                                          *
  156.  *  If we are pondering or in analyze mode, we do not       *
  157.  *  terminate on time since there is no time limit placed   *
  158.  *  on these searches.  If we have reached the absolute     *
  159.  *  time limit, we stop the search instantly.               *
  160.  *                                                          *
  161.  ************************************************************
  162.  */
  163.   if (pondering || analyze_mode)
  164.     return 0;
  165.   if (time_used > absolute_time_limit)
  166.     return 1;
  167. /*
  168.  ************************************************************
  169.  *                                                          *
  170.  *  If the operator has specified a specific time limit, we *
  171.  *  stop when we hit that regardless of any other tests     *
  172.  *  used during normal timeing.                             *
  173.  *                                                          *
  174.  ************************************************************
  175.  */
  176.   if (search_time_limit) {
  177.     if (time_used < time_limit)
  178.       return 0;
  179.     else
  180.       return 1;
  181.   }
  182. /*
  183.  ************************************************************
  184.  *                                                          *
  185.  *  If we are under the time limit already set, we do not   *
  186.  *  terminate the search.  Once we reach that limit, we     *
  187.  *  abort the search if we are fixing to start another      *
  188.  *  iteration, otherwise we keep searching to try to        *
  189.  *  complete the current iteration.                         *
  190.  *                                                          *
  191.  ************************************************************
  192.  */
  193.   if (time_used < (difficulty * time_limit) / 100)
  194.     return 0;
  195.   if (!busy)
  196.     return 1;
  197. /*
  198.  ************************************************************
  199.  *                                                          *
  200.  *  We have reached the target time limit.  If we are in    *
  201.  *  the middle of an iteration, we keep going unless we are *
  202.  *  stuck on the first move, where there is no benefit to   *
  203.  *  continuing and this will just burn clock time away.     *
  204.  *                                                          *
  205.  *  This is a bit tricky, because if we are on the first    *
  206.  *  move AND we have failed low, we want to continue the    *
  207.  *  search to find something better, if we have not failed  *
  208.  *  low, we will abort the search in the test that follows  *
  209.  *  this one.                                               *
  210.  *                                                          *
  211.  ************************************************************
  212.  */
  213.   ndone = 0;
  214.   for (i = 0; i < n_root_moves; i++)
  215.     if (root_moves[i].status & 8)
  216.       ndone++;
  217.   if (ndone == 1 && !(root_moves[0].status & 1))
  218.     return 1;
  219. /*
  220.  ************************************************************
  221.  *                                                          *
  222.  *  We are in the middle of an iteration, we have used the  *
  223.  *  allocated time limit, but we have more moves left to    *
  224.  *  search.  We forge on until we complete the iteration    *
  225.  *  which will terminate the search, or until we reach the  *
  226.  *  "absolute_time_limit" where we terminate the search no  *
  227.  *  matter what is going on.                                *
  228.  *                                                          *
  229.  ************************************************************
  230.  */
  231.   if (time_used + 300 > tc_time_remaining[root_wtm])
  232.     return 1;
  233.   return 0;
  234. }
  235.  
  236. /* last modified 02/23/14 */
  237. /*
  238.  *******************************************************************************
  239.  *                                                                             *
  240.  *   TimeSet() is called to set the two variables "time_limit" and             *
  241.  *   "absolute_time_limit" which controls the amount of time taken by the      *
  242.  *   iterated search.  It simply takes the timing controls as set by the user  *
  243.  *   and uses these values to calculate how much time should be spent on the   *
  244.  *   next search.                                                              *
  245.  *                                                                             *
  246.  *******************************************************************************
  247.  */
  248. void TimeSet(int search_type) {
  249.   int mult = 0, extra = 0;
  250.   int surplus, average;
  251.   int simple_average;
  252.  
  253.   surplus = 0;
  254.   average = 0;
  255. /*
  256.  ************************************************************
  257.  *                                                          *
  258.  *  Check to see if we are in a sudden-death type of time   *
  259.  *  control.  If so, we have a fixed amount of time         *
  260.  *  remaining.  Set the search time accordingly and exit.   *
  261.  *                                                          *
  262.  *  If we have less than 5 seconds on the clock prior to    *
  263.  *  the increment, then limit our search to the increment.  *
  264.  *                                                          *
  265.  *  If we have less than 2.5 seconds on the clock prior to  *
  266.  *  the increment, then limit our search to half the        *
  267.  *  increment in an attempt to add some time to our buffer. *
  268.  *                                                          *
  269.  *  Set our MAX search time to half the remaining time.     *
  270.  *                                                          *
  271.  *  If our search time will drop the clock below 1 second,  *
  272.  *  then limit our MAX search time to the normal search     *
  273.  *  time.  This is done to stop any extensions from         *
  274.  *  dropping us too low.                                    *
  275.  *                                                          *
  276.  ************************************************************
  277.  */
  278.   if (tc_sudden_death == 1) {
  279.     if (tc_increment) {
  280.       time_limit =
  281.           (tc_time_remaining[root_wtm] -
  282.           tc_operator_time * tc_moves_remaining[root_wtm]) /
  283.           (ponder ? 20 : 26) + tc_increment;
  284.       if (tc_time_remaining[root_wtm] < 500 + tc_increment) {
  285.         time_limit = tc_increment;
  286.         if (tc_time_remaining[root_wtm] < 250 + tc_increment)
  287.           time_limit /= 2;
  288.       }
  289.       absolute_time_limit = tc_time_remaining[root_wtm] / 2 + tc_increment;
  290.       if (absolute_time_limit < time_limit ||
  291.           tc_time_remaining[root_wtm] - time_limit < 100)
  292.         absolute_time_limit = time_limit;
  293.       if (tc_time_remaining[root_wtm] - time_limit < 50) {
  294.         time_limit = tc_time_remaining[root_wtm] - 50;
  295.         if (time_limit < 5)
  296.           time_limit = 5;
  297.       }
  298.       if (tc_time_remaining[root_wtm] - absolute_time_limit < 25) {
  299.         absolute_time_limit = tc_time_remaining[root_wtm] - 25;
  300.         if (absolute_time_limit < 5)
  301.           absolute_time_limit = 5;
  302.       }
  303.  
  304.     } else {
  305.       time_limit = tc_time_remaining[root_wtm] / (ponder ? 20 : 26);
  306.       absolute_time_limit =
  307.           Min(time_limit * 5, tc_time_remaining[root_wtm] / 2);
  308.     }
  309.   }
  310. /*
  311.  ************************************************************
  312.  *                                                          *
  313.  *  We are not in a sudden_death situation.  We now have    *
  314.  *  two choices:  If the program has saved enough time to   *
  315.  *  meet the surplus requirement, then we simply divide     *
  316.  *  the time left evenly among the moves left.  If we       *
  317.  *  haven't yet saved up a cushion so that "fail-lows"      *
  318.  *  have extra time to find a solution, we simply take the  *
  319.  *  number of moves divided into the total time less the    *
  320.  *  necessary operator time as the target.                  *
  321.  *                                                          *
  322.  ************************************************************
  323.  */
  324.   else {
  325.     if (move_number <= tc_moves)
  326.       simple_average =
  327.           (tc_time -
  328.           (tc_operator_time * tc_moves_remaining[root_wtm])) / tc_moves;
  329.     else
  330.       simple_average =
  331.           (tc_secondary_time -
  332.           (tc_operator_time * tc_moves_remaining[root_wtm])) /
  333.           tc_secondary_moves;
  334.     surplus =
  335.         Max(tc_time_remaining[root_wtm] -
  336.         (tc_operator_time * tc_moves_remaining[root_wtm]) -
  337.         simple_average * tc_moves_remaining[root_wtm], 0);
  338.     average =
  339.         (tc_time_remaining[root_wtm] -
  340.         (tc_operator_time * tc_moves_remaining[root_wtm]) +
  341.         tc_moves_remaining[root_wtm] * tc_increment)
  342.         / tc_moves_remaining[root_wtm];
  343.     if (surplus < tc_safety_margin)
  344.       time_limit = (average < simple_average) ? average : simple_average;
  345.     else
  346.       time_limit =
  347.           (average < 2/*.0*/ * simple_average) ? average : 2/*.0*/ * simple_average; // Pierre-Marie Baty -- this is integer math
  348.   }
  349.   if (surplus < 0)
  350.     surplus = 0;
  351.   if (tc_increment > 200 && moves_out_of_book < 2)
  352.     /*time_limit *= 1.2;*/ time_limit = (time_limit * 12) / 10; // Pierre-Marie Baty -- this is integer math
  353.   if (time_limit <= 0)
  354.     time_limit = 5;
  355.   absolute_time_limit =
  356.       time_limit + surplus / 2 + ((tc_time_remaining[root_wtm] -
  357.           tc_operator_time * tc_moves_remaining[root_wtm]) / 4);
  358.   if (absolute_time_limit > 6 * time_limit)
  359.     absolute_time_limit = 6 * time_limit;
  360.   if (absolute_time_limit > tc_time_remaining[root_wtm] / 2)
  361.     absolute_time_limit = tc_time_remaining[root_wtm] / 2;
  362. /*
  363.  ************************************************************
  364.  *                                                          *
  365.  *  The "usage" option can be used to force the time limit  *
  366.  *  higher or lower than normal.  The new "timebook"        *
  367.  *  command can also modify the target time making the      *
  368.  *  program use more time early in the game as it exits the *
  369.  *  book, knowing it will save time later on by ponder hits *
  370.  *  and instant moves.                                      *
  371.  *                                                          *
  372.  ************************************************************
  373.  */
  374.   if (usage_level)
  375.     /*time_limit *= 1.0 + usage_level / 100.0;*/ time_limit += time_limit * usage_level / 100; // Pierre-Marie Baty -- this is integer math
  376.   if (first_nonbook_factor && moves_out_of_book < first_nonbook_span) {
  377.     mult =
  378.         (first_nonbook_span - moves_out_of_book + 1) * first_nonbook_factor;
  379.     extra = time_limit * mult / first_nonbook_span / 100;
  380.     time_limit += extra;
  381.   }
  382. /*
  383.  ************************************************************
  384.  *                                                          *
  385.  *  If the operator has set an absolute search time limit   *
  386.  *  already, then we simply copy this value and return.     *
  387.  *                                                          *
  388.  ************************************************************
  389.  */
  390.   if (search_time_limit) {
  391.     time_limit = search_time_limit;
  392.     absolute_time_limit = time_limit;
  393.   }
  394.   if (search_type == puzzle || search_type == booking) {
  395.     time_limit /= 10;
  396.     absolute_time_limit = time_limit * 3;
  397.   }
  398.   if (!tc_sudden_death && !search_time_limit &&
  399.       time_limit > 3 * tc_time / tc_moves)
  400.     time_limit = 3 * tc_time / tc_moves;
  401.   time_limit = Min(time_limit, absolute_time_limit);
  402.   if (search_type != puzzle) {
  403.     if (!tc_sudden_death)
  404.       Print(128, "        time surplus %s  ", DisplayTime(surplus));
  405.     else
  406.       Print(128, "         ");
  407.     Print(128, "time limit %s", DisplayTimeKibitz(time_limit));
  408.     Print(128, " (+%s)", DisplayTimeKibitz(extra));
  409.     Print(128, " (%s)", DisplayTimeKibitz(absolute_time_limit));
  410.     if (fabs(usage_level) > 0.0001) {
  411.       Print(128, "/");
  412.       Print(128, "(%d)", usage_level);
  413.     }
  414.     Print(128, "\n");
  415.   }
  416.   if (time_limit <= 1) {
  417.     time_limit = 1;
  418.     usage_level = 0;
  419.   }
  420. }
  421.