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