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