/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* This is GNU Go, a Go program. Contact gnugo@gnu.org, or see *
* http://www.gnu.org/software/gnugo/ for more information. *
* Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
* 2008 and 2009 by the Free Software Foundation. *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation - version 3 or *
* (at your option) any later version. *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License in file COPYING for more details. *
* You should have received a copy of the GNU General Public *
* License along with this program; if not, write to the Free *
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02111, USA. *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* ============================================================= *\
\* ============================================================= */
static int level
= DEFAULT_LEVEL
; /* current level */
static int level_offset
= 0;
static int min_level
= 0;
static int max_level
= gg_max(DEFAULT_LEVEL
, 10);
/*************************/
/* Datas and other stuff */
/*************************/
static int main_time
= -1;
static int byoyomi_time
= -1;
static int byoyomi_stones
= -1; /* <= 0 if no byo-yomi */
/* Keep track of the remaining time left.
* If stones_left is zero, .._time_left is the remaining main time.
* Otherwise, the remaining time for this byoyomi period.
struct remaining_time_data
{
double time_for_last_move
;
struct remaining_time_data official
;
struct remaining_time_data estimated
;
static struct timer_data black_time_data
;
static struct timer_data white_time_data
;
/* Echo a time value in STANDARD format */
timeval_print(FILE *outfile
, double tv
)
fprintf(outfile
, "%3dmin %.2fsec ", min
, sec
);
/* Print the clock status for one side. */
struct timer_data
*const td
= (color
== BLACK
) ? &black_time_data
: &white_time_data
;
fprintf(stderr
, "clock: ");
fprintf(stderr
, "%s ", color_to_string(color
));
fprintf(stderr
, "TIME OUT! ");
if (td
->estimated
.in_byoyomi
) {
fprintf(stderr
, "byoyomi");
timeval_print(stderr
, td
->estimated
.time_left
);
fprintf(stderr
, "for %d stones.", td
->estimated
.stones
);
timeval_print(stderr
, td
->estimated
.time_left
);
/******************************/
/* Initialization functions */
/******************************/
* Initialize the time settings for this game.
* -1 means "do not modify this value".
* byo_time > 0 and byo_stones == 0 means no time settings.
clock_settings(int time
, int byo_time
, int byo_stones
)
byoyomi_stones
= byo_stones
;
/* Get time settings. Returns 1 if any time settings have been made,
/* According to the semantics of the GTP command 'time_settings', the
* following signifies no time limits.
if (byoyomi_time
> 0 && byoyomi_stones
== 0)
return (main_time
>= 0 || byoyomi_time
>= 0);
/* Initialize all timers. */
white_time_data
.official
.time_left
= main_time
;
white_time_data
.official
.time_for_last_move
= -1.0;
white_time_data
.official
.stones
= 0;
white_time_data
.official
.movenum
= 0;
white_time_data
.official
.in_byoyomi
= 0;
white_time_data
.estimated
= white_time_data
.official
;
white_time_data
.time_out
= 0;
black_time_data
= white_time_data
;
/*****************************/
/* Clock access functions. */
/*****************************/
update_time_left(int color
, int time_left
, int stones
)
struct timer_data
*const td
= ((color
== BLACK
) ? &black_time_data
: &white_time_data
);
int time_used
= td
->official
.time_left
- time_left
;
/* Did our estimate for time usage go wrong? */
&& gg_abs(time_used
- td
->estimated
.time_for_last_move
) >= 1.0)
td
->estimated
.time_for_last_move
= time_used
;
td
->estimated
.stones
= stones
;
td
->estimated
.movenum
= movenum
;
/* Did our clock go wrong? */
if (gg_abs(td
->estimated
.time_left
- time_left
) >= 1.0)
td
->estimated
.time_left
= time_left
;
td
->estimated
.in_byoyomi
= 1;
td
->estimated
.in_byoyomi
= 0;
td
->official
.stones
= stones
;
td
->official
.movenum
= movenum
;
td
->official
.time_for_last_move
= td
->official
.time_for_last_move
- time_left
;
td
->official
.time_left
= time_left
;
td
->official
.in_byoyomi
= td
->estimated
.in_byoyomi
;
* Update the estimated timer after a move has been made.
clock_push_button(int color
)
static double last_time
= -1.0;
static int last_movenum
= -1;
struct timer_data
*const td
= (color
== BLACK
) ? &black_time_data
: &white_time_data
;
double now
= gg_gettimeofday();
if (!have_time_settings())
&& movenum
== last_movenum
+ 1
&& movenum
> td
->estimated
.movenum
) {
double time_used
= now
- last_time
;
td
->estimated
.time_left
-= time_used
;
td
->estimated
.movenum
= movenum
;
td
->estimated
.time_for_last_move
= time_used
;
if (td
->estimated
.time_left
< 0) {
if (td
->estimated
.in_byoyomi
|| byoyomi_stones
== 0) {
DEBUG(DEBUG_TIME
, "%s ran out of time.\n", color_to_string(color
));
gg_assert(!(td
->estimated
.in_byoyomi
));
td
->estimated
.in_byoyomi
= 1;
td
->estimated
.stones
= byoyomi_stones
- 1;
td
->estimated
.time_left
+= byoyomi_time
;
if (td
->estimated
.time_left
< 0)
else if (td
->estimated
.stones
> 0) {
gg_assert(td
->estimated
.in_byoyomi
);
td
->estimated
.stones
= td
->estimated
.stones
- 1;
if (td
->estimated
.stones
== 0) {
td
->estimated
.time_left
= byoyomi_time
;
td
->estimated
.stones
= byoyomi_stones
;
/* Analyze the two most recent time reports and determine the time
* spent on the last moves, the (effective) number of stones left and
* the (effective) remaining time.
analyze_time_data(int color
, double *time_for_last_move
, double *time_left
,
struct remaining_time_data
*const timer
= (color
== BLACK
) ? &black_time_data
.estimated
: &white_time_data
.estimated
;
/* Do we have any time limits. */
if (!have_time_settings())
/* If we don't have consistent time information yet, just return. */
if (timer
->time_for_last_move
< 0.0)
*time_for_last_move
= timer
->time_for_last_move
;
if (timer
->stones
== 0) {
*time_left
= timer
->time_left
+ byoyomi_time
;
*stones_left
= byoyomi_stones
;
/* Absolute time. Here we aim to be able to play at least X more
* moves or a total of Y moves. We choose Y as a third of the
* number of vertices and X as 40% of Y. For 19x19 this means
* that we aim to play at least a total of 120 moves
* (corresponding to a 240 move game) or another 24 moves.
* FIXME: Maybe we should use the game_status of
* influence_evaluate_position() here to guess how many moves
int nominal_moves
= board_size
* board_size
/ 3;
*stones_left
= gg_max(nominal_moves
- movenum
/ 2,
*time_left
= timer
->time_left
;
*stones_left
= timer
->stones
;
/* Adjust the level offset given information of current playing speed
* and remaining time and stones.
adjust_level_offset(int color
)
double time_for_last_move
;
if (!analyze_time_data(color
, &time_for_last_move
, &time_left
, &stones_left
))
/* These rules are both crude and ad hoc.
* FIXME: Use rules with at least some theoretical basis.
if (time_left
< time_for_last_move
* (stones_left
+ 3))
if (time_left
< time_for_last_move
* stones_left
)
if (3 * time_left
< 2 * time_for_last_move
* stones_left
)
if (2 * time_left
< time_for_last_move
* stones_left
)
if (3 * time_left
< time_for_last_move
* stones_left
)
if (time_for_last_move
== 0)
if (time_left
> time_for_last_move
* (stones_left
+ 6))
if (time_left
> 2 * time_for_last_move
* (stones_left
+ 6))
if (level
+ level_offset
< min_level
)
level_offset
= min_level
- level
;
if (level
+ level_offset
> max_level
)
level_offset
= max_level
- level
;
DEBUG(DEBUG_TIME
, "New level %d (%d %C %f %f %d)\n", level
+ level_offset
,
movenum
/ 2, color
, time_for_last_move
, time_left
, stones_left
);
/********************************/
/* Interface to level settings. */
/********************************/
return level
+ level_offset
;
set_max_level(int new_max
)
set_min_level(int new_min
)