Rev 154 | Details | Compare with Previous | 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 | */ |
||
108 | pmbaty | 15 | void TimeAdjust(int side, int time_used) { |
33 | pmbaty | 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 | |||
108 | pmbaty | 40 | /* last modified 10/01/14 */ |
33 | pmbaty | 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); |
||
156 | pmbaty | 118 | if (time_used >= (int) noise_level && display_options & 16 && time_used > burp) { // Pierre-Marie Baty -- added type cast |
33 | pmbaty | 119 | Lock(lock_io); |
120 | if (pondering) |
||
108 | pmbaty | 121 | printf(" %2i %s%7s? ", iteration, Display2Times(time_used), |
122 | tree->remaining_moves_text); |
||
33 | pmbaty | 123 | else |
108 | pmbaty | 124 | printf(" %2i %s%7s* ", iteration, Display2Times(time_used), |
125 | tree->remaining_moves_text); |
||
126 | if (display_options & 16) |
||
33 | pmbaty | 127 | printf("%d. ", move_number); |
108 | pmbaty | 128 | if ((display_options & 16) && Flip(root_wtm)) |
33 | pmbaty | 129 | printf("... "); |
130 | printf("%s(%snps) \r", tree->root_move_text, |
||
108 | pmbaty | 131 | DisplayKMB(nodes_per_second, 0)); |
33 | pmbaty | 132 | burp = (time_used / 1500) * 1500 + 1500; |
133 | fflush(stdout); |
||
134 | Unlock(lock_io); |
||
135 | } |
||
136 | /* |
||
137 | ************************************************************ |
||
138 | * * |
||
139 | * First, check to see if there is only one root move. If * |
||
140 | * so, and we are not pondering, searching a book move or * |
||
141 | * or annotating a game, we can return and make this move * |
||
142 | * instantly. We do need to finish iteration 1 so that we * |
||
143 | * actually back up a move to play. * |
||
144 | * * |
||
145 | ************************************************************ |
||
146 | */ |
||
147 | if (n_root_moves == 1 && !booking && !annotate_mode && !pondering && |
||
108 | pmbaty | 148 | iteration > 1 && (time_used > time_limit || time_used > 100)) |
33 | pmbaty | 149 | return 1; |
108 | pmbaty | 150 | if (iteration <= 2) |
33 | pmbaty | 151 | return 0; |
152 | /* |
||
153 | ************************************************************ |
||
154 | * * |
||
155 | * If we are pondering or in analyze mode, we do not * |
||
156 | * terminate on time since there is no time limit placed * |
||
157 | * on these searches. If we have reached the absolute * |
||
158 | * time limit, we stop the search instantly. * |
||
159 | * * |
||
160 | ************************************************************ |
||
161 | */ |
||
162 | if (pondering || analyze_mode) |
||
163 | return 0; |
||
164 | if (time_used > absolute_time_limit) |
||
165 | return 1; |
||
166 | /* |
||
167 | ************************************************************ |
||
168 | * * |
||
169 | * If the operator has specified a specific time limit, we * |
||
170 | * stop when we hit that regardless of any other tests * |
||
171 | * used during normal timeing. * |
||
172 | * * |
||
173 | ************************************************************ |
||
174 | */ |
||
175 | if (search_time_limit) { |
||
176 | if (time_used < time_limit) |
||
177 | return 0; |
||
178 | else |
||
179 | return 1; |
||
180 | } |
||
181 | /* |
||
182 | ************************************************************ |
||
183 | * * |
||
184 | * If we are under the time limit already set, we do not * |
||
185 | * terminate the search. Once we reach that limit, we * |
||
186 | * abort the search if we are fixing to start another * |
||
187 | * iteration, otherwise we keep searching to try to * |
||
188 | * complete the current iteration. * |
||
189 | * * |
||
190 | ************************************************************ |
||
191 | */ |
||
192 | if (time_used < (difficulty * time_limit) / 100) |
||
193 | return 0; |
||
194 | if (!busy) |
||
195 | return 1; |
||
196 | /* |
||
197 | ************************************************************ |
||
198 | * * |
||
199 | * We have reached the target time limit. If we are in * |
||
200 | * the middle of an iteration, we keep going unless we are * |
||
201 | * stuck on the first move, where there is no benefit to * |
||
202 | * continuing and this will just burn clock time away. * |
||
203 | * * |
||
204 | * This is a bit tricky, because if we are on the first * |
||
205 | * move AND we have failed low, we want to continue the * |
||
206 | * search to find something better, if we have not failed * |
||
207 | * low, we will abort the search in the test that follows * |
||
208 | * this one. * |
||
209 | * * |
||
210 | ************************************************************ |
||
211 | */ |
||
212 | ndone = 0; |
||
213 | for (i = 0; i < n_root_moves; i++) |
||
214 | if (root_moves[i].status & 8) |
||
215 | ndone++; |
||
216 | if (ndone == 1 && !(root_moves[0].status & 1)) |
||
217 | return 1; |
||
218 | /* |
||
219 | ************************************************************ |
||
220 | * * |
||
221 | * We are in the middle of an iteration, we have used the * |
||
222 | * allocated time limit, but we have more moves left to * |
||
223 | * search. We forge on until we complete the iteration * |
||
224 | * which will terminate the search, or until we reach the * |
||
225 | * "absolute_time_limit" where we terminate the search no * |
||
226 | * matter what is going on. * |
||
227 | * * |
||
228 | ************************************************************ |
||
229 | */ |
||
230 | if (time_used + 300 > tc_time_remaining[root_wtm]) |
||
231 | return 1; |
||
232 | return 0; |
||
233 | } |
||
234 | |||
108 | pmbaty | 235 | /* last modified 09/30/14 */ |
33 | pmbaty | 236 | /* |
237 | ******************************************************************************* |
||
238 | * * |
||
239 | * TimeSet() is called to set the two variables "time_limit" and * |
||
240 | * "absolute_time_limit" which controls the amount of time taken by the * |
||
241 | * iterated search. It simply takes the timing controls as set by the user * |
||
242 | * and uses these values to calculate how much time should be spent on the * |
||
243 | * next search. * |
||
244 | * * |
||
245 | ******************************************************************************* |
||
246 | */ |
||
247 | void TimeSet(int search_type) { |
||
248 | int mult = 0, extra = 0; |
||
249 | int surplus, average; |
||
250 | int simple_average; |
||
251 | |||
252 | surplus = 0; |
||
253 | average = 0; |
||
254 | /* |
||
255 | ************************************************************ |
||
256 | * * |
||
257 | * Check to see if we are in a sudden-death type of time * |
||
258 | * control. If so, we have a fixed amount of time * |
||
259 | * remaining. Set the search time accordingly and exit. * |
||
260 | * * |
||
108 | pmbaty | 261 | * The basic algorithm is to divide the remaining time * |
262 | * left on the clock by a constant (that is larger for * |
||
263 | * ponder=off games since we don't get to ponder and save * |
||
264 | * time as the game progresses) and add the increment. * |
||
33 | pmbaty | 265 | * * |
108 | pmbaty | 266 | * Set our MAX search time to the smaller of 5 * the time * |
267 | * limit or 1/2 of the time left on the clock. * |
||
33 | pmbaty | 268 | * * |
269 | ************************************************************ |
||
270 | */ |
||
271 | if (tc_sudden_death == 1) { |
||
108 | pmbaty | 272 | time_limit = |
273 | (tc_time_remaining[root_wtm] - |
||
274 | tc_safety_margin) / (ponder ? 20 : 26) + tc_increment; |
||
275 | absolute_time_limit = |
||
276 | Min(time_limit * 5, tc_time_remaining[root_wtm] / 2); |
||
33 | pmbaty | 277 | } |
278 | /* |
||
279 | ************************************************************ |
||
280 | * * |
||
281 | * We are not in a sudden_death situation. We now have * |
||
282 | * two choices: If the program has saved enough time to * |
||
283 | * meet the surplus requirement, then we simply divide * |
||
284 | * the time left evenly among the moves left. If we * |
||
108 | pmbaty | 285 | * haven't yet saved up a cushion so that "hard-moves" * |
286 | * can be searched more thoroughly, we simply take the * |
||
287 | * number of moves divided into the total time as the * |
||
288 | * target. * |
||
33 | pmbaty | 289 | * * |
290 | ************************************************************ |
||
291 | */ |
||
292 | else { |
||
293 | if (move_number <= tc_moves) |
||
108 | pmbaty | 294 | simple_average = (tc_time - tc_safety_margin) / tc_moves; |
33 | pmbaty | 295 | else |
296 | simple_average = |
||
108 | pmbaty | 297 | (tc_secondary_time - tc_safety_margin) / tc_secondary_moves; |
33 | pmbaty | 298 | surplus = |
108 | pmbaty | 299 | Max(tc_time_remaining[root_wtm] - tc_safety_margin - |
33 | pmbaty | 300 | simple_average * tc_moves_remaining[root_wtm], 0); |
301 | average = |
||
108 | pmbaty | 302 | (tc_time_remaining[root_wtm] - tc_safety_margin + |
33 | pmbaty | 303 | tc_moves_remaining[root_wtm] * tc_increment) |
304 | / tc_moves_remaining[root_wtm]; |
||
305 | if (surplus < tc_safety_margin) |
||
306 | time_limit = (average < simple_average) ? average : simple_average; |
||
307 | else |
||
156 | pmbaty | 308 | time_limit = (int) // Pierre-Marie Baty -- added type cast |
309 | ((average < 2.0 * simple_average) ? average : 2.0 * simple_average); |
||
33 | pmbaty | 310 | } |
311 | if (tc_increment > 200 && moves_out_of_book < 2) |
||
156 | pmbaty | 312 | time_limit = (int) (time_limit * 1.2); // Pierre-Marie Baty -- added type cast |
33 | pmbaty | 313 | if (time_limit <= 0) |
314 | time_limit = 5; |
||
315 | absolute_time_limit = |
||
108 | pmbaty | 316 | time_limit + surplus / 2 + (tc_time_remaining[root_wtm] - |
317 | tc_safety_margin) / 4; |
||
318 | if (absolute_time_limit > 5 * time_limit) |
||
319 | absolute_time_limit = 5 * time_limit; |
||
33 | pmbaty | 320 | if (absolute_time_limit > tc_time_remaining[root_wtm] / 2) |
321 | absolute_time_limit = tc_time_remaining[root_wtm] / 2; |
||
322 | /* |
||
323 | ************************************************************ |
||
324 | * * |
||
325 | * The "usage" option can be used to force the time limit * |
||
326 | * higher or lower than normal. The new "timebook" * |
||
327 | * command can also modify the target time making the * |
||
328 | * program use more time early in the game as it exits the * |
||
329 | * book, knowing it will save time later on by ponder hits * |
||
330 | * and instant moves. * |
||
331 | * * |
||
332 | ************************************************************ |
||
333 | */ |
||
334 | if (usage_level) |
||
156 | pmbaty | 335 | time_limit = (int) (time_limit * (1.0 + usage_level / 100.0)); // Pierre-Marie Baty -- added type cast |
33 | pmbaty | 336 | if (first_nonbook_factor && moves_out_of_book < first_nonbook_span) { |
337 | mult = |
||
338 | (first_nonbook_span - moves_out_of_book + 1) * first_nonbook_factor; |
||
339 | extra = time_limit * mult / first_nonbook_span / 100; |
||
340 | time_limit += extra; |
||
341 | } |
||
342 | /* |
||
343 | ************************************************************ |
||
344 | * * |
||
345 | * If the operator has set an absolute search time limit * |
||
346 | * already, then we simply copy this value and return. * |
||
347 | * * |
||
348 | ************************************************************ |
||
349 | */ |
||
350 | if (search_time_limit) { |
||
351 | time_limit = search_time_limit; |
||
352 | absolute_time_limit = time_limit; |
||
353 | } |
||
354 | if (search_type == puzzle || search_type == booking) { |
||
355 | time_limit /= 10; |
||
356 | absolute_time_limit = time_limit * 3; |
||
357 | } |
||
358 | if (!tc_sudden_death && !search_time_limit && |
||
359 | time_limit > 3 * tc_time / tc_moves) |
||
360 | time_limit = 3 * tc_time / tc_moves; |
||
361 | time_limit = Min(time_limit, absolute_time_limit); |
||
362 | if (search_type != puzzle) { |
||
363 | if (!tc_sudden_death) |
||
108 | pmbaty | 364 | Print(32, " time surplus %s ", DisplayTime(surplus)); |
33 | pmbaty | 365 | else |
108 | pmbaty | 366 | Print(32, " "); |
367 | Print(32, "time limit %s", DisplayTimeKibitz(time_limit)); |
||
368 | Print(32, " (%s)\n", DisplayTimeKibitz(absolute_time_limit)); |
||
33 | pmbaty | 369 | } |
370 | if (time_limit <= 1) { |
||
371 | time_limit = 1; |
||
372 | usage_level = 0; |
||
373 | } |
||
374 | } |