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