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