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