date and time created 85/05/30 19:13:39 by sklower
[unix-history] / usr / src / old / boggle / boggle.c
CommitLineData
4dba9a68 1#ifndef lint
a9e5f6cf 2static char sccsid[] = "@(#)boggle.c 4.2 %G%";
4dba9a68
SL
3#endif
4
5#include <ctype.h>
6#include <errno.h>
7#include <setjmp.h>
8#include <sgtty.h>
9#include <signal.h>
10#include <stdio.h>
11
12/* basic parameters */
13#define N 4
14#define SSIZE 200
15#define MAXWORDS 1000
16#define CWIDTH 10
17#define LWIDTH 80
18
19/* parameters defined in terms of above */
20#define BSIZE (N*N)
21#define row(x) (x/N)
22#define col(x) (x%N)
23
24/* word being searched for */
25int wlength;
26int numsame;
27char wbuff [BSIZE+1];
28
29/* tty and process control */
30extern int errno;
31int status;
32int pipefd[2];
33int super = 0;
34int delct = 1;
35int zero = 0;
36int master = 1;
37int column;
38int *timept;
39int timeint[] = {60,60,50,7,1,1,1,0};
40long timein;
41extern long int time();
42struct sgttyb origttyb, tempttyb;
43int ctlecho = 0;
44int lctlech = LCTLECH;
45jmp_buf env;
46
47/* monitoring variables */
48int games;
49int logfile = -1;
50long logloc;
51char logbuff[100] = {"inst\t"};
52extern char *ctime(), *getlogin();
53extern long lseek();
54
55/* dictionary interface */
56char defname[] = "/usr/games/bogdict";
57char *dictname = &defname[0];
58FILE *dict;
59
60/* structures for doing matching */
61struct frame {
62 struct frame *parent;
63 int place;
64};
65struct frame stack [SSIZE];
66struct frame *level [BSIZE+1];
67
68/* the board and subsidiary structures */
69char present [BSIZE+1];
70char board [BSIZE];
71char olink [BSIZE];
72char adj [BSIZE+1][BSIZE];
73char occurs [26];
74
75/* the boggle cubes */
76char *cube [BSIZE] = {
77 "forixb", "moqabj", "gurilw", "setupl",
78 "cmpdae", "acitao", "slcrae", "romash",
79 "nodesw", "hefiye", "onudtk", "tevign",
80 "anedvz", "pinesh", "abilyt", "gkyleu"
81};
82
83
84/* storage for words found */
85int ubotch, ustart, wcount;
86char *word [MAXWORDS];
87char *freesp;
88char space[10000];
89
90endline ()
91{
92 if (column != 0) {
93 putchar('\n');
94 column = 0;
95 }
96}
97
98timeout ()
99{
100 if (*timept > 0) {
101 signal (SIGALRM, timeout);
102 alarm(*timept++);
103 }
104 putchar('\007');
105}
106
107interrupt ()
108{
109 signal(SIGINT, interrupt);
110 if (delct++ >= 1)
111 longjmp(env, 1);
112 timept = &zero;
113}
114
115goodbye (stat)
116int stat;
117{
118 if (master != 0) {
119 wait(&status);
120 if ( ctlecho & LCTLECH ) {
121 ioctl( fileno(stdin), TIOCLBIS, &lctlech );
122 }
123 stty(fileno(stdin), &origttyb);
124 }
125 exit(stat);
126}
127
128clearscreen ()
129{
130 stty (fileno(stdin), &tempttyb);
131 printf("\n\033\f\r");
132}
133
134compare (a, b)
135char **a, **b;
136{
137 return(wordcomp(*a, *b));
138}
139
140wordcomp (p, q)
141register char *p, *q;
142{
143 if (*p=='0' && *q!='0')
144 return(-1);
145 if (*p!='0' && *q=='0')
146 return(1);
147 while (*++p == *++q && isalpha(*p)) ;
148 if (!isalpha(*p))
149 return(-isalpha(*q));
150 if (!isalpha(*q))
151 return(1);
152 return(*p-*q);
153}
154
155printinst ()
156{
157 stty (fileno(stdin), &tempttyb);
158 printf("instructions?");
159 if (getchar() == 'y') {
160 clearscreen();
161 printf(" The object of Boggle (TM Parker Bros.) is to find, within 3\n");
162 printf("minutes, as many words as possible in a 4 by 4 grid of letters. Words\n");
163 printf("may be formed from any sequence of 3 or more adjacent letters in the\n");
164 printf("grid. The letters may join horizontally, vertically, or diagonally.\n");
165 printf("However, no position in the grid may be used more than once within any\n");
166 printf("one word. In competitive play amongst humans, each player is given\n");
167 printf("credit for those of his words which no other player has found.\n");
168 printf(" This program is intended for people wishing to sharpen their\n");
169 printf("skills at Boggle. If you invoke the program with 4 arguments of 4\n");
170 printf("letters each, (e.g. \"boggle appl epie moth erhd\") the program forms the\n");
171 printf("obvious Boggle grid and lists all the words from /usr/dict/words found\n");
172 printf("therein. If you invoke the program without arguments, it will generate\n");
173 printf("a board for you, let you enter words for 3 minutes, and then tell you\n");
174 printf("how well you did relative to /usr/dict/words.\n");
175 printf(" In interactive play, enter your words separated by spaces, tabs,\n");
176 printf("or newlines. A bell will ring when there is 2:00, 1:00, 0:10, 0:02,\n");
177 printf("0:01, and 0:00 time left. You may complete any word started before the\n");
178 printf("expiration of time. You can surrender before time is up by hitting\n");
179 printf("'break'. While entering words, your erase character is only effective\n");
180 printf("within the current word and your line kill character is ignored.\n");
181 printf(" Advanced players may wish to invoke the program with 1 or 2 +'s as\n");
a9e5f6cf 182 printf("the first argument. The first + removes the restriction that positions\n");
4dba9a68
SL
183 printf("can only be used once in each word. The second + causes a position to\n");
184 printf("be considered adjacent to itself as well as its (up to) 8 neighbors.\n");
185 printf("Hit any key to begin.\n");
186 stty (fileno(stdin), &tempttyb);
187 getchar();
188 }
189 stty (fileno(stdin), &tempttyb);
190}
191
192setup ()
193{
194 register int i, j;
195 int rd, cd, k;
196 for (i=0; i<BSIZE; i++) {
197 adj[i][i] = super>=2 ? 1 : 0;
198 adj[BSIZE][i] = 1;
199 for (j=0; j<i; j++) {
200 rd = row(i)-row(j);
201 cd = col(i)-col(j);
202 k = 0;
203 switch (rd) {
204 case -1:
205 case 1:
206 if (-1<=cd && cd<=1)
207 k = 1;
208 break;
209 case 0:
210 if (cd==-1 || cd==1)
211 k = 1;
212 break;
213 }
214 adj[i][j] = adj[j][i] = k;
215 }
216 }
217 stack[0].parent = &stack[0];
218 stack[0].place = BSIZE;
219 level[0] = &stack[0];
220 level[1] = &stack[1];
221}
222
223makelists ()
224{
225 register int i, c;
226 for (i=0; i<26; i++)
227 occurs[i] = BSIZE;
228 for (i=0; i<BSIZE; i++) {
229 c = board[i];
230 olink[i] = occurs[c-'a'];
231 occurs[c-'a'] = i;
232 }
233}
234
235genboard ()
236{
237 register int i, j;
238 for (i=0; i<BSIZE; i++)
239 board[i] = 0;
240 for (i=0; i<BSIZE; i++) {
241 j = rand()%BSIZE;
242 while (board[j] != 0)
243 j = (j+1)%BSIZE;
244 board[j] = cube[i][rand()%6];
245 }
246}
247
248printboard ()
249{
250 register int i, j;
251 for (i=0; i<N; i++) {
252 printf("\t\t\t\t\b\b");
253 for (j=0; j<N; j++) {
254 putchar ((putchar(board[i*N+j]) == 'q') ? 'u' : ' ');
255 putchar(' ');
256 }
257 putchar('\n');
258 }
259 putchar('\n');
260}
261
262getdword ()
263{
264 /* input: numsame = # chars same as last word */
265 /* output: numsame = # same chars for next word */
266 /* word in wbuff[0]...wbuff[wlength-1] */
267 register int c;
268 register char *p;
269 if (numsame == EOF)
270 return (0);
271 p = &wbuff[numsame]+1;
272 while ((*p++ = c = getc(dict)) != EOF && isalpha(c)) ;
273 numsame = c;
274 wlength = p - &wbuff[2];
275 return (1);
276}
277
278getuword ()
279{
280 int c;
281 register char *p, *q, *r;
282 numsame = 0;
283 while (*timept>0 && (isspace(c=getchar()) || c==EOF));
284 if (*timept == 0)
285 return(0);
286 word[wcount++] = freesp;
287 *freesp++ = '0';
288 r = &wbuff[1];
289 q = p = freesp;
290 *p++ = c;
291 while (!isspace(c = getchar())) {
292 if (c == EOF)
293 continue;
294 if (c == origttyb.sg_erase) {
295 if (p > q)
296 p--;
297 continue;
298 }
299 *p++ = c;
300 }
301 freesp = p;
302 for (p=q; p<freesp && r<&wbuff[BSIZE]; )
303 if (islower(c = *p++) && (*r++ = *q++ = c) == 'q' && *p == 'u')
304 p++;
305 *(freesp = q) = '0';
306 wlength = r-&wbuff[1];
307 return(1);
308}
309
310aputuword (ways)
311int ways;
312{
313 *word[wcount-1] = ways>=10 ? '*' : '0'+ways;
314}
315
316aputword (ways)
317int ways;
318{
319 /* store (wbuff, ways) in next slot in space */
320 register int i;
321 *freesp++ = ways>=10 ? '*' : '0'+ways;
322 for (i=1; i<= wlength; i++)
323 *freesp++ = wbuff[i];
324 word[++wcount] = freesp;
325}
326
327tputword (ways)
328int ways;
329{
330 /* print (wbuff, ways) on terminal */
331 wbuff[wlength+1] = '0';
332 wbuff[0] = ways>=10 ? '*' : '0'+ways;
333 outword(&wbuff[0]);
334}
335
336outword (p)
337register char *p;
338{
339 register int newcol;
340 register char *q;
341 for (q=p+1; isalpha(*q); ) {
342 putchar(*q);
343 if (*q++ == 'q') {
344 putchar('u');
345 column++;
346 }
347 }
348 column += q-p-1;
349 if (column > LWIDTH-CWIDTH) {
350 putchar('\n');
351 column = 0;
352 return;
353 }
354 newcol = ((column+CWIDTH)/CWIDTH)*CWIDTH;
355 while (((column+8)/8)*8 <= newcol) {
356 putchar('\t');
357 column = ((column+8)/8)*8;
358 }
359 while (column < newcol) {
360 putchar(' ');
361 column++;
362 }
363}
364
365printdiff ()
366{
367 register int c, d, u;
368 char both, donly, uonly;
369 word[wcount] = freesp;
370 *freesp = '0';
371 both = donly = uonly = column = d = 0;
372 u = ustart;
373 while (d < ubotch) {
374 c = u<wcount ? wordcomp (word[d], word[u]) : -1;
375 if (c == 0) {
376 /* dict and user found same word */
377 if (both == 0) {
378 both = 1;
379 printf("\t\t\t we both found:\n");
380 }
381 outword(word[d]);
382 word[d++] = NULL;
383 word[u++] = NULL;
384 } else if (c < 0) {
385 /* dict found it, user didn't */
386 donly = 1;
387 d++;
388 } else {
389 /* user found it, dict didn't */
390 uonly = 1;
391 u++;
392 }
393 }
394 endline();
395 if (donly) {
396 printf("\n\t\t\tI alone found these:\n");
397 for (d=0; d<ubotch; d++)
398 if (word[d] != NULL)
399 outword(word[d]);
400 endline();
401 }
402 if (uonly) {
403 printf("\n\t\t\tyou alone found these:\n");
404 for (u=ustart; u<wcount; u++)
405 if (word[u] != NULL)
406 outword(word[u]);
407 endline();
408 }
409 if (ubotch < ustart) {
410 printf("\n\t\t\t you botched these:\n");
411 for (u=ubotch; u<ustart; u++)
412 outword(word[u]);
413 endline();
414 }
415}
416
417numways (leaf, last)
418register struct frame *leaf;
419struct frame *last;
420{
421 int count;
422 register char *p;
423 register struct frame *node;
424 if (super > 0)
425 return(last-leaf);
426 count = 0;
427 present[BSIZE] = 1;
428 while (leaf < last) {
429 for (p = &present[0]; p < &present[BSIZE]; *p++ = 0);
430 for (node = leaf; present[node->place]++ == 0; node = node->parent);
431 if (node == &stack[0])
432 count++;
433 leaf++;
434 }
435 return(count);
436}
437
438evalboard (getword, putword)
439int (*getword)(), (*putword)();
440{
441 register struct frame *top;
442 register int l, q;
443 int fo, found;
444 struct frame *parent, *lastparent;
445 char *padj;
446
447 numsame = found = 0;
448 makelists ();
449
450 while (1) {
451 l = numsame;
452 if (!(*getword) ())
453 break;
454 top = level[l+1];
455
456 while (1) {
457 level[l+1] = lastparent = top;
458 /* wbuff[1]...wbuff[l] have been matched */
459 /* level[0],...,level[l] of tree built */
460 if (l == wlength) {
461 if (wlength >= 3 && (q = numways(level[l], top)) != 0) {
462 (*putword) (q);
463 found++;
464 }
465 l = BSIZE+1;
466 break;
467 }
468 if ((fo = occurs[wbuff[++l]-'a']) == BSIZE)
469 break;
470 /* wbuff[1]...wbuff[l-1] have been matched */
471 /* level[0],...,level[l-1] of tree built */
472 for (parent=level[l-1]; parent<lastparent; parent++) {
473 padj = &adj[parent->place][0];
474 for (q=fo; q!=BSIZE; q=olink[q])
475 if (padj[q]) {
476 top->parent = parent;
477 top->place = q;
478 if (++top >= &stack[SSIZE]) {
479 printf("stack overflow\n");
480 goodbye(1);
481 }
482 }
483 }
484 /* were any nodes added? */
485 if (top == lastparent)
486 break;
487 }
488
489 /* advance until first l characters of next word are different */
490 while (numsame >= l && (*getword)()) ;
491 }
492 return(found);
493}
494
495main (argc, argv)
496int argc;
497char **argv;
498{
499 char *q;
500 register char *p;
501 register int i, c;
502
503 gtty (fileno(stdin), &origttyb);
504 setbuf(stdin, NULL);
505 tempttyb = origttyb;
506 if (setjmp(env) != 0)
507 goodbye(0);
508 signal (SIGINT, interrupt);
509 timein = time(0L);
510 if (argv[0][0] != 'a' && (logfile = open("/usr/games/boglog", 1)) >= 0) {
511 p = &logbuff[5];
512 q = getlogin();
513 while (*p++ = *q++);
514 p[-1] = '\t';
515 q = ctime(&timein);
516 while (*p++ = *q++);
517 logloc = lseek(logfile, 0L, 2);
518 write(logfile, &logbuff[0], p-&logbuff[1]);
519 }
520 if ((dict = fopen(dictname, "r")) == NULL) {
521 printf("can't open %s\n", dictname);
522 goodbye (2);
523 }
524 while ( argc > 1 && ( argv[1][0] == '+' || argv[1][0] == '-' ) ) {
525 if (argv[1][0]=='+') {
526 while(*(argv[1]++) == '+')
527 super++;
528 argc--;
529 argv++;
530 }
531 if ( argv[1][0] == '-' ) {
532 timeint[0] = 60 * ( atol( &argv[1][1] ) - 2 );
533 if ( timeint[0] <= 0 ) {
534 timeint[0] = 60;
535 }
536 argc--;
537 argv++;
538 }
539 }
540 setup ();
541 switch (argc) {
542 default: punt:
543 printf("usage: boggle [+[+]] [row1 row2 row3 row4]\n");
544 goodbye (3);
545 case 5:
546 for (i=0; i<BSIZE; i++) {
547 board[i] = c = argv[row(i)+1][col(i)];
548 if (!islower(c)) {
549 printf("bad board\n");
550 goto punt;
551 }
552 }
553 printboard();
554 column = 0;
555 evalboard(getdword, tputword);
556 endline();
557 if (logfile >= 0) {
558 strncpy(&logbuff[0], "eval", 4);
559 lseek(logfile, logloc, 0);
560 write(logfile, &logbuff[0], 4);
561 }
562 goodbye(0);
563 case 1:
564 tempttyb.sg_flags |= CBREAK;
565 if ( ioctl( fileno(stdin), TIOCLGET, &ctlecho ) == 0 ) {
566 if ( ctlecho & LCTLECH ) {
567 ioctl( fileno(stdin), TIOCLBIC, &lctlech );
568 }
569 }
570 printinst();
571 srand((int) timein);
572 while (setjmp(env) == 0) {
573 errno = 0;
574 if (pipe(&pipefd[0]) != 0) {
575 printf("can't create pipe\n");
576 goodbye(4);
577 }
578 genboard();
579 delct = wcount = 0;
580 word[0] = freesp = &space[0];
581 if ((master = fork()) == 0) {
582 close(pipefd[0]);
583 clearscreen();
584 printboard();
585 signal(SIGALRM, timeout);
586 timept = &timeint[0];
587 alarm(*timept++);
588 evalboard(getuword, aputuword);
589 clearscreen();
590 qsort(&word[0], wcount, sizeof (int), compare);
591 for (i=0; i<wcount; i++)
592 if (i==0 || wordcomp(word[i], word[i-1])!=0) {
593 p = word[i];
594 while (isalpha(*++p)) ;
595 write (pipefd[1], word[i], p-word[i]);
596 }
597 close(pipefd[1]);
598 goodbye(0);
599 }
600 close(pipefd[1]);
601 rewind(dict);
602 getc(dict);
603 evalboard(getdword, aputword);
604 p = freesp;
605 while ((i = read(pipefd[0], freesp, 512)) != 0) {
606 if (i < 0)
607 if (errno != EINTR)
608 break;
609 else
610 i = 0;
611 freesp += i;
612 }
613 close(pipefd[0]);
614 ustart = ubotch = wcount;
615 while (p < freesp) {
616 word[wcount++] = p;
617 if (*p == '0')
618 ustart = wcount;
619 while (isalpha(*++p));
620 }
621 wait(&status);
622 if (status != 0)
623 goodbye (5);
624 delct = 1;
625 printdiff();
626 printboard();
627 games++;
628 if (logfile >= 0) {
629 sprintf(&logbuff[0], "%4d", games);
630 lseek(logfile, logloc, 0);
631 write(logfile, &logbuff[0], 4);
632 }
633 stty (fileno(stdin), &tempttyb);
634 printf("\nanother game?");
635 if (getchar() != 'y') {
636 putchar('\n');
637 break;
638 }
639 stty (fileno(stdin), &tempttyb);
640 }
641 goodbye(0);
642 }
643}