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