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
setup(sflag,seed)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
cleanup()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
results()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
prword(base,indx)116 prword(base, indx)
117 char *base[];
118 int indx;
119 {
120 printw("%s", base[indx]);
121 }
122
123 static int
prwidth(base,indx)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 *
getline(q)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
inputch()232 inputch()
233 {
234 return (getch() & 0177);
235 }
236
237 void
redraw()238 redraw()
239 {
240 clearok(stdscr, 1);
241 refresh();
242 }
243
244 void
flushin(fp)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
stoptime()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
starttime()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
startwords()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
addword(w)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
badword()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
showword(n)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
findword()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
showstr(str,delaysecs)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
putstr(s)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
getword(q)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
showboard(b)529 showboard(b)
530 char *b;
531 {
532 tty_showboard(b);
533 }
534
535 void
prompt(mesg)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
tty_setup()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
stop_catcher(signo)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
cont_catcher(signo)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
winch_catcher(signo)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
tty_cleanup()614 tty_cleanup()
615 {
616 move(nlines - 1, 0);
617 refresh();
618 noraw();
619 echo();
620 endwin();
621 }
622
623 static void
tty_showboard(b)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