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