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