|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T b
Length: 13598 (0x351e) Types: TextFile Names: »bog.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Bog/bog.c«
/* vi: set tabstop=4 : */ #include "bog.h" #include <ctype.h> #include <stdio.h> char *version[] = "bog V1.0 brachman@ubc.csnet 23-Feb-88"; struct dictindex dictindex[26]; /* * Cube position numbering: * * 0 1 2 3 * 4 5 6 7 * 8 9 A B * C D E F */ static int adjacency[16][16] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ { 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 0 */ { 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 1 */ { 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 2 */ { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 3 */ { 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, /* 4 */ { 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, /* 5 */ { 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 }, /* 6 */ { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, /* 7 */ { 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0 }, /* 8 */ { 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0 }, /* 9 */ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1 }, /* A */ { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1 }, /* B */ { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 }, /* C */ { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0 }, /* D */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1 }, /* E */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 } /* F */ }; static int letter_map[26][16]; char board[17]; int wordpath[MAXWORDLEN + 1]; int wordlen; /* Length of last word returned by nextword() */ int usedbits; char *pword[MAXPWORDS], pwords[MAXPSPACE], *pwordsp; int npwords; char *mword[MAXMWORDS], mwords[MAXMSPACE], *mwordsp; int nmwords; int ngames = 0; int tnmwords = 0, tnpwords = 0; #ifdef TIMER #include <setjmp.h> jmp_buf env; #endif TIMER long start_t; static FILE *dictfp = (FILE *) NULL; int batch; int debug; int minlength; int reuse; int tlimit; char *batchword(), *getline(); main(argc, argv) int argc; char **argv; { int done, i, selfuse; char *bspec, *p; long t; long atol(); FILE *opendict(); debug = 0; bspec = (char *) NULL; reuse = 0; batch = 0; selfuse = 0; minlength = 3; tlimit = 180; /* 3 minutes is standard */ time(&t); for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'b': batch = 1; break; case 'd': debug = 1; break; case 's': t = atol(&argv[i][2]); break; case 't': if ((tlimit = atoi(&argv[i][2])) < 1) { (void) fprintf(stderr, "Bad time limit\n"); exit(1); } break; case 'w': if ((minlength = atoi(&argv[i][2])) < 3) { (void) fprintf(stderr, "Min word length must be > 2\n"); exit(1); } break; default: usage(); /*NOTREACHED*/ } } else if (strcmp(argv[i], "+") == 0) reuse = 1; else if (strcmp(argv[i], "++") == 0) selfuse = 1; else if (islower(argv[i][0])) { if (strlen(argv[i]) != 16) { usage(); /*NOTREACHED*/ } /* This board is assumed to be valid... */ bspec = argv[i]; } else { usage(); /*NOREACHED*/ } } if (batch && bspec == (char *) NULL) { (void) fprintf(stderr, "Must give both -b and a board setup\n"); exit(1); } if (selfuse) { for (i = 0; i < 16; i++) adjacency[i][i] = 1; } if (batch) { newgame(bspec); while ((p = batchword(stdin)) != (char *) NULL) (void) printf("%s\n", p); } else { if (debug) (void) printf("seed = %ld\n", t); setup(); prompt("Loading the dictionary..."); if ((dictfp = opendict(DICT)) == (FILE *) NULL) { (void) fprintf(stderr, "Can't load %s\n", DICT); cleanup(); exit(1); } #ifdef LOADDICT if (loaddict(dictfp) < 0) { (void) fprintf(stderr, "Can't load %s\n", DICT); cleanup(); exit(1); } (void) fclose(dictfp); dictfp = (FILE *) NULL; #endif if (loadindex(DICTINDEX) < 0) { (void) fprintf(stderr, "Can't load %s\n", DICTINDEX); cleanup(); exit(1); } srandom(t); prompt("Type <space> to begin..."); while (inputch() != ' ') ; done = 0; while (!done) { newgame(bspec); bspec = (char *) NULL; /* reset for subsequent games */ playgame(); prompt("Type <space> to continue, any cap to quit..."); flushin(stdin); while (1) { int ch; ch = inputch(); if (ch == '\033') findword(); else { if (isupper(ch)) { done = 1; break; } if (ch == ' ') break; } } } cleanup(); } exit(0); } /* * Read a line from the given stream and check if it is legal * Return a pointer to a legal word or a null pointer when EOF is reached */ char * batchword(fp) FILE *fp; { register int *p, *q; register char *w; char *nextword(); q = &wordpath[MAXWORDLEN + 1]; p = wordpath; while (p < q) *p++ = -1; while ((w = nextword(fp)) != (char *) NULL) { if (wordlen < minlength) continue; p = wordpath; while (p < q && *p != -1) *p++ = -1; usedbits = 0; if (checkword(w, -1, wordpath) != -1) return(w); } return((char *) NULL); } /* * Play a single game * Reset the word lists from last game * Keep track of the running stats */ playgame() { /* Can't use register variables if setjmp() is used! */ int i, *p, *q; long t; char buf[MAXWORDLEN + 1]; int compar(); ngames++; npwords = 0; pwordsp = pwords; nmwords = 0; mwordsp = mwords; time(&start_t); q = &wordpath[MAXWORDLEN + 1]; p = wordpath; while (p < q) *p++ = -1; showboard(board); startwords(); if (setjmp(env)) { badword(); goto timesup; } while (1) { if (getline(buf) == (char *) NULL) { if (feof(stdin)) clearerr(stdin); break; } time(&t); if (t - start_t >= tlimit) { badword(); break; } if (buf[0] == '\0') { int remaining; remaining = tlimit - (int) (t - start_t); (void) sprintf(buf, "%d:%02d", remaining / 60, remaining % 60); showstr(buf, 1); continue; } if (strlen(buf) < minlength) { badword(); continue; } p = wordpath; while (p < q && *p != -1) *p++ = -1; usedbits = 0; if (checkword(buf, -1, wordpath) < 0) badword(); else { if (debug) { (void) printf("["); for (i = 0; wordpath[i] != -1; i++) (void) printf(" %d", wordpath[i]); (void) printf(" ]\n"); } for (i = 0; i < npwords; i++) { if (strcmp(pword[i], buf) == 0) break; } if (i != npwords) { /* already used the word */ badword(); showword(i); } else if (!validword(buf)) badword(); else { int len; len = strlen(buf) + 1; if (npwords == MAXPWORDS - 1 || pwordsp + len >= &pwords[MAXPSPACE]) { (void) fprintf(stderr, "Too many words!\n"); cleanup(); exit(1); } pword[npwords++] = pwordsp; (void) strcpy(pwordsp, buf); pwordsp += len; addword(buf); } } } timesup: ; /* * Sort the player's words and terminate the list with a null * entry to help out checkdict() */ qsort(pword, npwords, sizeof(pword[0]), compar); pword[npwords] = (char *) NULL; /* * These words don't need to be sorted since the dictionary is sorted */ checkdict(); tnmwords += nmwords; tnpwords += npwords; results(); } /* * Check if the given word is present on the board, with the constraint * that the first letter of the word is adjacent to square 'prev' * Keep track of the current path of squares for the word * A 'q' must be followed by a 'u' * Words must end with a null * Return 1 on success, -1 on failure */ checkword(word, prev, path) char *word; int prev, *path; { register char *p, *q; register int i, *lm; if (debug) { (void) printf("checkword(%s, %d, [", word, prev); for (i = 0; wordpath[i] != -1; i++) (void) printf(" %d", wordpath[i]); (void) printf(" ]\n"); } if (*word == '\0') return(1); lm = letter_map[*word - 'a']; if (prev == -1) { char subword[MAXWORDLEN + 1]; /* * Check for letters not appearing in the cube to eliminate some * recursive calls * Fold 'qu' into 'q' */ p = word; q = subword; while (*p != '\0') { if (*letter_map[*p - 'a'] == -1) return(-1); *q++ = *p; if (*p++ == 'q') { if (*p++ != 'u') return(-1); } } *q = '\0'; while (*lm != -1) { *path = *lm; usedbits |= (1 << *lm); if (checkword(subword + 1, *lm, path + 1) > 0) return(1); usedbits &= ~(1 << *lm); lm++; } return(-1); } /* * A cube is only adjacent to itself in the adjacency matrix if selfuse * was set, so a cube can't be used twice in succession if only the reuse * flag is set */ for (i = 0; lm[i] != -1; i++) { if (adjacency[prev][lm[i]]) { int used; used = 1 << lm[i]; /* If necessary, check if the square has already been used */ if (!reuse && (usedbits & used)) continue; *path = lm[i]; usedbits |= used; if (checkword(word + 1, lm[i], path + 1) > 0) return(1); usedbits &= ~used; } } *path = -1; /* in case of a backtrack */ return(-1); } /* * A word is invalid if it is not in the dictionary * At this point it is already known that the word can be formed from * the current board */ validword(word) char *word; { register int j; register char *q, *w; char *nextword(); j = word[0] - 'a'; if (dictseek(dictfp, dictindex[j].start, 0) < 0) { (void) fprintf(stderr, "Seek error\n"); cleanup(); exit(1); } while ((w = nextword(dictfp)) != (char *) NULL) { int ch; if (*w != word[0]) /* end of words starting with word[0] */ break; q = word; while ((ch = *w++) == *q++ && ch != '\0') ; if (*(w - 1) == '\0' && *(q - 1) == '\0') return(1); } if (dictfp != (FILE *) NULL && feof(dictfp)) /* Special case for z's */ clearerr(dictfp); return(0); } /* * Check each word in the dictionary against the board * Delete words from the machine list that the player has found * Assume both the dictionary and the player's words are already sorted */ checkdict() { register char *p, **pw, *w; register int i; int prevch, previndex, *pi, *qi, st; mwordsp = mwords; nmwords = 0; pw = pword; prevch ='a'; qi = &wordpath[MAXWORDLEN + 1]; (void) dictseek(dictfp, 0L, 0); while ((w = nextword(dictfp)) != (char *) NULL) { if (wordlen < minlength) continue; if (*w != prevch) { /* * If we've moved on to a word with a different first letter * then we can speed things up by skipping all words starting * with a letter that doesn't appear in the cube */ i = (int) (*w - 'a'); while (i < 26 && letter_map[i][0] == -1) i++; if (i == 26) break; previndex = prevch - 'a'; prevch = i + 'a'; /* * Fall through if the word's first letter appears in the cube * (i.e., if we can't skip ahead), otherwise seek to the * beginning of words in the dictionary starting with the * next letter (alphabetically) appearing in the cube and then * read the first word */ if (i != previndex + 1) { if (dictseek(dictfp, dictindex[i].start, 0) < 0) { (void) fprintf(stderr, "Seek error in checkdict()\n"); cleanup(); exit(1); } continue; } } pi = wordpath; while (pi < qi && *pi != -1) *pi++ = -1; usedbits = 0; if (checkword(w, -1, wordpath) == -1) continue; st = 1; while (*pw != (char *) NULL && (st = strcmp(*pw, w)) < 0) pw++; if (st == 0) /* found it */ continue; if (nmwords == MAXMWORDS || mwordsp + wordlen + 1 >= &mwords[MAXMSPACE]) { (void) fprintf(stderr, "Too many words!\n"); cleanup(); exit(1); } mword[nmwords++] = mwordsp; p = w; while (*mwordsp++ = *p++) ; } } /* * Crank up a new game * If the argument is non-null then it is assumed to be a legal board spec * in ascending cube order, oth. make a random board */ newgame(b) char *b; { register int i, p, q; char *tmp; int *lm[26]; long random(); static char *cubes[16] = { "ednosw", "aaciot", "acelrs", "ehinps", "eefhiy", "elpstu", "acdemp", "gilruw", "egkluy", "ahmors", "abilty", "adenvz", "bfiorx", "dknotu", "abjmoq", "egintv" }; if (b == (char *) NULL) { /* * Shake the cubes and make the board */ i = 0; while (i < 100) { p = (int) (random() % 16); q = (int) (random() % 16); if (p != q) { tmp = cubes[p]; cubes[p] = cubes[q]; cubes[q] = tmp; i++; } /* else try again */ } for (i = 0; i < 16; i++) board[i] = cubes[i][random() % 6]; } else { for (i = 0; i < 16; i++) board[i] = b[i]; } board[16] = '\0'; /* * Set up the map from letter to location(s) * Each list is terminated by a -1 entry */ for (i = 0; i < 26; i++) { lm[i] = letter_map[i]; *lm[i] = -1; } for (i = 0; i < 16; i++) { register int j; j = (int) (board[i] - 'a'); *lm[j] = i; *(++lm[j]) = -1; } if (debug) { for (i = 0; i < 26; i++) { int ch, j; (void) printf("%c:", 'a' + i); for (j = 0; (ch = letter_map[i][j]) != -1; j++) (void) printf(" %d", ch); (void) printf("\n"); } } } compar(p, q) char **p, **q; { return(strcmp(*p, *q)); } usage() { (void) fprintf(stderr, "Usage: bog [-b] [-d] [-s#] [-t#] [-w#] [+[+]] [boardspec]\n"); (void) fprintf(stderr, "-b: 'batch mode' (boardspec must be present)\n"); (void) fprintf(stderr, "-d: debug\n"); (void) fprintf(stderr, "-s#: use # as the random number seed\n"); (void) fprintf(stderr, "-t#: time limit is # seconds\n"); (void) fprintf(stderr, "-w#: minimum word length is # letters\n"); (void) fprintf(stderr, "+: can reuse a cube, but not twice in succession\n"); (void) fprintf(stderr, "++: can reuse cubes arbitrarily\n"); (void) fprintf(stderr, "boardspec: the first board to use (use 'q' for 'qu')\n"); exit(1); }