Rev 108 | Go to most recent revision | Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 33 | pmbaty | 1 | #include <math.h> | 
| 2 | #include "chess.h" | ||
| 3 | #include "data.h" | ||
| 4 | /* 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 | } |