13 #define MAX_NET_LAG 2.0 /* Max net lag in seconds. TODO: estimate dynamically. */
14 #define RESERVED_BYOYOMI_PERCENT 15 /* Reserve 15% of byoyomi time as safety margin if risk of losing on time */
17 time_parse(struct time_info
*ti
, char *s
)
20 case '_': ti
->period
= TT_TOTAL
; s
++; break;
21 default: ti
->period
= TT_MOVE
; break;
26 ti
->len
.games
= atoi(++s
);
31 ti
->dim
= TD_WALLTIME
;
32 ti
->len
.t
.recommended_time
= atof(s
);
33 ti
->len
.t
.max_time
= ti
->len
.t
.recommended_time
;
34 ti
->len
.t
.net_lag
= MAX_NET_LAG
;
35 ti
->len
.t
.timer_start
= 0;
36 ti
->len
.t
.byoyomi_time
= 0.0;
37 ti
->len
.t
.byoyomi_periods
= 0;
43 /* Update time settings according to gtp time_settings or kgs-time_settings command. */
45 time_settings(struct time_info
*ti
, int main_time
, int byoyomi_time
, int byoyomi_stones
, int byoyomi_periods
)
47 if (byoyomi_time
> 0 && byoyomi_stones
== 0) {
48 ti
->period
= TT_NULL
; // no time limit, rely on engine default
50 ti
->period
= TT_TOTAL
;
51 ti
->dim
= TD_WALLTIME
;
52 ti
->len
.t
.max_time
= (double) main_time
; // byoyomi will be added at next genmove
53 ti
->len
.t
.recommended_time
= ti
->len
.t
.max_time
;
54 ti
->len
.t
.timer_start
= 0;
55 ti
->len
.t
.net_lag
= MAX_NET_LAG
;
56 ti
->len
.t
.byoyomi_time
= (double) byoyomi_time
;
57 if (byoyomi_stones
> 0)
58 ti
->len
.t
.byoyomi_time
/= byoyomi_stones
;
59 ti
->len
.t
.byoyomi_periods
= byoyomi_periods
;
63 /* Update time information according to gtp time_left command.
64 * kgs doesn't give time_left for the first move, so make sure
65 * that just time_settings + time_select_best still work. */
67 time_left(struct time_info
*ti
, int time_left
, int stones_left
)
69 assert(ti
->period
!= TT_NULL
);
70 ti
->dim
= TD_WALLTIME
;
71 ti
->len
.t
.max_time
= (double)time_left
;
73 if (ti
->len
.t
.byoyomi_periods
> 0 && stones_left
> 0) {
74 ti
->len
.t
.byoyomi_periods
= stones_left
; // field misused by kgs
77 /* For non-canadian byoyomi, we use all periods as main time. */
78 if (stones_left
== 0 || ti
->len
.t
.byoyomi_periods
> 1) {
80 ti
->period
= TT_TOTAL
;
81 ti
->len
.t
.recommended_time
= ti
->len
.t
.max_time
;
82 /* byoyomi_time, net_lag & timer_start unchanged. */
85 ti
->len
.t
.byoyomi_time
= ((double)time_left
)/stones_left
;
86 ti
->len
.t
.recommended_time
= ti
->len
.t
.byoyomi_time
;
87 /* net_lag & timer_start unchanged. */
91 /* Set correct time information before making a move, and
92 * always make it time per move for the engine. */
94 time_prepare_move(struct time_info
*ti
, struct board
*board
)
98 if (ti
->period
== TT_TOTAL
) {
99 moves_left
= board_estimated_moves_left(board
);
100 assert(moves_left
> 0);
101 if (ti
->dim
== TD_GAMES
) {
102 ti
->period
= TT_MOVE
;
103 ti
->len
.games
/= moves_left
;
106 if (ti
->period
== TT_NULL
|| ti
->dim
!= TD_WALLTIME
)
109 double now
= time_now();
111 if (!ti
->len
.t
.timer_start
) {
112 ti
->len
.t
.timer_start
= now
; // we're playing the first game move
115 lag
= now
- ti
->len
.t
.timer_start
;
116 // TODO: keep statistics to get good estimate of lag not just current move
117 ti
->len
.t
.max_time
-= lag
; // can become < 0, taken into account below
118 ti
->len
.t
.recommended_time
-= lag
;
119 if (DEBUGL(2) && lag
> MAX_NET_LAG
)
120 fprintf(stderr
, "lag %0.2f > max_net_lag %0.2f\n", lag
, MAX_NET_LAG
);
122 if (ti
->period
== TT_TOTAL
) {
123 /* For non-canadian byoyomi, we use all periods as main time, just making sure
124 * to avoid running out of the last one. */
125 if (ti
->len
.t
.byoyomi_periods
> 1) {
126 ti
->len
.t
.max_time
+= (ti
->len
.t
.byoyomi_periods
- 1) * ti
->len
.t
.byoyomi_time
;
127 // Will add 1 more byoyomi_time just below
129 if (ti
->len
.t
.byoyomi_time
> 0) {
130 ti
->len
.t
.max_time
+= ti
->len
.t
.byoyomi_time
;
131 ti
->len
.t
.recommended_time
= ti
->len
.t
.max_time
;
133 /* Maximize the number of moves played uniformly in main time, while
134 * not playing faster in main time than in byoyomi. At this point,
135 * the main time remaining is ti->len.t.max_time and already includes
136 * the first (canadian) or all byoyomi periods.
137 * main_speed = max_time / main_moves >= byoyomi_time
138 * => main_moves <= max_time / byoyomi_time */
139 double actual_byoyomi
= ti
->len
.t
.byoyomi_time
- MAX_NET_LAG
;
140 if (actual_byoyomi
> 0) {
141 int main_moves
= (int)(ti
->len
.t
.max_time
/ actual_byoyomi
);
142 if (moves_left
> main_moves
)
143 moves_left
= main_moves
; // will do the rest in byoyomi
144 if (moves_left
<= 0) // possible if too much lag
148 ti
->period
= TT_MOVE
;
149 ti
->len
.t
.recommended_time
/= moves_left
; // may be < 0 if too much lag
151 // To simplify the engine code, do not leave negative times:
152 if (ti
->len
.t
.recommended_time
< 0)
153 ti
->len
.t
.recommended_time
= 0;
154 if (ti
->len
.t
.max_time
< 0)
155 ti
->len
.t
.max_time
= 0;
156 assert(ti
->len
.t
.recommended_time
<= ti
->len
.t
.max_time
+ 0.001);
158 /* Use a larger safety margin if we risk losing on time on this move: */
159 double safe_margin
= RESERVED_BYOYOMI_PERCENT
* ti
->len
.t
.byoyomi_time
/100;
160 if (safe_margin
> MAX_NET_LAG
&& ti
->len
.t
.recommended_time
>= ti
->len
.t
.max_time
- MAX_NET_LAG
) {
161 ti
->len
.t
.net_lag
= safe_margin
;
163 ti
->len
.t
.net_lag
= MAX_NET_LAG
;
167 fprintf(stderr
, "recommended_time %0.2f, max_time %0.2f, byoyomi %0.2f, lag %0.2f max %0.2f\n",
168 ti
->len
.t
.recommended_time
, ti
->len
.t
.max_time
, ti
->len
.t
.byoyomi_time
, lag
,
172 /* Start our timer. kgs does this (correctly) on "play" not "genmove"
173 * unless we are making the first move of the game. */
175 time_start_timer(struct time_info
*ti
)
177 if (ti
->period
!= TT_NULL
&& ti
->dim
== TD_WALLTIME
)
178 ti
->len
.t
.timer_start
= time_now();
181 /* Returns true if we are in byoyomi (or should play as if in byo yomi
182 * because remaining time per move in main time is less than byoyomi time
185 time_in_byoyomi(struct time_info
*ti
) {
186 return ti
->period
== TT_MOVE
&& ti
->dim
== TD_WALLTIME
&& ti
->len
.t
.byoyomi_time
> 0
187 && ti
->len
.t
.recommended_time
<= ti
->len
.t
.byoyomi_time
+ 0.001;
190 /* Returns the current time. */
195 clock_gettime(CLOCK_REALTIME
, &now
);
196 return now
.tv_sec
+ now
.tv_nsec
/1000000000.0;
199 /* Sleep for a given interval (in seconds). Return immediately if interval < 0. */
201 time_sleep(double interval
)
205 ts
.tv_nsec
= (int)(modf(interval
, &sec
)*1000000000.0);
206 ts
.tv_sec
= (int)sec
;
207 nanosleep(&ts
, NULL
); /* ignore error if interval was < 0 */