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