1 /*
2  * src/gotime.c, part of Complete Goban (game program)
3  * Copyright (C) 1995 William Shubert
4  * See "configure.h.in" for more copyright information.
5  */
6 
7 #include <wms.h>
8 #include <wms/str.h>
9 #include "goTime.h"
10 
11 
goTime_parseChars(const char * t,bool ignoreExtra,bool * err)12 int  goTime_parseChars(const char *t, bool ignoreExtra, bool *err)  {
13   bool  dummy;
14   int  numScans;
15   int  v1, v2, v3;
16   char  c1, c2, c3;
17 
18   if (err == NULL)
19     err = &dummy;
20   numScans = sscanf(t, "%d%c%d%c%d%c", &v1, &c1, &v2, &c2, &v3, &c3);
21   if (ignoreExtra)  {
22     if ((numScans > 1) && (c1 != ':'))  {
23       numScans = 1;
24     } else if ((numScans > 3) && (c2 != ':'))  {
25       numScans = 3;
26     } else if (numScans > 5)
27       numScans = 5;
28   }
29   switch(numScans)  {
30   case 1:
31     /* Format "mm" */
32     if (v1 >= 0)  {
33       *err = FALSE;
34       return(v1 * 60);
35     }
36     break;
37   case 3:
38     /* Format "mm:ss" */
39     if ((c1 == ':') && (v1 >= 0) && (v2 >= 0) && (v2 < 60))  {
40       *err = FALSE;
41       return(v1 * 60 + v2);
42     }
43     break;
44   case 5:
45     /* Format "hh:mm:ss" */
46     if ((c1 == ':') && (c2 == ':') && (v1 >= 0) && (v2 >= 0) && (v3 >= 0) &&
47 	(v2 < 60) && (v3 < 60))  {
48       *err = FALSE;
49       return(v1*60*60 + v2*60 + v3);
50     }
51     break;
52   default:
53     break;
54   }
55   *err = TRUE;
56   return(0);
57 }
58 
59 
goTime_str(int time,Str * out)60 void  goTime_str(int time, Str *out)  {
61   assert(time >= 0);
62   if (time < 60*60)  {
63     str_print(out, "%d:%02d", time/60, time%60);
64   } else  {
65     str_print(out, "%d:%02d:%02d", time/(60*60), (time/60)%60, time%60);
66   }
67 }
68 
69 
goTime_describeStr(const GoTime * time,Str * out)70 void  goTime_describeStr(const GoTime *time, Str *out)  {
71   Str  tmp;
72 
73   switch(time->type)  {
74   case(goTime_none):
75     str_copyChar(out, '-');
76     break;
77   case(goTime_absolute):
78     goTime_str(time->main, out);
79     break;
80   case(goTime_canadian):
81     str_init(&tmp);
82     goTime_str(time->main, out);
83     goTime_str(time->by, &tmp);
84     str_catChar(out, '+');
85     str_cat(out, &tmp);
86     str_catChar(out, '/');
87     str_catInt(out, time->aux);
88     str_deinit(&tmp);
89     break;
90   case(goTime_japanese):
91     str_init(&tmp);
92     goTime_str(time->main, out);
93     goTime_str(time->by, &tmp);
94     str_catChar(out, '(');
95     str_catInt(out, time->aux);
96     str_catChar(out, 'x');
97     str_cat(out, &tmp);
98     str_catChar(out, ')');
99     str_deinit(&tmp);
100     break;
101   case(goTime_ing):
102     str_init(&tmp);
103     goTime_str(time->main, out);
104     goTime_str(time->by, &tmp);
105     str_catChar(out, '+');
106     str_cat(out, &tmp);
107     str_catChar(out, 'x');
108     str_catInt(out, time->aux);
109     str_deinit(&tmp);
110     break;
111   default:
112     assert(0);
113     break;
114   }
115 }
116 
117 
goTime_parseDescribeChars(GoTime * time,const char * desc)118 void  goTime_parseDescribeChars(GoTime *time, const char *desc)  {
119   if (isdigit(desc[0]))  {
120     time->main = goTime_parseChars(desc, TRUE, NULL);
121     for (;;)  {
122       switch(*(++desc))  {
123       case '\0':
124 	time->type = goTime_absolute;
125 	return;
126       case '+':
127 	++desc;
128 	time->by = goTime_parseChars(desc, TRUE, NULL);
129 	while (*desc && (*desc != '/') && (*desc != 'x'))
130 	  ++desc;
131 	switch(*desc)  {
132 	case '/':
133 	  time->type = goTime_canadian;
134 	  ++desc;
135 	  time->aux = wms_atoi(desc, NULL);
136 	  break;
137 	case 'x':
138 	  time->type = goTime_ing;
139 	  ++desc;
140 	  time->aux = wms_atoi(desc, NULL);
141 	  break;
142 	default:
143 	  time->type = goTime_none;
144 	  break;
145 	}
146 	return;
147 	break;
148       case '(':
149 	time->type = goTime_japanese;
150 	++desc;
151 	/* Can't use wms_atoi here, it will report an error and return 0. */
152 	time->aux = atoi(desc);
153 	while (*desc && (*desc != 'x'))
154 	  ++desc;
155 	if (*desc == 'x')  {
156 	  time->by = goTime_parseChars(desc+1, TRUE, NULL);
157 	} else  {
158 	  time->type = goTime_none;
159 	}
160 	return;
161 	break;
162       default:
163 	break;
164       }
165     }
166   } else
167     time->type = goTime_none;
168 }
169 
170 
goTime_remainStr(const GoTime * time,const GoTimer * timer,Str * out)171 void  goTime_remainStr(const GoTime *time, const GoTimer *timer, Str *out)  {
172   int  tLeft;
173 
174   tLeft = timer->timeLeft;
175   if (timer->usLeft)
176     ++tLeft;
177   if (tLeft < 0)
178     tLeft = 0;
179   switch(time->type)  {
180   case(goTime_none):
181     str_copyChar(out, '-');
182     break;
183   case(goTime_absolute):
184     goTime_str(tLeft, out);
185     break;
186   case(goTime_canadian):
187     goTime_str(tLeft, out);
188     str_catChar(out, '/');
189     if (timer->aux == 0)  {
190       str_catChar(out, '-');
191     } else  {
192       str_catInt(out, timer->aux);
193     }
194     break;
195   case(goTime_japanese):
196     goTime_str(tLeft, out);
197     if (tLeft <= time->aux * time->by)  {
198       str_catChar(out, '(');
199       if (tLeft < 0)
200 	str_catChar(out, '0');
201       else
202 	str_catInt(out, (tLeft + time->by - 1) / time->by);
203       str_catChar(out, ')');
204     }
205     break;
206   case(goTime_ing):
207     goTime_str(tLeft, out);
208     if (timer->aux > 0)  {
209       str_catChar(out, '+');
210       str_catInt(out, timer->aux);
211     }
212     break;
213   default:
214     assert(0);
215     break;
216   }
217 }
218 
219 
goTimer_init(GoTimer * timer,const GoTime * time)220 void  goTimer_init(GoTimer *timer, const GoTime *time)  {
221   timer->timeLeft = time->main;
222   if (time->type == goTime_ing)
223     timer->aux = time->aux;
224   else
225     timer->aux = 0;
226   timer->usLeft = 0;
227 }
228 
229 
goTime_startTimer(const GoTime * time,GoTimer * timer)230 void  goTime_startTimer(const GoTime *time, GoTimer *timer)  {
231   struct timezone  tzone;
232 
233   gettimeofday(&timer->startTime, &tzone);
234 }
235 
236 
237 /*
238  * goTime_checkTimer() returns FALSE if you are out of time.
239  * Call it whenever you want the clock to be updated.  Every second is nice.
240  */
goTime_checkTimer(const GoTime * time,GoTimer * timer)241 bool  goTime_checkTimer(const GoTime *time, GoTimer *timer)  {
242   struct timeval now;
243   struct timezone  tzone;
244 
245   gettimeofday(&now, &tzone);
246   timer->usLeft -= (now.tv_usec - timer->startTime.tv_usec);
247   while (timer->usLeft <= 0)  {
248     timer->usLeft += 1000000;
249     --timer->timeLeft;
250   }
251   timer->timeLeft -= (now.tv_sec - timer->startTime.tv_sec);
252   timer->startTime = now;
253   if (timer->timeLeft < 0)  {
254     switch(time->type)  {
255     case goTime_none:
256       timer->timeLeft = 1;
257       break;
258     case goTime_absolute:
259     case goTime_japanese:
260       timer->timeLeft = -1;
261       timer->usLeft = 1000000;
262       break;
263     case goTime_canadian:
264       if (timer->aux == 0)  {
265 	timer->aux = time->aux;
266 	timer->timeLeft += time->by;
267 	if (timer->timeLeft < 0)  {
268 	  timer->timeLeft = -1;
269 	  timer->usLeft = 1000000;
270 	}
271       } else  {
272 	timer->timeLeft = -1;
273 	timer->usLeft = 1000000;
274       }
275       break;
276     case goTime_ing:
277       while ((timer->timeLeft < 0) && (timer->aux > 0))  {
278 	timer->timeLeft += time->by;
279 	--timer->aux;
280       }
281       if ((timer->timeLeft < 0) && (timer->aux == 0))  {
282 	timer->timeLeft = -1;
283 	timer->usLeft = 1000000;
284       }
285       break;
286     default:
287       assert(0);
288       break;
289     }
290   }
291   return(timer->timeLeft >= 0);
292 }
293 
294 
goTime_endTimer(const GoTime * time,GoTimer * timer)295 bool  goTime_endTimer(const GoTime *time, GoTimer *timer)  {
296   bool  result;
297   int  left;
298 
299   result = goTime_checkTimer(time, timer);
300   if (time->type == goTime_japanese)  {
301     left = timer->timeLeft;
302     if (timer->usLeft)
303       ++left;
304     if ((left > 0) && (left <= time->aux * time->by))  {
305       left = (left + time->by - 1);
306       timer->timeLeft = left - (left % time->by);
307       timer->usLeft = 0;
308     }
309   } else if (time->type == goTime_canadian)  {
310     if (timer->aux)  {
311       --timer->aux;
312       if (timer->aux == 0)  {
313 	timer->aux = time->aux;
314 	timer->timeLeft = time->by;
315 	timer->usLeft = 0;
316       }
317     }
318   }
319   return(result);
320 }
321 
322 
goTime_ingPenalty(const GoTime * time,const GoTimer * timer)323 int  goTime_ingPenalty(const GoTime *time, const GoTimer *timer)  {
324   if (time->type == goTime_ing)
325     return(2 * (time->aux - timer->aux));
326   else
327     return(0);
328 }
329