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