1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see *
3 * http://www.gnu.org/software/gnugo/ for more information. *
4 * *
5 * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
6 * 2008 and 2009 by the Free Software Foundation. *
7 * *
8 * This program is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU General Public License as *
10 * published by the Free Software Foundation - version 3 or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License in file COPYING for more details. *
17 * *
18 * You should have received a copy of the GNU General Public *
19 * License along with this program; if not, write to the Free *
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
21 * Boston, MA 02111, USA. *
22 \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23
24 /* ============================================================= *\
25 * Time handling *
26 * for GNU Go *
27 * __ __ *
28 * < > < > *
29 * +--++-------++--+ *
30 * | .'11 12 1'. | *
31 * | :10 \ 2: | *
32 * | :9 @-> 3: | *
33 * | :8 4; | *
34 * | '..7 6 5..' | *
35 * |_______________| *
36 * *
37 \* ============================================================= */
38
39 #include "clock.h"
40 #include "gg_utils.h"
41 #include "board.h"
42
43 /* Level data */
44 static int level = DEFAULT_LEVEL; /* current level */
45 static int level_offset = 0;
46 static int min_level = 0;
47 static int max_level = gg_max(DEFAULT_LEVEL, 10);
48
49
50 /*************************/
51 /* Datas and other stuff */
52 /*************************/
53
54 /* clock parameters */
55 static int main_time = -1;
56 static int byoyomi_time = -1;
57 static int byoyomi_stones = -1; /* <= 0 if no byo-yomi */
58
59 /* Keep track of the remaining time left.
60 * If stones_left is zero, .._time_left is the remaining main time.
61 * Otherwise, the remaining time for this byoyomi period.
62 */
63 struct remaining_time_data {
64 double time_left;
65 double time_for_last_move;
66 int stones;
67 int movenum;
68 int in_byoyomi;
69 };
70
71 struct timer_data {
72 struct remaining_time_data official;
73 struct remaining_time_data estimated;
74 int time_out;
75 };
76
77 static struct timer_data black_time_data;
78 static struct timer_data white_time_data;
79
80
81 /* Echo a time value in STANDARD format */
82 static void
timeval_print(FILE * outfile,double tv)83 timeval_print(FILE *outfile, double tv)
84 {
85 int min;
86 double sec;
87
88 min = (int) tv / 60;
89 sec = tv - min*60;
90
91 fprintf(outfile, "%3dmin %.2fsec ", min, sec);
92 }
93
94
95 /* Print the clock status for one side. */
96 void
clock_print(int color)97 clock_print(int color)
98 {
99 struct timer_data *const td
100 = (color == BLACK) ? &black_time_data : &white_time_data;
101
102 fprintf(stderr, "clock: ");
103 fprintf(stderr, "%s ", color_to_string(color));
104
105 if (td->time_out)
106 fprintf(stderr, "TIME OUT! ");
107 else {
108 if (td->estimated.in_byoyomi) {
109 fprintf(stderr, "byoyomi");
110 timeval_print(stderr, td->estimated.time_left);
111 fprintf(stderr, "for %d stones.", td->estimated.stones);
112 }
113 else
114 timeval_print(stderr, td->estimated.time_left);
115
116 }
117 fprintf(stderr, "\n");
118 }
119
120
121 /******************************/
122 /* Initialization functions */
123 /******************************/
124
125 /*
126 * Initialize the time settings for this game.
127 * -1 means "do not modify this value".
128 *
129 * byo_time > 0 and byo_stones == 0 means no time settings.
130 */
131 void
clock_settings(int time,int byo_time,int byo_stones)132 clock_settings(int time, int byo_time, int byo_stones)
133 {
134 if (time >= 0)
135 main_time = time;
136 if (byo_time >= 0)
137 byoyomi_time = byo_time;
138 if (byo_stones >= 0)
139 byoyomi_stones = byo_stones;
140 init_timers();
141 }
142
143 /* Get time settings. Returns 1 if any time settings have been made,
144 * 0 otherwise.
145 */
146 int
have_time_settings(void)147 have_time_settings(void)
148 {
149 /* According to the semantics of the GTP command 'time_settings', the
150 * following signifies no time limits.
151 */
152 if (byoyomi_time > 0 && byoyomi_stones == 0)
153 return 0;
154 else
155 return (main_time >= 0 || byoyomi_time >= 0);
156 }
157
158
159 /* Initialize all timers. */
160 void
init_timers()161 init_timers()
162 {
163 white_time_data.official.time_left = main_time;
164 white_time_data.official.time_for_last_move = -1.0;
165 white_time_data.official.stones = 0;
166 white_time_data.official.movenum = 0;
167 white_time_data.official.in_byoyomi = 0;
168 white_time_data.estimated = white_time_data.official;
169 white_time_data.time_out = 0;
170 black_time_data = white_time_data;
171
172 level_offset = 0;
173 }
174
175
176 /*****************************/
177 /* Clock access functions. */
178 /*****************************/
179
180
181 void
update_time_left(int color,int time_left,int stones)182 update_time_left(int color, int time_left, int stones)
183 {
184 struct timer_data *const td
185 = ((color == BLACK) ? &black_time_data : &white_time_data);
186 int time_used = td->official.time_left - time_left;
187
188 if (time_left > 0)
189 td->time_out = 0;
190 else
191 td->time_out = 1;
192
193 /* Did our estimate for time usage go wrong? */
194 if (time_used > 0
195 && gg_abs(time_used - td->estimated.time_for_last_move) >= 1.0)
196 td->estimated.time_for_last_move = time_used;
197 td->estimated.stones = stones;
198 td->estimated.movenum = movenum;
199 /* Did our clock go wrong? */
200 if (gg_abs(td->estimated.time_left - time_left) >= 1.0)
201 td->estimated.time_left = time_left;
202 if (stones > 0)
203 td->estimated.in_byoyomi = 1;
204 else
205 td->estimated.in_byoyomi = 0;
206
207 td->official.stones = stones;
208 td->official.movenum = movenum;
209 td->official.time_for_last_move = td->official.time_for_last_move - time_left;
210 td->official.time_left = time_left;
211 td->official.in_byoyomi = td->estimated.in_byoyomi;
212 }
213
214 /*
215 * Update the estimated timer after a move has been made.
216 */
217 void
clock_push_button(int color)218 clock_push_button(int color)
219 {
220 static double last_time = -1.0;
221 static int last_movenum = -1;
222 struct timer_data *const td
223 = (color == BLACK) ? &black_time_data : &white_time_data;
224 double now = gg_gettimeofday();
225
226 if (!have_time_settings())
227 return;
228
229 if (last_movenum >= 0
230 && movenum == last_movenum + 1
231 && movenum > td->estimated.movenum) {
232 double time_used = now - last_time;
233 td->estimated.time_left -= time_used;
234 td->estimated.movenum = movenum;
235 td->estimated.time_for_last_move = time_used;
236 if (td->estimated.time_left < 0) {
237 if (td->estimated.in_byoyomi || byoyomi_stones == 0) {
238 DEBUG(DEBUG_TIME, "%s ran out of time.\n", color_to_string(color));
239 if (debug & DEBUG_TIME)
240 clock_print(color);
241 td->time_out = 1;
242 }
243 else {
244 /* Entering byoyomi. */
245 gg_assert(!(td->estimated.in_byoyomi));
246 td->estimated.in_byoyomi = 1;
247 td->estimated.stones = byoyomi_stones - 1;
248 td->estimated.time_left += byoyomi_time;
249 if (td->estimated.time_left < 0)
250 td->time_out = 1;
251 }
252 }
253 else if (td->estimated.stones > 0) {
254 gg_assert(td->estimated.in_byoyomi);
255 td->estimated.stones = td->estimated.stones - 1;
256 if (td->estimated.stones == 0) {
257 td->estimated.time_left = byoyomi_time;
258 td->estimated.stones = byoyomi_stones;
259 }
260 }
261 }
262
263 last_movenum = movenum;
264 last_time = now;
265
266 /* Update main timer. */
267 if (debug & DEBUG_TIME)
268 clock_print(color);
269 }
270
271
272 /**********************/
273 /* Autolevel system */
274 /**********************/
275
276
277 /* Analyze the two most recent time reports and determine the time
278 * spent on the last moves, the (effective) number of stones left and
279 * the (effective) remaining time.
280 */
281 static int
analyze_time_data(int color,double * time_for_last_move,double * time_left,int * stones_left)282 analyze_time_data(int color, double *time_for_last_move, double *time_left,
283 int *stones_left)
284 {
285 struct remaining_time_data *const timer
286 = (color == BLACK) ? &black_time_data.estimated
287 : &white_time_data.estimated;
288
289 /* Do we have any time limits. */
290 if (!have_time_settings())
291 return 0;
292
293 /* If we don't have consistent time information yet, just return. */
294 if (timer->time_for_last_move < 0.0)
295 return 0;
296
297 *time_for_last_move = timer->time_for_last_move;
298
299 if (timer->stones == 0) {
300 /* Main time running. */
301 *time_left = timer->time_left + byoyomi_time;
302 if (byoyomi_time > 0)
303 *stones_left = byoyomi_stones;
304 else {
305 /* Absolute time. Here we aim to be able to play at least X more
306 * moves or a total of Y moves. We choose Y as a third of the
307 * number of vertices and X as 40% of Y. For 19x19 this means
308 * that we aim to play at least a total of 120 moves
309 * (corresponding to a 240 move game) or another 24 moves.
310 *
311 * FIXME: Maybe we should use the game_status of
312 * influence_evaluate_position() here to guess how many moves
313 * are remaining.
314 */
315 int nominal_moves = board_size * board_size / 3;
316 *stones_left = gg_max(nominal_moves - movenum / 2,
317 2 * nominal_moves / 5);
318 }
319 }
320 else {
321 *time_left = timer->time_left;
322 *stones_left = timer->stones;
323 }
324 return 1;
325 }
326
327
328 /* Adjust the level offset given information of current playing speed
329 * and remaining time and stones.
330 */
331 void
adjust_level_offset(int color)332 adjust_level_offset(int color)
333 {
334 double time_for_last_move;
335 double time_left;
336 int stones_left;
337
338 if (!analyze_time_data(color, &time_for_last_move, &time_left, &stones_left))
339 return;
340
341
342 /* These rules are both crude and ad hoc.
343 *
344 * FIXME: Use rules with at least some theoretical basis.
345 */
346 if (time_left < time_for_last_move * (stones_left + 3))
347 level_offset--;
348 if (time_left < time_for_last_move * stones_left)
349 level_offset--;
350 if (3 * time_left < 2 * time_for_last_move * stones_left)
351 level_offset--;
352 if (2 * time_left < time_for_last_move * stones_left)
353 level_offset--;
354 if (3 * time_left < time_for_last_move * stones_left)
355 level_offset--;
356
357 if (time_for_last_move == 0)
358 time_for_last_move = 1;
359 if (time_left > time_for_last_move * (stones_left + 6))
360 level_offset++;
361 if (time_left > 2 * time_for_last_move * (stones_left + 6))
362 level_offset++;
363
364 if (level + level_offset < min_level)
365 level_offset = min_level - level;
366
367 if (level + level_offset > max_level)
368 level_offset = max_level - level;
369
370 DEBUG(DEBUG_TIME, "New level %d (%d %C %f %f %d)\n", level + level_offset,
371 movenum / 2, color, time_for_last_move, time_left, stones_left);
372 }
373
374
375 /********************************/
376 /* Interface to level settings. */
377 /********************************/
378
379 int
get_level()380 get_level()
381 {
382 return level + level_offset;
383 }
384
385 void
set_level(int new_level)386 set_level(int new_level)
387 {
388 level = new_level;
389 level_offset = 0;
390 if (level > max_level)
391 max_level = level;
392 if (level < min_level)
393 min_level = level;
394 }
395
396 void
set_max_level(int new_max)397 set_max_level(int new_max)
398 {
399 max_level = new_max;
400 }
401
402 void
set_min_level(int new_min)403 set_min_level(int new_min)
404 {
405 min_level = new_min;
406 }
407
408
409 /*
410 * Local Variables:
411 * tab-width: 8
412 * c-basic-offset: 2
413 * End:
414 */
415