1 /*- 2 * Copyright (c) 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Barry Brachman. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 static char sccsid[] = "@(#)mach.c 8.1 (Berkeley) 06/11/93"; 13 #endif /* not lint */ 14 15 /* 16 * Terminal interface 17 * 18 * Input is raw and unechoed 19 */ 20 #include <ctype.h> 21 #include <curses.h> 22 #include <fcntl.h> 23 #include <sgtty.h> 24 #include <signal.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <time.h> 28 29 #include "bog.h" 30 #include "extern.h" 31 32 static int ccol, crow, maxw; 33 static int colstarts[MAXCOLS], ncolstarts; 34 static int lastline; 35 int ncols, nlines; 36 37 extern char *pword[], *mword[]; 38 extern int ngames, nmwords, npwords, tnmwords, tnpwords; 39 40 static void cont_catcher __P((int)); 41 static int prwidth __P((char *[], int)); 42 static void prword __P((char *[], int)); 43 static void stop_catcher __P((int)); 44 static void tty_cleanup __P((void)); 45 static int tty_setup __P((void)); 46 static void tty_showboard __P((char *)); 47 static void winch_catcher __P((int)); 48 49 /* 50 * Do system dependent initialization 51 * This is called once, when the program starts 52 */ 53 int 54 setup(sflag, seed) 55 int sflag; 56 long seed; 57 { 58 extern int debug; 59 60 if (tty_setup() < 0) 61 return(-1); 62 63 if (!sflag) 64 time(&seed); 65 srandom(seed); 66 if (debug) 67 (void) printf("seed = %ld\n", seed); 68 return(0); 69 } 70 71 /* 72 * Do system dependent clean up 73 * This is called once, just before the program terminates 74 */ 75 void 76 cleanup() 77 { 78 tty_cleanup(); 79 } 80 81 /* 82 * Display the player's word list, the list of words not found, and the running 83 * stats 84 */ 85 void 86 results() 87 { 88 int col, row; 89 int denom1, denom2; 90 91 move(LIST_LINE, LIST_COL); 92 clrtobot(); 93 printw("Words you found (%d):", npwords); 94 refresh(); 95 move(LIST_LINE + 1, LIST_COL); 96 prtable(pword, npwords, 0, ncols, prword, prwidth); 97 98 getyx(stdscr, row, col); 99 move(row + 1, col); 100 printw("Words you missed (%d):", nmwords); 101 refresh(); 102 move(row + 2, col); 103 prtable(mword, nmwords, 0, ncols, prword, prwidth); 104 105 denom1 = npwords + nmwords; 106 denom2 = tnpwords + tnmwords; 107 108 move(SCORE_LINE, SCORE_COL); 109 printw("Percentage: %0.2f%% (%0.2f%% over %d game%s)\n", 110 denom1 ? (100.0 * npwords) / (double) (npwords + nmwords) : 0.0, 111 denom2 ? (100.0 * tnpwords) / (double) (tnpwords + tnmwords) : 0.0, 112 ngames, ngames > 1 ? "s" : ""); 113 } 114 115 static void 116 prword(base, indx) 117 char *base[]; 118 int indx; 119 { 120 printw("%s", base[indx]); 121 } 122 123 static int 124 prwidth(base, indx) 125 char *base[]; 126 int indx; 127 { 128 return (strlen(base[indx])); 129 } 130 131 /* 132 * Main input routine 133 * 134 * - doesn't accept words longer than MAXWORDLEN or containing caps 135 */ 136 char * 137 getline(q) 138 char *q; 139 { 140 register int ch, done; 141 register char *p; 142 int row, col; 143 144 p = q; 145 done = 0; 146 while (!done) { 147 ch = timerch(); 148 switch (ch) { 149 case '\n': 150 case '\r': 151 case ' ': 152 done = 1; 153 break; 154 case '\033': 155 findword(); 156 break; 157 case '\177': /* <del> */ 158 case '\010': /* <bs> */ 159 if (p == q) 160 break; 161 p--; 162 getyx(stdscr, row, col); 163 move(row, col - 1); 164 clrtoeol(); 165 refresh(); 166 break; 167 case '\025': /* <^u> */ 168 case '\027': /* <^w> */ 169 if (p == q) 170 break; 171 getyx(stdscr, row, col); 172 move(row, col - (int) (p - q)); 173 p = q; 174 clrtoeol(); 175 refresh(); 176 break; 177 #ifdef SIGTSTP 178 case '\032': /* <^z> */ 179 stop_catcher(0); 180 break; 181 #endif 182 case '\023': /* <^s> */ 183 stoptime(); 184 printw("<PAUSE>"); 185 refresh(); 186 while ((ch = inputch()) != '\021' && ch != '\023') 187 ; 188 move(crow, ccol); 189 clrtoeol(); 190 refresh(); 191 starttime(); 192 break; 193 case '\003': /* <^c> */ 194 cleanup(); 195 exit(0); 196 /*NOTREACHED*/ 197 case '\004': /* <^d> */ 198 done = 1; 199 ch = EOF; 200 break; 201 case '\014': /* <^l> */ 202 case '\022': /* <^r> */ 203 redraw(); 204 break; 205 case '?': 206 stoptime(); 207 if (help() < 0) 208 showstr("Can't open help file", 1); 209 starttime(); 210 break; 211 default: 212 if (!islower(ch)) 213 break; 214 if ((int) (p - q) == MAXWORDLEN) { 215 p = q; 216 badword(); 217 break; 218 } 219 *p++ = ch; 220 addch(ch); 221 refresh(); 222 break; 223 } 224 } 225 *p = '\0'; 226 if (ch == EOF) 227 return((char *) NULL); 228 return(q); 229 } 230 231 int 232 inputch() 233 { 234 return (getch() & 0177); 235 } 236 237 void 238 redraw() 239 { 240 clearok(stdscr, 1); 241 refresh(); 242 } 243 244 void 245 flushin(fp) 246 FILE *fp; 247 { 248 int arg; 249 250 arg = FREAD; 251 (void)ioctl(fileno(fp), TIOCFLUSH, &arg); 252 } 253 254 static int gone; 255 256 /* 257 * Stop the game timer 258 */ 259 void 260 stoptime() 261 { 262 extern long start_t; 263 long t; 264 265 (void)time(&t); 266 gone = (int) (t - start_t); 267 } 268 269 /* 270 * Restart the game timer 271 */ 272 void 273 starttime() 274 { 275 extern long start_t; 276 long t; 277 278 (void)time(&t); 279 start_t = t - (long) gone; 280 } 281 282 /* 283 * Initialize for the display of the player's words as they are typed 284 * This display starts at (LIST_LINE, LIST_COL) and goes "down" until the last 285 * line. After the last line a new column is started at LIST_LINE 286 * Keep track of each column position for showword() 287 * There is no check for exceeding COLS 288 */ 289 void 290 startwords() 291 { 292 crow = LIST_LINE; 293 ccol = LIST_COL; 294 maxw = 0; 295 ncolstarts = 1; 296 colstarts[0] = LIST_COL; 297 move(LIST_LINE, LIST_COL); 298 refresh(); 299 } 300 301 /* 302 * Add a word to the list and start a new column if necessary 303 * The maximum width of the current column is maintained so we know where 304 * to start the next column 305 */ 306 void 307 addword(w) 308 char *w; 309 { 310 int n; 311 312 if (crow == lastline) { 313 crow = LIST_LINE; 314 ccol += (maxw + 5); 315 colstarts[ncolstarts++] = ccol; 316 maxw = 0; 317 move(crow, ccol); 318 } 319 else { 320 move(++crow, ccol); 321 if ((n = strlen(w)) > maxw) 322 maxw = n; 323 } 324 refresh(); 325 } 326 327 /* 328 * The current word is unacceptable so erase it 329 */ 330 void 331 badword() 332 { 333 334 move(crow, ccol); 335 clrtoeol(); 336 refresh(); 337 } 338 339 /* 340 * Highlight the nth word in the list (starting with word 0) 341 * No check for wild arg 342 */ 343 void 344 showword(n) 345 int n; 346 { 347 int col, row; 348 349 row = LIST_LINE + n % (lastline - LIST_LINE + 1); 350 col = colstarts[n / (lastline - LIST_LINE + 1)]; 351 move(row, col); 352 standout(); 353 printw("%s", pword[n]); 354 standend(); 355 move(crow, ccol); 356 refresh(); 357 delay(15); 358 move(row, col); 359 printw("%s", pword[n]); 360 move(crow, ccol); 361 refresh(); 362 } 363 364 /* 365 * Get a word from the user and check if it is in either of the two 366 * word lists 367 * If it's found, show the word on the board for a short time and then 368 * erase the word 369 * 370 * Note: this function knows about the format of the board 371 */ 372 void 373 findword() 374 { 375 int c, col, found, i, r, row; 376 char buf[MAXWORDLEN + 1]; 377 extern char board[]; 378 extern int usedbits, wordpath[]; 379 extern char *mword[], *pword[]; 380 extern int nmwords, npwords; 381 382 getyx(stdscr, r, c); 383 getword(buf); 384 found = 0; 385 for (i = 0; i < npwords; i++) { 386 if (strcmp(buf, pword[i]) == 0) { 387 found = 1; 388 break; 389 } 390 } 391 if (!found) { 392 for (i = 0; i < nmwords; i++) { 393 if (strcmp(buf, mword[i]) == 0) { 394 found = 1; 395 break; 396 } 397 } 398 } 399 for (i = 0; i < MAXWORDLEN; i++) 400 wordpath[i] = -1; 401 usedbits = 0; 402 if (!found || checkword(buf, -1, wordpath) == -1) { 403 move(r, c); 404 clrtoeol(); 405 addstr("[???]"); 406 refresh(); 407 delay(10); 408 move(r, c); 409 clrtoeol(); 410 refresh(); 411 return; 412 } 413 414 standout(); 415 for (i = 0; wordpath[i] != -1; i++) { 416 row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1; 417 col = BOARD_COL + (wordpath[i] % 4) * 4 + 2; 418 move(row, col); 419 if (board[wordpath[i]] == 'q') 420 printw("Qu"); 421 else 422 printw("%c", toupper(board[wordpath[i]])); 423 move(r, c); 424 refresh(); 425 delay(5); 426 } 427 428 standend(); 429 430 for (i = 0; wordpath[i] != -1; i++) { 431 row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1; 432 col = BOARD_COL + (wordpath[i] % 4) * 4 + 2; 433 move(row, col); 434 if (board[wordpath[i]] == 'q') 435 printw("Qu"); 436 else 437 printw("%c", toupper(board[wordpath[i]])); 438 } 439 move(r, c); 440 clrtoeol(); 441 refresh(); 442 } 443 444 /* 445 * Display a string at the current cursor position for the given number of secs 446 */ 447 void 448 showstr(str, delaysecs) 449 char *str; 450 int delaysecs; 451 { 452 addstr(str); 453 refresh(); 454 delay(delaysecs * 10); 455 move(crow, ccol); 456 clrtoeol(); 457 refresh(); 458 } 459 460 void 461 putstr(s) 462 char *s; 463 { 464 addstr(s); 465 } 466 467 /* 468 * Get a valid word and put it in the buffer 469 */ 470 void 471 getword(q) 472 char *q; 473 { 474 int ch, col, done, i, row; 475 char *p; 476 477 done = 0; 478 i = 0; 479 p = q; 480 addch('['); 481 refresh(); 482 while (!done && i < MAXWORDLEN - 1) { 483 ch = getch() & 0177; 484 switch (ch) { 485 case '\177': /* <del> */ 486 case '\010': /* <bs> */ 487 if (p == q) 488 break; 489 p--; 490 getyx(stdscr, row, col); 491 move(row, col - 1); 492 clrtoeol(); 493 break; 494 case '\025': /* <^u> */ 495 case '\027': /* <^w> */ 496 if (p == q) 497 break; 498 getyx(stdscr, row, col); 499 move(row, col - (int) (p - q)); 500 p = q; 501 clrtoeol(); 502 break; 503 case ' ': 504 case '\n': 505 case '\r': 506 done = 1; 507 break; 508 case '\014': /* <^l> */ 509 case '\022': /* <^r> */ 510 clearok(stdscr, 1); 511 refresh(); 512 break; 513 default: 514 if (islower(ch)) { 515 *p++ = ch; 516 addch(ch); 517 i++; 518 } 519 break; 520 } 521 refresh(); 522 } 523 *p = '\0'; 524 addch(']'); 525 refresh(); 526 } 527 528 void 529 showboard(b) 530 char *b; 531 { 532 tty_showboard(b); 533 } 534 535 void 536 prompt(mesg) 537 char *mesg; 538 { 539 move(PROMPT_LINE, PROMPT_COL); 540 printw("%s", mesg); 541 move(PROMPT_LINE + 1, PROMPT_COL); 542 refresh(); 543 } 544 545 static int 546 tty_setup() 547 { 548 initscr(); 549 raw(); 550 noecho(); 551 552 /* 553 * Does curses look at the winsize structure? 554 * Should handle SIGWINCH ... 555 */ 556 nlines = LINES; 557 lastline = nlines - 1; 558 ncols = COLS; 559 560 (void) signal(SIGTSTP, stop_catcher); 561 (void) signal(SIGCONT, cont_catcher); 562 (void) signal(SIGWINCH, winch_catcher); 563 return(0); 564 } 565 566 static void 567 stop_catcher(signo) 568 int signo; 569 { 570 stoptime(); 571 noraw(); 572 echo(); 573 move(nlines - 1, 0); 574 refresh(); 575 576 (void) signal(SIGTSTP, SIG_DFL); 577 (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP-1))); 578 (void) kill(0, SIGTSTP); 579 (void) signal(SIGTSTP, stop_catcher); 580 } 581 582 static void 583 cont_catcher(signo) 584 int signo; 585 { 586 (void) signal(SIGCONT, cont_catcher); 587 noecho(); 588 raw(); 589 clearok(stdscr, 1); 590 move(crow, ccol); 591 refresh(); 592 starttime(); 593 } 594 595 /* 596 * The signal is caught but nothing is done about it... 597 * It would mean reformatting the entire display 598 */ 599 static void 600 winch_catcher(signo) 601 int signo; 602 { 603 struct winsize win; 604 605 (void) signal(SIGWINCH, winch_catcher); 606 (void) ioctl(fileno(stdout), TIOCGWINSZ, &win); 607 /* 608 LINES = win.ws_row; 609 COLS = win.ws_col; 610 */ 611 } 612 613 static void 614 tty_cleanup() 615 { 616 move(nlines - 1, 0); 617 refresh(); 618 noraw(); 619 echo(); 620 endwin(); 621 } 622 623 static void 624 tty_showboard(b) 625 char *b; 626 { 627 register int i; 628 int line; 629 630 clear(); 631 move(BOARD_LINE, BOARD_COL); 632 line = BOARD_LINE; 633 printw("+---+---+---+---+"); 634 move(++line, BOARD_COL); 635 for (i = 0; i < 16; i++) { 636 if (b[i] == 'q') 637 printw("| Qu"); 638 else 639 printw("| %c ", toupper(b[i])); 640 if ((i + 1) % 4 == 0) { 641 printw("|"); 642 move(++line, BOARD_COL); 643 printw("+---+---+---+---+"); 644 move(++line, BOARD_COL); 645 } 646 } 647 move(SCORE_LINE, SCORE_COL); 648 printw("Type '?' for help"); 649 refresh(); 650 } 651