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
main(int argc,char ** argv)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
readinput(FILE * fp)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
whatsup(int signum)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
dlog(char * str)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
logit(char * str)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
qlog(char * str)517 qlog(char *str)
518 {
519 dlog(str);
520 if (interactive)
521 beep();
522 sleep(5);
523 quit(0);
524 }
525
526 void
quit(int sig)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
panic(char * str)540 panic(char *str)
541 {
542 fprintf(stderr, "%s: %s\n", __progname, str);
543 fputs("resign\n", stdout);
544 quit(0);
545 }
546