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