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