afdb32cd3663f17bdd489a7104e8fc4fd4cddc29
[gnushogi.git] / gnushogi / tcontrl.c
1 /*
2  * FILE: tcontrl.c
3  *
4  * ----------------------------------------------------------------------
5  * Copyright (c) 1993, 1994, 1995 Matthias Mutz
6  * Copyright (c) 1999 Michael Vanier and the Free Software Foundation
7  * Copyright (c) 2008, 2013, 2014 Yann Dirson and the Free Software Foundation
8  *
9  * GNU SHOGI is based on GNU CHESS
10  *
11  * Copyright (c) 1988, 1989, 1990 John Stanback
12  * Copyright (c) 1992 Free Software Foundation
13  *
14  * This file is part of GNU SHOGI.
15  *
16  * GNU Shogi is free software; you can redistribute it and/or modify it
17  * under the terms of the GNU General Public License as published by the
18  * Free Software Foundation; either version 3 of the License,
19  * or (at your option) any later version.
20  *
21  * GNU Shogi is distributed in the hope that it will be useful, but WITHOUT
22  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24  * for more details.
25  *
26  * You should have received a copy of the GNU General Public License along
27  * with GNU Shogi; see the file COPYING. If not, see
28  * <http://www.gnu.org/licenses/>.
29  * ----------------------------------------------------------------------
30  *
31  */
32
33
34 #include "gnushogi.h"
35 #include <math.h>
36
37 #define ALTERNATIVE_TC
38
39
40 /*
41  * In a networked enviroment gnushogi might be compiled on different hosts
42  * with different random number generators; that is not acceptable if they
43  * are going to share the same transposition table.
44  */
45
46 static unsigned long next = 1;
47
48 unsigned int
49 urand(void)
50 {
51     next *= 1103515245;
52     next += 12345;
53     return ((unsigned int) (next >> 16) & 0xFFFF);
54 }
55
56
57
58 void
59 gsrand(unsigned int seed)
60 {
61     next = seed;
62 }
63
64
65
66 void
67 TimeCalc()
68 {
69     /* adjust number of moves remaining in gamein games */
70     int increment = 0;
71     int topsum = 0;
72     int tcompsum = 0;
73     int me, him;
74     int i;
75
76     /* Don't do anything until you have enough numbers. */
77     if (GameCnt < (MINGAMEIN * 2))
78         return;
79
80     /* Calculate average time in sec for last MINGAMEIN moves. */
81     for (i = 0; i < MINGAMEIN; i++)
82     {
83         tcompsum += timecomp[i];
84         topsum += timeopp[i];
85     }
86
87     topsum   /= (100 * MINGAMEIN);
88     tcompsum /= (100 * MINGAMEIN);
89
90     /* If I have less time than opponent add another move. */
91     me  = TimeControl.clock[computer] / 100;
92     him = TimeControl.clock[opponent] / 100;
93
94     if (me < him)
95         increment += 2;
96
97     if (((him - me) > 60) || ((me < him) && (me < 120)))
98         increment++;
99
100     /* If I am losing more time with each move add another. */
101     /* If (!((me - him) > 60) && tcompsum > topsum) increment++; */
102
103     if (tcompsum > topsum)
104     {
105         increment += 2;
106     }
107     else if ((TimeControl.moves[computer] < MINMOVES) && !increment)
108     {
109         /* ... but don't let moves go below MINMOVES. */
110         increment++;
111     }
112     else if ((me > him) && (tcompsum < topsum))
113     {
114         /* If I am doing really well use more time per move. */
115         increment = -1;
116     }
117
118     /* If not fischer clock be careful about time. */
119     /* CHECKME: what's a fischer clock? */
120
121     if ((TCadd == 0) && (increment > 0))
122         increment += 2;
123
124     if ((me == 0) && (increment > 0))
125         increment += 2;
126
127     TimeControl.moves[computer] += increment;
128 }
129
130
131
132 /*
133  * Set ResponseTime, TCcount, and TCleft.
134  */
135
136 void SetResponseTime(short side)
137 {
138 #ifdef ALTERNATIVE_TC
139     int DetermineTCcount = true;
140
141     if (TCflag)
142     {
143         TCcount = 0;
144
145         if (TimeControl.moves[side] < 1)
146             TimeControl.moves[side] = 1;
147
148         /* special case time per move specified */
149         if (flag.onemove)
150         {
151             ResponseTime = TimeControl.clock[side] - 100;
152             TCleft = 0;
153         }
154         else
155         {
156             /* calculate avg time per move remaining */
157             if (TimeControl.clock[side] <= 0)
158             {
159                 ResponseTime = 0;
160                 TCleft = (long)MINRESPONSETIME / MAXTCCOUNTX;
161             }
162             else
163             {
164                 short rtf = in_opening_stage ? 8 : 2;
165                 short tcq = in_opening_stage ? 2 : 4;
166                 int moves = TimeControl.moves[side];
167
168                 if(!xboard) /* no pre-add of increment in XBoard mode */
169                     TimeControl.clock[side] += TCadd;
170                 if(TCflag == 2 && TCadd == 0) /* sudden death */
171                     moves = (moves < 30 ? 30 : moves);
172                 ResponseTime = (TimeControl.clock[side])
173                     / (moves * rtf + 1);
174                 TCleft = (long)ResponseTime / tcq;
175                 ResponseTime += TCadd / 2;
176             }
177
178             if (TimeControl.moves[side] < 5)
179             {
180                 TCcount = MAXTCCOUNTX - 10;
181
182                 if (TCcount < 0)
183                     TCcount = 0;
184
185                 DetermineTCcount = false;
186             }
187         }
188
189         if (ResponseTime < MINRESPONSETIME)
190         {
191             ResponseTime = MINRESPONSETIME;
192             TCcount = MAXTCCOUNTX - 10;
193
194             if (TCcount < 0)
195                 TCcount = 0;
196
197             DetermineTCcount = false;
198         }
199
200         if (!hard_time_limit && (ResponseTime < 2 * MINRESPONSETIME))
201         {
202             TCcount = MAXTCCOUNTX - 10;
203
204             if (TCcount < 0)
205                 TCcount = 0;
206
207             DetermineTCcount = false;
208         }
209     }
210     else
211     {
212         TCleft = 0;
213         ResponseTime = MaxResponseTime;
214         ElapsedTime(COMPUTE_AND_INIT_MODE);
215     }
216
217     if (DetermineTCcount)
218     {
219         if (TCleft )
220         {
221             int AllowedCounts
222                 = ((int)((TimeControl.clock[side] - ResponseTime)) / 2)
223                 / TCleft;
224
225             if (AllowedCounts <= 0)
226                 TCcount = MAXTCCOUNTX;
227             else if (AllowedCounts > MAXTCCOUNTX)
228                 TCcount = 0;
229             else
230                 TCcount = MAXTCCOUNTX - AllowedCounts;
231         }
232         else
233         {
234             TCcount = MAXTCCOUNTX;
235         }
236     }
237
238     if (ResponseTime < MINRESPONSETIME)
239         ResponseTime = MINRESPONSETIME;
240
241 #else
242
243     if (TCflag)
244     {
245         TCcount = 0;
246
247         if (TimeControl.moves[side] < 1)
248             TimeControl.moves[side] = 1;
249
250         /* special case time per move specified */
251         if (flag.onemove)
252         {
253             ResponseTime = TimeControl.clock[side] - 100;
254             TCleft = 0;
255         }
256         else
257         {
258             /* calculate avg time per move remaining */
259             int moves = TimeControl.moves[side];
260
261             if(!xboard) /* no pre-add of increment in XBoard mode */
262                 TimeControl.clock[side] += TCadd;
263
264             if(TCflag == 2 && TCadd == 0) /* sudden death */
265                 moves = (moves < 30 ? 30 : moves);
266             ResponseTime = (TimeControl.clock[side])
267                 / (moves * 2 + 1);
268             TCleft = (int) ResponseTime / 3;
269             ResponseTime += TCadd / 2;
270
271             if (TimeControl.moves[side] < 5)
272                 TCcount = MAXTCCOUNTX - 10;
273         }
274
275         if (ResponseTime < 101)
276         {
277             ResponseTime = 100;
278             TCcount = MAXTCCOUNTX - 10;
279         }
280         else if (ResponseTime < 200)
281         {
282             TCcount = MAXTCCOUNTX - 10;
283         }
284     }
285     else
286     {
287         ResponseTime = MaxResponseTime;
288         TCleft = 0;
289         ElapsedTime(COMPUTE_AND_INIT_MODE);
290     }
291
292     if (TCleft)
293     {
294         TCcount = ((int)((TimeControl.clock[side] - ResponseTime)) / 2)
295             / TCleft;
296
297         if (TCcount > MAXTCCOUNTX)
298             TCcount = 0;
299         else
300             TCcount = MAXTCCOUNTX - TCcount;
301     }
302     else
303     {
304         TCcount = MAXTCCOUNTX;
305     }
306 #endif
307 printf("# %2d. moves=%d,%d time=%d,%d ResponseTime=%d+%d\n",GameCnt,TimeControl.moves[computer],TimeControl.moves[opponent],TimeControl.clock[computer],TimeControl.clock[opponent],ResponseTime,TCleft);
308     assert(TCcount <= MAXTCCOUNTX);
309 }
310
311
312
313 void
314 CheckForTimeout(int score, int globalscore, int Jscore, int zwndw)
315 {
316     if (flag.musttimeout || (Sdepth >= MaxSearchDepth))
317         flag.timeout = true;
318
319     else if (TCflag && (Sdepth > (MINDEPTH - 1)) && (TCcount < MAXTCCOUNTR))
320     {
321         if (killr0[1] != PrVar[1] /* || Killr0[2] != PrVar[2] */)
322         {
323             TCcount++;
324             ExtraTime += TCleft;
325         }
326
327         if ((TCcount < MAXTCCOUNTR)
328             && (abs(score - globalscore) / Sdepth) > ZDELTA)
329         {
330             TCcount++;
331             ExtraTime += TCleft;
332         }
333     }
334
335     if ((score > (Jscore - zwndw)) && (score > (Tree[1].score + 250)))
336         ExtraTime = 0;
337
338     ElapsedTime(COMPUTE_MODE);
339
340     if (root->flags & exact)
341         flag.timeout = true;
342     /*else if (Tree[1].score < -SCORE_LIMIT) flag.timeout = true;
343      */
344 #if defined OLDTIME || !defined HAVE_GETTIMEOFDAY
345     else if (!(Sdepth < MINDEPTH)
346              && TCflag
347              && ((4 * et) > (2*ResponseTime + ExtraTime)))
348         flag.timeout = true;
349 #else
350     else if (!(Sdepth < MINDEPTH)
351              && TCflag
352              && ((int)(1.93913099l * (pow((double)et, 1.12446928l)))
353                  > (ResponseTime + ExtraTime)))
354         flag.timeout = true;
355 #endif
356
357     if (flag.timeout)
358         dsp->ShowMessage("timeout");
359 }
360
361
362 /*
363  * Determine the time that has passed since the search was started. If the
364  * elapsed time exceeds the target(ResponseTime + ExtraTime) then set timeout
365  * to true which will terminate the search.
366  * iop = COMPUTE_MODE calculate et, bump ETnodes
367  * iop = COMPUTE_AND_INIT_MODE calculate et, set timeout if time exceeded,
368  *     set reference time
369  */
370 void
371 ElapsedTime(ElapsedTime_mode iop)
372 {
373     long current_time;
374 #ifdef HAVE_GETTIMEOFDAY
375     struct timeval tv;
376 #endif
377
378     dsp->PollForInput();
379
380 #ifdef HAVE_GETTIMEOFDAY
381     gettimeofday(&tv, NULL);
382     current_time = tv.tv_sec*100 + (tv.tv_usec/10000);
383 #else
384     et = ((current_time = time((long *) 0)) - time0) * 100;
385 #endif
386
387 #ifdef INTERRUPT_TEST
388     if (iop == INIT_INTERRUPT_MODE)
389     {
390         itime0 = current_time;
391     }
392     else if (iop == COMPUTE_INTERRUPT_MODE)
393     {
394         it = current_time - itime0;
395     }
396     else
397 #endif
398     {
399 #ifdef HAVE_GETTIMEOFDAY
400         et = current_time - time0;
401 #endif
402         ETnodes = NodeCnt + znodes;
403
404         if (et < 0)
405         {
406 #ifdef INTERRUPT_TEST
407             printf("elapsed time %ld not positive\n", et);
408 #endif
409             et = 0;
410         }
411
412         if (iop == COMPUTE_AND_INIT_MODE)
413         {
414             if ((et > (ResponseTime + ExtraTime)) && (Sdepth > MINDEPTH))
415                 flag.timeout = true;
416
417             time0 = current_time;
418         }
419
420 #ifdef QUIETBACKGROUND
421         if (!background)
422 #endif
423             dsp->UpdateClocks();
424     }
425 }
426
427
428 void
429 SetTimeControl(void)
430 {
431     if (TCflag)
432     {
433         TimeControl.moves[black] = TimeControl.moves[white] = TCmoves;
434         TimeControl.clock[black] += 6000L * TCminutes + TCseconds * 100;
435         TimeControl.clock[white] += 6000L * TCminutes + TCseconds * 100;
436     }
437     else
438     {
439         TimeControl.moves[black] = TimeControl.moves[white] = 0;
440         TimeControl.clock[black] = TimeControl.clock[white] = 0;
441     }
442
443     flag.onemove = (TCmoves == 1);
444     et = 0;
445     ElapsedTime(COMPUTE_AND_INIT_MODE);
446 }
447
448 void
449 RenewTimeControl(int side, int TCadd)
450 {
451     if (flag.gamein || TCadd)
452     {
453         TimeCalc();
454     }
455     else if (TimeControl.moves[side] == 0)
456     {
457         if (XC)
458         {
459             if (XCmore < XC)
460             {
461                 TCmoves   = XCmoves[XCmore];
462                 TCminutes = XCminutes[XCmore];
463                 TCseconds = XCseconds[XCmore];
464                 XCmore++;
465             }
466         }
467
468         SetTimeControl();
469     }
470 }