main.c revision 1.38 1 /* $NetBSD: main.c,v 1.38 2022/05/16 21:53:41 rillig Exp $ */
2
3 /*
4 * Copyright (c) 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Ralph Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 __COPYRIGHT("@(#) Copyright (c) 1994\
38 The Regents of the University of California. All rights reserved.");
39 #endif /* not lint */
40
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 5/4/95";
44 #else
45 __RCSID("$NetBSD: main.c,v 1.38 2022/05/16 21:53:41 rillig Exp $");
46 #endif
47 #endif /* not lint */
48
49 #include <curses.h>
50 #include <err.h>
51 #include <limits.h>
52 #include <signal.h>
53 #include <stdarg.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <time.h>
57 #include <unistd.h>
58
59 #include "gomoku.h"
60
61 #define USER 0 /* get input from standard input */
62 #define PROGRAM 1 /* get input from program */
63 #define INPUTF 2 /* get input from a file */
64
65 bool interactive = true; /* true if interactive */
66 int debug; /* > 0 if debugging */
67 static int test; /* both moves come from 1: input, 2: computer */
68 static char *prog; /* name of program */
69 static char user[LOGIN_NAME_MAX]; /* name of player */
70 static FILE *debugfp; /* file for debug output */
71 static FILE *inputfp; /* file for debug input */
72
73 const char pdir[4] = "-\\|/";
74
75 struct spotstr board[BAREA]; /* info for board */
76 struct combostr frames[FAREA]; /* storage for all frames */
77 struct combostr *sortframes[2]; /* sorted list of non-empty frames */
78 u_char overlap[FAREA * FAREA]; /* true if frame [a][b] overlap */
79 short intersect[FAREA * FAREA]; /* frame [a][b] intersection */
80 int movelog[BSZ * BSZ]; /* log of all the moves */
81 int movenum; /* current move number */
82 const char *plyr[2]; /* who's who */
83
84 static int readinput(FILE *);
85 static void misclog(const char *, ...) __printflike(1, 2);
86 static void quit(void) __dead;
87 #if !defined(DEBUG)
88 static void quitsig(int) __dead;
89 #endif
90
91 int
92 main(int argc, char **argv)
93 {
94 char buf[128];
95 char fname[PATH_MAX];
96 char *tmp;
97 int color, curmove, i, ch;
98 int input[2];
99
100 /* Revoke setgid privileges */
101 setgid(getgid());
102
103 tmp = getlogin();
104 if (tmp != NULL) {
105 strlcpy(user, tmp, sizeof(user));
106 } else {
107 strcpy(user, "you");
108 }
109
110 color = curmove = 0;
111
112 prog = strrchr(argv[0], '/');
113 if (prog != NULL)
114 prog++;
115 else
116 prog = argv[0];
117
118 while ((ch = getopt(argc, argv, "bcdD:u")) != -1) {
119 switch (ch) {
120 case 'b': /* background */
121 interactive = false;
122 break;
123 case 'd': /* debugging */
124 debug++;
125 break;
126 case 'D': /* log debug output to file */
127 if ((debugfp = fopen(optarg, "w")) == NULL)
128 err(1, "%s", optarg);
129 break;
130 case 'u': /* testing: user versus user */
131 test = 1;
132 break;
133 case 'c': /* testing: computer versus computer */
134 test = 2;
135 break;
136 default:
137 fprintf(stderr, "usage: %s [-bcdu] [-Dfile] [file]\n",
138 getprogname());
139 return EXIT_FAILURE;
140 }
141 }
142 argc -= optind;
143 argv += optind;
144 if (argc != 0) {
145 if ((inputfp = fopen(*argv, "r")) == NULL)
146 err(1, "%s", *argv);
147 }
148
149 if (debug == 0)
150 srandom((unsigned int)time(0));
151 if (interactive)
152 cursinit(); /* initialize curses */
153 again:
154 bdinit(board); /* initialize board contents */
155
156 if (interactive) {
157 plyr[BLACK] = plyr[WHITE] = "???";
158 bdisp_init(); /* initialize display of board */
159 #ifdef DEBUG
160 signal(SIGINT, whatsup);
161 #else
162 signal(SIGINT, quitsig);
163 #endif
164
165 if (inputfp == NULL && test == 0) {
166 move(BSZ + 3, 0);
167 printw("Black moves first. ");
168 ask("(B)lack or (W)hite? ");
169 for (;;) {
170 ch = get_key(NULL);
171 if (ch == 'b' || ch == 'B') {
172 color = BLACK;
173 break;
174 }
175 if (ch == 'w' || ch == 'W') {
176 color = WHITE;
177 break;
178 }
179 if (ch == 'q' || ch == 'Q') {
180 quit();
181 }
182 beep();
183 ask("Please choose (B)lack or (W)hite: ");
184 }
185 move(BSZ + 3, 0);
186 clrtoeol();
187 }
188 } else {
189 setbuf(stdout, 0);
190 get_line(buf, sizeof(buf));
191 if (strcmp(buf, "black") == 0)
192 color = BLACK;
193 else if (strcmp(buf, "white") == 0)
194 color = WHITE;
195 else {
196 panic("Huh? Expected `black' or `white', got `%s'\n",
197 buf);
198 }
199 }
200
201 if (inputfp != NULL) {
202 input[BLACK] = INPUTF;
203 input[WHITE] = INPUTF;
204 } else {
205 switch (test) {
206 case 0: /* user versus program */
207 input[color] = USER;
208 input[color != BLACK ? BLACK : WHITE] = PROGRAM;
209 break;
210
211 case 1: /* user versus user */
212 input[BLACK] = USER;
213 input[WHITE] = USER;
214 break;
215
216 case 2: /* program versus program */
217 input[BLACK] = PROGRAM;
218 input[WHITE] = PROGRAM;
219 break;
220 }
221 }
222 if (interactive) {
223 plyr[BLACK] = input[BLACK] == USER ? user : prog;
224 plyr[WHITE] = input[WHITE] == USER ? user : prog;
225 bdwho(true);
226 }
227
228 for (color = BLACK; ; color = color != BLACK ? BLACK : WHITE) {
229 top:
230 switch (input[color]) {
231 case INPUTF: /* input comes from a file */
232 curmove = readinput(inputfp);
233 if (curmove != ILLEGAL)
234 break;
235 switch (test) {
236 case 0: /* user versus program */
237 input[color] = USER;
238 input[color != BLACK ? BLACK : WHITE] =
239 PROGRAM;
240 break;
241
242 case 1: /* user versus user */
243 input[BLACK] = USER;
244 input[WHITE] = USER;
245 break;
246
247 case 2: /* program versus program */
248 input[BLACK] = PROGRAM;
249 input[WHITE] = PROGRAM;
250 break;
251 }
252 plyr[BLACK] = input[BLACK] == USER ? user : prog;
253 plyr[WHITE] = input[WHITE] == USER ? user : prog;
254 bdwho(true);
255 goto top;
256
257 case USER: /* input comes from standard input */
258 getinput:
259 if (interactive) {
260 ask("Select move, (S)ave or (Q)uit.");
261 curmove = get_coord();
262 if (curmove == SAVE) {
263 FILE *fp;
264
265 ask("Save file name? ");
266 (void)get_line(fname, sizeof(fname));
267 if ((fp = fopen(fname, "w")) == NULL) {
268 misclog("cannot create save file");
269 goto getinput;
270 }
271 for (i = 0; i < movenum - 1; i++)
272 fprintf(fp, "%s\n",
273 stoc(movelog[i]));
274 fclose(fp);
275 goto getinput;
276 }
277 if (curmove != RESIGN &&
278 board[curmove].s_occ != EMPTY) {
279 /*misclog("Illegal move");*/
280 beep();
281 goto getinput;
282 }
283 } else {
284 if (!get_line(buf, sizeof(buf))) {
285 curmove = RESIGN;
286 break;
287 }
288 if (buf[0] == '\0')
289 goto getinput;
290 curmove = ctos(buf);
291 }
292 break;
293
294 case PROGRAM: /* input comes from the program */
295 if (interactive)
296 ask("Thinking...");
297 curmove = pickmove(color);
298 break;
299 }
300 if (interactive) {
301 misclog("%3d%s%-6s", movenum,
302 color != BLACK ? " " : " ",
303 stoc(curmove));
304 }
305 if ((i = makemove(color, curmove)) != MOVEOK)
306 break;
307 if (interactive)
308 bdisp();
309 }
310 if (interactive) {
311 move(BSZ + 3, 0);
312 switch (i) {
313 case WIN:
314 if (input[color] == PROGRAM)
315 addstr("Ha ha, I won");
316 else if (input[0] == USER && input[1] == USER)
317 addstr("Well, you won (and lost)");
318 else
319 addstr("Rats! you won");
320 break;
321 case TIE:
322 addstr("Wow! It's a tie");
323 break;
324 case ILLEGAL:
325 addstr("Illegal move");
326 break;
327 }
328 clrtoeol();
329 bdisp();
330 if (i != RESIGN) {
331 replay:
332 ask("Play again? ");
333 ch = get_key("YyNnQqSs");
334 if (ch == 'Y' || ch == 'y')
335 goto again;
336 if (ch == 'S') {
337 FILE *fp;
338
339 ask("Save file name? ");
340 (void)get_line(fname, sizeof(fname));
341 if ((fp = fopen(fname, "w")) == NULL) {
342 misclog("cannot create save file");
343 goto replay;
344 }
345 for (i = 0; i < movenum - 1; i++)
346 fprintf(fp, "%s\n",
347 stoc(movelog[i]));
348 fclose(fp);
349 goto replay;
350 }
351 }
352 }
353 quit();
354 /* NOTREACHED */
355 return 0;
356 }
357
358 static int
359 readinput(FILE *fp)
360 {
361 int c;
362 char buf[128];
363 size_t pos;
364
365 pos = 0;
366 while ((c = getc(fp)) != EOF && c != '\n' && pos < sizeof(buf) - 1)
367 buf[pos++] = c;
368 buf[pos] = '\0';
369 return ctos(buf);
370 }
371
372 #ifdef DEBUG
373 /*
374 * Handle strange situations.
375 */
376 /* ARGSUSED */
377 void
378 whatsup(int signum)
379 {
380 int i, n, s1, s2, d1, d2;
381 struct spotstr *sp;
382 FILE *fp;
383 char *str;
384 struct elist *ep;
385 struct combostr *cbp;
386 char input[128];
387 char tmp[128];
388
389 if (!interactive)
390 quit();
391 top:
392 ask("debug command: ");
393 if (!get_line(input, sizeof(input)))
394 quit();
395 switch (*input) {
396 case '\0':
397 goto top;
398 case 'q': /* conservative quit */
399 quit();
400 /* NOTREACHED */
401 case 'd': /* set debug level */
402 debug = input[1] - '0';
403 debuglog("Debug set to %d", debug);
404 sleep(1);
405 break;
406 case 'c':
407 break;
408 case 'b': /* back up a move */
409 if (movenum > 1) {
410 movenum--;
411 board[movelog[movenum - 1]].s_occ = EMPTY;
412 bdisp();
413 }
414 goto top;
415 case 's': /* suggest a move */
416 i = input[1] == 'b' ? BLACK : WHITE;
417 debuglog("suggest %c %s", i == BLACK ? 'B' : 'W',
418 stoc(pickmove(i)));
419 goto top;
420 case 'f': /* go forward a move */
421 board[movelog[movenum - 1]].s_occ =
422 (movenum & 1) != 0 ? BLACK : WHITE;
423 movenum++;
424 bdisp();
425 goto top;
426 case 'l': /* print move history */
427 if (input[1] == '\0') {
428 for (i = 0; i < movenum - 1; i++)
429 debuglog("%s", stoc(movelog[i]));
430 goto top;
431 }
432 if ((fp = fopen(input + 1, "w")) == NULL)
433 goto top;
434 for (i = 0; i < movenum - 1; i++) {
435 fprintf(fp, "%s", stoc(movelog[i]));
436 if (++i < movenum - 1)
437 fprintf(fp, " %s\n", stoc(movelog[i]));
438 else
439 fputc('\n', fp);
440 }
441 bdump(fp);
442 fclose(fp);
443 goto top;
444 case 'o':
445 /* avoid use w/o initialization on invalid input */
446 d1 = s1 = 0;
447
448 n = 0;
449 for (str = input + 1; *str != '\0'; str++)
450 if (*str == ',') {
451 for (d1 = 0; d1 < 4; d1++)
452 if (str[-1] == pdir[d1])
453 break;
454 str[-1] = '\0';
455 sp = &board[s1 = ctos(input + 1)];
456 n = (int)(sp->s_frame[d1] - frames) * FAREA;
457 *str++ = '\0';
458 break;
459 }
460 sp = &board[s2 = ctos(str)];
461 while (*str != '\0')
462 str++;
463 for (d2 = 0; d2 < 4; d2++)
464 if (str[-1] == pdir[d2])
465 break;
466 n += (int)(sp->s_frame[d2] - frames);
467 debuglog("overlap %s%c,%s%c = %x", stoc(s1), pdir[d1],
468 stoc(s2), pdir[d2], overlap[n]);
469 goto top;
470 case 'p':
471 sp = &board[i = ctos(input + 1)];
472 debuglog("V %s %x/%d %d %x/%d %d %d %x", stoc(i),
473 sp->s_combo[BLACK].s, sp->s_level[BLACK],
474 sp->s_nforce[BLACK],
475 sp->s_combo[WHITE].s, sp->s_level[WHITE],
476 sp->s_nforce[WHITE], sp->s_wval, sp->s_flags);
477 debuglog("FB %s %x %x %x %x", stoc(i),
478 sp->s_fval[BLACK][0].s, sp->s_fval[BLACK][1].s,
479 sp->s_fval[BLACK][2].s, sp->s_fval[BLACK][3].s);
480 debuglog("FW %s %x %x %x %x", stoc(i),
481 sp->s_fval[WHITE][0].s, sp->s_fval[WHITE][1].s,
482 sp->s_fval[WHITE][2].s, sp->s_fval[WHITE][3].s);
483 goto top;
484 case 'e': /* e {b|w} [0-9] spot */
485 str = input + 1;
486 if (*str >= '0' && *str <= '9')
487 n = *str++ - '0';
488 else
489 n = 0;
490 sp = &board[i = ctos(str)];
491 for (ep = sp->s_empty; ep != NULL; ep = ep->e_next) {
492 cbp = ep->e_combo;
493 if (n != 0) {
494 if (cbp->c_nframes > n)
495 continue;
496 if (cbp->c_nframes != n)
497 break;
498 }
499 printcombo(cbp, tmp, sizeof(tmp));
500 debuglog("%s", tmp);
501 }
502 goto top;
503 default:
504 debuglog("Options are:");
505 debuglog("q - quit");
506 debuglog("c - continue");
507 debuglog("d# - set debug level to #");
508 debuglog("p# - print values at #");
509 goto top;
510 }
511 }
512 #endif /* DEBUG */
513
514 /*
515 * Display debug info.
516 */
517 void
518 debuglog(const char *fmt, ...)
519 {
520 va_list ap;
521 char buf[128];
522
523 va_start(ap, fmt);
524 vsnprintf(buf, sizeof(buf), fmt, ap);
525 va_end(ap);
526
527 if (debugfp != NULL)
528 fprintf(debugfp, "%s\n", buf);
529 if (interactive)
530 dislog(buf);
531 else
532 fprintf(stderr, "%s\n", buf);
533 }
534
535 static void
536 misclog(const char *fmt, ...)
537 {
538 va_list ap;
539 char buf[128];
540
541 va_start(ap, fmt);
542 vsnprintf(buf, sizeof(buf), fmt, ap);
543 va_end(ap);
544
545 if (debugfp != NULL)
546 fprintf(debugfp, "%s\n", buf);
547 if (interactive)
548 dislog(buf);
549 else
550 printf("%s\n", buf);
551 }
552
553 static void
554 quit(void)
555 {
556 if (interactive) {
557 bdisp(); /* show final board */
558 cursfini();
559 }
560 exit(0);
561 }
562
563 #if !defined(DEBUG)
564 static void
565 quitsig(int dummy __unused)
566 {
567 quit();
568 }
569 #endif
570
571 /*
572 * Die gracefully.
573 */
574 void
575 panic(const char *fmt, ...)
576 {
577 va_list ap;
578
579 if (interactive) {
580 bdisp();
581 cursfini();
582 }
583
584 fprintf(stderr, "%s: ", prog);
585 va_start(ap, fmt);
586 vfprintf(stderr, fmt, ap);
587 va_end(ap);
588 fprintf(stderr, "\n");
589
590 fputs("I resign\n", stdout);
591 exit(1);
592 }
593