xref: /original-bsd/games/boggle/boggle/mach.c (revision 3705696b)
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