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