Subversion Repositories Games.Chess Giants

Rev

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
}