Bring in OpenBSD's boggle(6).
authorzrj <rimvydas.jasinskas@gmail.com>
Thu, 16 Nov 2017 12:27:33 +0000 (14:27 +0200)
committerzrj <zrj@dragonflybsd.org>
Thu, 16 Nov 2017 14:59:00 +0000 (16:59 +0200)
As per README, stuff is addictive!

Pointed-by: Thierry
Taken-from: OpenBSD

21 files changed:
etc/mtree/BSD.usr.dist
games/Makefile
games/boggle/Makefile [new file with mode: 0644]
games/boggle/Makefile.inc [new file with mode: 0644]
games/boggle/README [new file with mode: 0644]
games/boggle/boggle/Makefile [new file with mode: 0644]
games/boggle/boggle/bog.c [new file with mode: 0644]
games/boggle/boggle/bog.h [new file with mode: 0644]
games/boggle/boggle/boggle.6 [new file with mode: 0644]
games/boggle/boggle/extern.h [new file with mode: 0644]
games/boggle/boggle/help.c [new file with mode: 0644]
games/boggle/boggle/helpfile [new file with mode: 0644]
games/boggle/boggle/mach.c [new file with mode: 0644]
games/boggle/boggle/prtable.c [new file with mode: 0644]
games/boggle/boggle/timer.c [new file with mode: 0644]
games/boggle/boggle/word.c [new file with mode: 0644]
games/boggle/dictfiles/Makefile [new file with mode: 0644]
games/boggle/mkdict/Makefile [new file with mode: 0644]
games/boggle/mkdict/mkdict.c [new file with mode: 0644]
games/boggle/mkindex/Makefile [new file with mode: 0644]
games/boggle/mkindex/mkindex.c [new file with mode: 0644]

index f079ece..3f8f1fe 100644 (file)
         games
             atc
             ..
+            boggle
+            ..
             fortune
             ..
             larn
index b173d28..aff7947 100644 (file)
@@ -9,6 +9,7 @@ SUBDIR= adventure \
        battlestar \
        bcd \
        bs \
+       boggle \
        caesar \
        canfield \
        cribbage \
diff --git a/games/boggle/Makefile b/games/boggle/Makefile
new file mode 100644 (file)
index 0000000..f4b3fd3
--- /dev/null
@@ -0,0 +1,10 @@
+#      $OpenBSD: Makefile,v 1.4 2003/04/10 22:42:29 millert Exp $
+
+SUBDIR=                boggle
+
+# Only if building the dictionary
+.if !defined(NO_SHARE)
+SUBDIR+=       mkdict mkindex dictfiles
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/games/boggle/Makefile.inc b/games/boggle/Makefile.inc
new file mode 100644 (file)
index 0000000..dc241da
--- /dev/null
@@ -0,0 +1,3 @@
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
diff --git a/games/boggle/README b/games/boggle/README
new file mode 100644 (file)
index 0000000..9d9d5ae
--- /dev/null
@@ -0,0 +1,56 @@
+$OpenBSD: README,v 1.4 2000/04/21 03:10:30 pjanzen Exp $
+
+Bog is a fairly portable simulation of Parker Brother's game of Boggle and
+is similar to the 4.[23] BSD "boggle" and Sun's "boggletool".
+Bog has not been derived from any proprietary code.
+It has been tested on the Sun 3 under SunOS 3.2 and on the Atari 1040ST (MWC).
+
+What You Need
+
+You will need curses and a large word list.
+The minix word list or /usr/dict/words will do nicely.
+The word list must already be sorted (you can use "sort -c" to check).
+
+Contents
+
+       README          - this file
+       Makefile
+       bog.man         - half-hearted man page (use the game's help command)
+       bog.h           - configuration and header info
+       bog.c           - machine independent game code
+       word.c          - machine independent word list routines
+       help.c          - (curses) help routine
+       mach.c          - (curses) display code
+       prtable.c       - ditto
+       timer.c         - machine dependent (os) input polling
+       mkdict.c        - convert a word list to a bog dictionary
+       mkindex.c       - create an index file for the bog dictionary
+       showdict.c      - print a bog dictionary to stdout
+
+Portability
+
+- I've tried to make bog.c (the program logic) independent of the I/O.
+  My plan was to make it straightforward to adapt the game to run under a
+  windowing system (eg., Suntools, GEM).  I have no plan to actually do this.
+  I've stuck to a small subset of the curses routines.
+- The program runs with the input in raw mode.
+- If you want the running timer you must #define TIMER in bog.h
+  and insert the input polling code in timer.c for your system.  There is
+  already code there for BSD, SYSV, and ATARI.
+
+Setup
+
+1. Check bog.h and Makefile and edit to fit your environment
+2. "make all"
+   This will make all the binaries and create the dictionary and index files
+3. Move "dict", "dict.ind", and "helpfile" to where you specified in bog.h
+4. Play away
+
+Enjoy. [But beware: boggle can be addictive!]
+
+-----
+Barry Brachman           | UUCP:    {alberta,uw-beaver,uunet}!
+Dept. of Computer Science|           ubc-vision!ubc-csgrads!brachman
+Univ. of British Columbia| Internet: brachman@cs.ubc.ca
+Vancouver, B.C. V6T 1W5  |           brachman%ubc.csnet@csnet-relay.arpa
+(604) 228-5010           | brachman@ubc.csnet
diff --git a/games/boggle/boggle/Makefile b/games/boggle/boggle/Makefile
new file mode 100644 (file)
index 0000000..a5903c5
--- /dev/null
@@ -0,0 +1,15 @@
+#      $OpenBSD: Makefile,v 1.5 2002/05/23 18:43:00 deraadt Exp $
+#      $NetBSD: Makefile,v 1.3 1995/03/21 12:14:28 cgd Exp $
+#      @(#)Makefile    8.1 (Berkeley) 6/11/93
+
+PROG=  boggle
+SRCS=  bog.c help.c mach.c prtable.c timer.c word.c
+MAN=   boggle.6
+
+DPADD= ${LIBNCURSES}
+LDADD= -lprivate_ncurses
+
+CFLAGS+=       -I${_SHLIBDIRPREFIX}/usr/include/priv/ncurses
+LDFLAGS+=      ${PRIVATELIB_LDFLAGS}
+
+.include <bsd.prog.mk>
diff --git a/games/boggle/boggle/bog.c b/games/boggle/boggle/bog.c
new file mode 100644 (file)
index 0000000..33a4578
--- /dev/null
@@ -0,0 +1,758 @@
+/*     $OpenBSD: bog.c,v 1.33 2016/09/12 20:11:10 tb Exp $     */
+/*     $NetBSD: bog.c,v 1.5 1995/04/24 12:22:32 cgd Exp $      */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "bog.h"
+#include "extern.h"
+
+static void init(void);
+static void init_adjacencies(void);
+static int compar(const void *, const void *);
+
+struct dictindex dictindex[26];
+
+static int **adjacency, **letter_map;
+
+char *board;
+int wordpath[MAXWORDLEN + 1];
+int wordlen;           /* Length of last word returned by nextword() */
+int usedbits;
+unsigned int ncubes;
+int grid = 4;
+
+char **pword, *pwords, *pwordsp;
+int npwords, maxpwords = MAXPWORDS, maxpspace = MAXPSPACE;
+
+char **mword, *mwords, *mwordsp;
+int nmwords, maxmwords = MAXMWORDS, maxmspace = MAXMSPACE;
+
+int ngames = 0;
+int tnmwords = 0, tnpwords = 0;
+
+jmp_buf env;
+
+time_t start_t;
+
+static FILE *dictfp;
+
+int batch;
+int challenge;
+int debug;
+int minlength;
+int reuse;
+int selfuse;
+int tlimit;
+
+int
+main(int argc, char *argv[])
+{
+       int ch, done;
+       char *bspec, *p;
+
+       batch = debug = reuse = selfuse;
+       bspec = NULL;
+       minlength = -1;
+       tlimit = 180;           /* 3 minutes is standard */
+
+       while ((ch = getopt(argc, argv, "Bbcdht:w:")) != -1)
+               switch(ch) {
+               case 'B':
+                       grid = 5;
+                       break;
+               case 'b':
+                       batch = 1;
+                       break;
+               case 'c':
+                       challenge = 1;
+                       break;
+               case 'd':
+                       debug = 1;
+                       break;
+               case 't':
+                       if ((tlimit = atoi(optarg)) < 1)
+                               errx(1, "bad time limit");
+                       break;
+               case 'w':
+                       if ((minlength = atoi(optarg)) < 3)
+                               errx(1, "min word length must be > 2");
+                       break;
+               case 'h':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       ncubes = grid * grid;
+
+       /* process final arguments */
+       if (argc > 0) {
+               if (strcmp(argv[0], "+") == 0)
+                       reuse = 1;
+               else if (strcmp(argv[0], "++") == 0)
+                       selfuse = 1;
+       }
+
+       if (reuse || selfuse) {
+               argc -= 1;
+               argv += 1;
+       }
+
+       if (argc == 1) {
+               if (strlen(argv[0]) != ncubes)
+                       usage();
+               for (p = argv[0]; *p != '\0'; p++)
+                       if (!islower((unsigned char)*p))
+                               errx(1, "only lower case letters are allowed "
+                                   "in boardspec");
+               bspec = argv[0];
+       } else if (argc != 0)
+               usage();
+
+       if (batch && bspec == NULL)
+               errx(1, "must give both -b and a board setup");
+
+       init();
+       if (batch) {
+               newgame(bspec);
+               while ((p = batchword(stdin)) != NULL)
+                       printf("%s\n", p);
+               return 0;
+       }
+       setup();
+       prompt("Loading the dictionary...");
+       if ((dictfp = opendict(DICT)) == NULL) {
+               warn("%s", DICT);
+               cleanup();
+               return 1;
+       }
+#ifdef LOADDICT
+       if (loaddict(dictfp) < 0) {
+               warnx("can't load %s", DICT);
+               cleanup();
+               return 1;
+       }
+       fclose(dictfp);
+       dictfp = NULL;
+#endif
+       if (loadindex(DICTINDEX) < 0) {
+               warnx("can't load %s", DICTINDEX);
+               cleanup();
+               return 1;
+       }
+
+       prompt("Type <space> to begin...");
+       while (inputch() != ' ');
+
+       for (done = 0; !done;) {
+               newgame(bspec);
+               bspec = NULL;   /* reset for subsequent games */
+               playgame();
+               prompt("Type <space> to continue, any cap to quit...");
+               delay(10);      /* wait for user to quit typing */
+               flushin(stdin);
+               for (;;) {
+                       ch = inputch();
+                       if (ch == '\033')
+                               findword();
+                       else if (ch == '\014' || ch == '\022')  /* ^l or ^r */
+                               redraw();
+                       else {
+                               if (isupper(ch)) {
+                                       done = 1;
+                                       break;
+                               }
+                               if (ch == ' ')
+                                       break;
+                       }
+               }
+       }
+       cleanup();
+       return 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(FILE *fp)
+{
+       int *p, *q;
+       char *w;
+
+       q = &wordpath[MAXWORDLEN + 1];
+       p = wordpath;
+       while (p < q)
+               *p++ = -1;
+       while ((w = nextword(fp)) != 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 (NULL);
+}
+
+/*
+ * Play a single game
+ * Reset the word lists from last game
+ * Keep track of the running stats
+ */
+void
+playgame(void)
+{
+       int i, *p, *q;
+       time_t t;
+       char buf[MAXWORDLEN + 1];
+
+       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 (get_line(buf) == 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);
+                       snprintf(buf, sizeof(buf),
+                           "%d:%02d", remaining / 60, remaining % 60);
+                       showstr(buf, 1);
+                       continue;
+               }
+               if (strlen(buf) < (size_t)minlength) {
+                       badword();
+                       continue;
+               }
+
+               p = wordpath;
+               while (p < q && *p != -1)
+                       *p++ = -1;
+               usedbits = 0;
+
+               if (checkword(buf, -1, wordpath) < 0)
+                       badword();
+               else {
+                       if (debug) {
+                               printf("[");
+                               for (i = 0; wordpath[i] != -1; i++)
+                                       printf(" %d", wordpath[i]);
+                               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;
+
+                               if (npwords == maxpwords - 1) {
+                                       maxpwords += MAXPWORDS;
+                                       pword = realloc(pword,
+                                                   maxpwords * sizeof(char *));
+                                       if (pword == NULL) {
+                                               cleanup();
+                                               errx(1, "%s", strerror(ENOMEM));
+                                       }
+                               }
+                               len = strlen(buf) + 1;
+                               if (pwordsp + len >= &pwords[maxpspace]) {
+                                       maxpspace += MAXPSPACE;
+                                       pwords = realloc(pwords, maxpspace);
+                                       if (pwords == NULL) {
+                                               cleanup();
+                                               errx(1, "%s", strerror(ENOMEM));
+                                       }
+                               }
+                               pword[npwords++] = pwordsp;
+                               memcpy(pwordsp, buf, len);
+                               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] = 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
+ */
+int
+checkword(char *word, int prev, int *path)
+{
+       char *p, *q;
+       int i, *lm;
+
+       if (debug) {
+               printf("checkword(%s, %d, [", word, prev);
+                       for (i = 0; wordpath[i] != -1; i++)
+                               printf(" %d", wordpath[i]);
+                       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 && !selfuse && (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
+ */
+int
+validword(char *word)
+{
+       int j;
+       char *q, *w;
+
+       j = word[0] - 'a';
+       if (dictseek(dictfp, dictindex[j].start, SEEK_SET) < 0) {
+               cleanup();
+               errx(1, "seek error in validword()");
+       }
+
+       while ((w = nextword(dictfp)) != 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 != 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
+ */
+void
+checkdict(void)
+{
+       char **pw, *w;
+       int i;
+       int prevch, previndex, *pi, *qi, st;
+
+       mwordsp = mwords;
+       nmwords = 0;
+       pw = pword;
+       prevch ='a';
+       qi = &wordpath[MAXWORDLEN + 1];
+
+       dictseek(dictfp, 0L, SEEK_SET);
+       while ((w = nextword(dictfp)) != 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, SEEK_SET) < 0) {
+                                       cleanup();
+                                       errx(1, "seek error in checkdict()");
+                               }
+                               continue;
+                       }
+               }
+
+               pi = wordpath;
+               while (pi < qi && *pi != -1)
+                       *pi++ = -1;
+               usedbits = 0;
+               if (checkword(w, -1, wordpath) == -1)
+                       continue;
+
+               st = 1;
+               while (*pw != NULL && (st = strcmp(*pw, w)) < 0)
+                       pw++;
+               if (st == 0)                    /* found it */
+                       continue;
+               if (nmwords == maxmwords - 1) {
+                       maxmwords += MAXMWORDS;
+                       mword = realloc(mword, maxmwords * sizeof(char *));
+                       if (mword == NULL) {
+                               cleanup();
+                               errx(1, "%s", strerror(ENOMEM));
+                       }
+               }
+               if (mwordsp + wordlen + 1 >= &mwords[maxmspace]) {
+                       maxmspace += MAXMSPACE;
+                       mwords = realloc(mwords, maxmspace);
+                       if (mwords == NULL) {
+                               cleanup();
+                               errx(1, "%s", strerror(ENOMEM));
+                       }
+               }
+               mword[nmwords++] = mwordsp;
+               memcpy(mwordsp, w, wordlen + 1);
+               mwordsp += wordlen + 1;
+       }
+}
+
+/*
+ * 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
+ */
+void
+newgame(char *b)
+{
+       unsigned int i;
+       int p, q;
+       const char *tmp, **cubes;
+       int *lm[26];
+       const char chal_cube[] = "iklmqu";      /* challenge cube */
+       static const char *cubes4[] = {
+               "ednosw", "aaciot", "acelrs", "ehinps",
+               "eefhiy", "elpstu", "acdemp", "gilruw",
+               "egkluy", "ahmors", "abilty", "adenvz",
+               "bfiorx", "dknotu", "abjmoq", "egintv"
+       };
+       static const char *cubes5[] = {
+               "aaafrs", "aaeeee", "aafirs", "adennn", "aeeeem",
+               "aeegmu", "aegmnn", "afirsy", "bjkqxz", "ccnstw",
+               "ceiilt", "ceilpt", "ceipst", "ddlnor", "dhhlor",
+               "dhhnot", "dhlnor", "eiiitt", "emottt", "ensssu",
+               "fiprsy", "gorrvw", "hiprry", "nootuw", "ooottu"
+       };
+
+       cubes = grid == 4 ? cubes4 : cubes5;
+       if (b == NULL) {
+               /* Shuffle the cubes using Fisher-Yates (aka Knuth P). */
+               p = ncubes;
+               while (--p) {
+                       q = (int)arc4random_uniform(p + 1);
+                       tmp = cubes[p];
+                       cubes[p] = cubes[q];
+                       cubes[q] = tmp;
+               }
+
+               /* Build the board by rolling each cube. */
+               for (i = 0; i < ncubes; i++)
+                       board[i] = cubes[i][arc4random_uniform(6)];
+
+               /*
+                * For challenge mode, roll chal_cube and replace a random
+                * cube with its value.  Set the high bit to distinguish it.
+                */
+               if (challenge) {
+                       i = arc4random_uniform(ncubes);
+                       board[i] = SETHI(chal_cube[arc4random_uniform(6)]);
+               }
+       } else {
+               for (i = 0; i < ncubes; i++)
+                       board[i] = b[i];
+       }
+       board[ncubes] = '\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 < ncubes; i++) {
+               int j;
+
+               j = (int) (SEVENBIT(board[i]) - 'a');
+               *lm[j] = i;
+               *(++lm[j]) = -1;
+       }
+
+       if (debug) {
+               for (i = 0; i < 26; i++) {
+                       int ch, j;
+
+                       printf("%c:", 'a' + i);
+                       for (j = 0; (ch = letter_map[i][j]) != -1; j++)
+                               printf(" %d", ch);
+                       printf("\n");
+               }
+       }
+
+}
+
+static int
+compar(const void *p, const void *q)
+{
+       return (strcmp(*(const char *const*)p, *(const char *const*)q));
+}
+
+/*
+ * Allocate and initialize data structures.
+ */
+static void
+init(void)
+{
+       int i;
+
+       if (minlength == -1)
+               minlength = grid - 1;
+       init_adjacencies();
+       board = malloc(ncubes + 1);
+       if (board == NULL)
+               err(1, NULL);
+       letter_map = calloc(26, sizeof(int *));
+       if (letter_map == NULL)
+               err(1, NULL);
+       for (i = 0; i < 26; i++) {
+               letter_map[i] = calloc(ncubes, sizeof(int));
+               if (letter_map[i] == NULL)
+                       err(1, NULL);
+       }
+       pword = calloc(maxpwords, sizeof(char *));
+       if (pword == NULL)
+               err(1, NULL);
+       pwords = malloc(maxpspace);
+       if (pwords == NULL)
+               err(1, NULL);
+       mword = calloc(maxmwords, sizeof(char *));
+       if (mword == NULL)
+               err(1, NULL);
+       mwords = malloc(maxmspace);
+       if (mwords == NULL)
+               err(1, NULL);
+}
+
+#define SET_ADJ(r) do {                                                        \
+       if (col > 0)                                                    \
+               adj[r - 1] = 1;                                         \
+       adj[r] = 1;                                                     \
+       if (col + 1 < grid)                                             \
+               adj[r + 1] = 1;                                         \
+} while(0)
+
+/*
+ * Compute adjacency matrix for the grid
+ */
+static void
+init_adjacencies(void)
+{
+       unsigned int cube;
+       int row, col, *adj;
+
+       adjacency = calloc(ncubes, sizeof(int *));
+       if (adjacency == NULL)
+               err(1, NULL);
+
+       /*
+        * Fill in adjacencies.  This is an ncubes x ncubes matrix where
+        * the position X,Y is set to 1 if cubes X and Y are adjacent.
+        */
+       for (cube = 0; cube < ncubes; cube++) {
+               adj = adjacency[cube] = calloc(ncubes, sizeof(int));
+               if (adj == NULL)
+                       err(1, NULL);
+
+               row = cube / grid;
+               col = cube % grid;
+
+               /* this row */
+               SET_ADJ(cube);
+               if (!selfuse)
+                       adj[cube] = 0;
+
+               /* prev row */
+               if (row > 0)
+                       SET_ADJ(cube - grid);
+
+               /* next row */
+               if (row + 1 < grid)
+                       SET_ADJ(cube + grid);
+       }
+}
+
+void
+usage(void)
+{
+       fprintf(stderr, "usage: "
+           "%s [-Bbcd] [-t time] [-w length] [+[+]] [boardspec]\n",
+           getprogname());
+       exit(1);
+}
diff --git a/games/boggle/boggle/bog.h b/games/boggle/boggle/bog.h
new file mode 100644 (file)
index 0000000..be5a35a
--- /dev/null
@@ -0,0 +1,72 @@
+/*     $OpenBSD: bog.h,v 1.4 2008/03/20 12:02:27 millert Exp $ */
+/*     $NetBSD: bog.h,v 1.2 1995/03/21 12:14:32 cgd Exp $      */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)bog.h       8.1 (Berkeley) 6/11/93
+ */
+
+#define        LOADDICT                1       /* Load the dictionary for speed */
+
+/*
+ * Locations for the dictionary (generated by mkdict),
+ * index (generated by mkindex), and helpfile
+ */
+#define        DICT                    "/usr/share/games/boggle/dictionary"
+#define        DICTINDEX               "/usr/share/games/boggle/dictindex"
+#define        HELPFILE                "/usr/share/games/boggle/helpfile"
+
+/*
+ * The theoretical maximum for MAXWORDLEN is ('a' - 1) == 96
+ */
+#define        MAXWORDLEN              40      /* Maximum word length */
+#define        MAXPWORDS               200     /* Maximum number of player's words */
+#define        MAXMWORDS               400     /* Maximum number of machine's words */
+#define        MAXPSPACE               2000    /* Space for player's words */
+#define        MAXMSPACE               4000    /* Space for machines's words */
+
+#define        MAXCOLS                 20
+
+#define        SEVENBIT(c)             ((c) & 0x7f)
+#define        HISET(c)                ((c) & 0x80)
+#define        SETHI(c)                ((c) | 0x80)
+
+/*
+ * Internal dictionary index
+ * Initialized from the file created by mkindex
+ */
+struct dictindex {
+       long start;
+       long length;
+};
+
+extern struct dictindex dictindex[];
diff --git a/games/boggle/boggle/boggle.6 b/games/boggle/boggle/boggle.6
new file mode 100644 (file)
index 0000000..05346cf
--- /dev/null
@@ -0,0 +1,162 @@
+.\"    $OpenBSD: boggle.6,v 1.21 2015/09/12 16:10:25 schwarze Exp $
+.\"    $NetBSD: boggle.6,v 1.2 1995/03/21 12:14:35 cgd Exp $
+.\"
+.\" Copyright (c) 1997, Jason Downs.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+.\" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" Copyright (c) 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Barry Brachman.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)boggle.6    8.1 (Berkeley) 6/11/93
+.\"
+.Dd November 16, 2017
+.Dt BOGGLE 6
+.Os
+.Sh NAME
+.Nm boggle
+.Nd word search game
+.Sh SYNOPSIS
+.Nm
+.Op Fl Bbcd
+.Op Fl t Ar time
+.Op Fl w Ar length
+.Op Cm + Ns Op Cm +
+.Op Ar boardspec
+.Sh DESCRIPTION
+The object of
+.Nm
+is to find as many words as possible on the Boggle board within the three
+minute time limit.
+A Boggle board is a four by four arrangement of Boggle cubes, each side of
+each cube displaying a letter of the alphabet or
+.Sq qu .
+Words are formed by finding a sequence of cubes (letters) that are in the
+game's dictionary.
+The (N+1)th cube in the word must be horizontally,
+vertically, or diagonally adjacent to the Nth cube.
+Cubes cannot be reused.
+Words consist solely of lower case letters and must be at least 3 letters long.
+.Pp
+Command line flags can be given to change the rules of the game:
+.Bl -tag -width boardspec
+.It Fl B
+Use an alternate five by five arrangement of Boggle cubes.
+In this mode the default minimum word length is 4 letters.
+This arrangement has been marketed over the years under the names
+.Em Big Boggle ,
+.Em Super Boggle
+and
+.Em Boggle Deluxe .
+.It Fl b
+Run
+.Nm
+in batch mode.
+A
+.Ar boardspec
+must also be given.
+The dictionary is read from stdin and a list of words appearing in
+.Ar boardspec
+is printed to stdout.
+.It Fl c
+Add a so-called
+.Em challenge cube
+to the board, which contains less commonly used letters.
+The challenge cube will be displayed in boldface on the board.
+Depending on the terminal capabilities, it may be displayed in
+underline or a different color (such as red).
+.It Fl d
+Enable debugging output.
+.It Fl t Ar time
+Change the time limit for each game from the default 3 minutes to
+.Ar time
+seconds.
+.It Fl w Ar length
+Change the minimum word length from 3 letters to
+.Ar length .
+.It Cm +
+Allow a cube to be used multiple times, but not in succession.
+.It Cm ++
+Allow a cube to be considered adjacent to itself.
+.El
+.Pp
+A starting board position,
+.Ar boardspec ,
+can be specified on the command line by
+listing the board left to right and top to bottom, in lower case.
+.Pp
+Help is available during play by typing
+.Sq \&? .
+More detailed information on the game is given there.
+.Sh FILES
+.Bl -tag -width 36n -compact
+.It Pa /usr/share/games/boggle/dictionary
+compressed dictionary
+.It Pa /usr/share/games/boggle/dictindex
+dictionary index
+.It Pa /usr/share/games/boggle/helpfile
+online help file
+.El
+.Sh AUTHORS
+Boggle is a trademark of Parker Brothers.
+.Pp
+.An Barry Brachman ,
+Dept. of Computer Science, University of British Columbia
+.Sh BUGS
+No word can contain a
+.Sq q
+that is not immediately followed by a
+.Sq u .
+.Pp
+When using the
+.Cm +
+or
+.Cm ++
+options the display of words found in the board doesn't clearly indicate
+reused cubes.
diff --git a/games/boggle/boggle/extern.h b/games/boggle/boggle/extern.h
new file mode 100644 (file)
index 0000000..e15222b
--- /dev/null
@@ -0,0 +1,91 @@
+/*     $OpenBSD: extern.h,v 1.9 2015/12/25 20:59:09 mestre Exp $       */
+/*     $NetBSD: extern.h,v 1.3 1995/04/24 12:22:37 cgd Exp $   */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)extern.h    8.1 (Berkeley) 6/11/93
+ */
+
+#include <setjmp.h>
+
+extern int tlimit;
+extern time_t start_t;
+extern jmp_buf env;
+extern int LIST_LINE, LIST_COL;
+extern int TIMER_LINE, TIMER_COL;
+
+extern char *board;
+extern int grid;
+extern int lastline;
+extern unsigned int ncubes;
+extern int ngames;
+extern int nlines;
+extern int usedbits;
+extern int wordlen;
+extern int wordpath[];
+extern char **mword, **pword;
+extern int nmwords, npwords;
+extern int tnmwords, tnpwords;
+
+void addword(char *);
+void badword(void);
+char *batchword(FILE *);
+void checkdict(void);
+int checkword(char *, int, int *);
+void cleanup(void);
+void delay(int);
+long dictseek(FILE *, long, int);
+void findword(void);
+void flushin(FILE *);
+char *get_line(char *);
+void getword(char *);
+int help(void);
+int inputch(void);
+int loaddict(FILE *);
+int loadindex(const char *);
+void newgame(char *);
+char *nextword(FILE *);
+FILE *opendict(const char *);
+void playgame(void);
+void prompt(const char *);
+void prtable(char *[], int, int, int,
+            void (*)(char *[], int), int (*)(char *[], int));
+void putstr(char *);
+void redraw(void);
+void results(void);
+int setup(void);
+void showboard(char *);
+void showstr(const char *, int);
+void showword(int);
+void starttime(void);
+void startwords(void);
+void stoptime(void);
+int timerch(void);
+__dead2 void usage(void);
+int validword(char *);
diff --git a/games/boggle/boggle/help.c b/games/boggle/boggle/help.c
new file mode 100644 (file)
index 0000000..862dd21
--- /dev/null
@@ -0,0 +1,95 @@
+/*     $OpenBSD: help.c,v 1.7 2016/01/10 14:10:38 mestre Exp $ */
+/*     $NetBSD: help.c,v 1.2 1995/03/21 12:14:38 cgd Exp $     */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <curses.h>
+
+#include "bog.h"
+#include "extern.h"
+
+int
+help(void)
+{
+       int eof, i;
+       FILE *fp;
+       WINDOW *win;
+       char buf[BUFSIZ];
+
+       if ((fp = fopen(HELPFILE, "r")) == NULL)
+               return(-1);
+       win = newwin(0, 0, 0, 0);
+       clearok(win, TRUE);
+
+       eof = 0;
+       if (ungetc(getc(fp), fp) == EOF) {
+               wprintw(win, "There doesn't seem to be any help.");
+               eof = 1;                        /* Nothing there... */
+       }
+
+       while (!eof) {
+               for (i = 0; i < nlines - 3; i++) {
+                       if (fgets(buf, sizeof(buf), fp) == NULL) {
+                               eof = 1;
+                               break;
+                       }
+                       if (buf[0] == '.' && buf[1] == '\n')
+                               break;
+                       wprintw(win, "%s", buf);
+               }
+               if (eof || ungetc(getc(fp), fp) == EOF) {
+                       eof = 1;
+                       break;
+               }
+               wmove(win, nlines - 1, 0);
+               wprintw(win,
+                   "Type <space> to continue, anything else to quit...");
+               wrefresh(win);
+               if ((inputch() & 0177) != ' ')
+                       break;
+               wclear(win);
+       }
+
+       fclose(fp);
+       if (eof) {
+               wmove(win, nlines - 1, 0);
+               wprintw(win, "Hit any key to continue...");
+               wrefresh(win);
+               inputch();
+       }
+       delwin(win);
+       clearok(stdscr, TRUE);
+       touchwin(stdscr);
+       refresh();
+       return(0);
+}
diff --git a/games/boggle/boggle/helpfile b/games/boggle/boggle/helpfile
new file mode 100644 (file)
index 0000000..355515f
--- /dev/null
@@ -0,0 +1,62 @@
+
+Commands:
+
+Enter word:                 <return> or <linefeed> or <space>
+Delete previous character:  <delete> or <backspace>
+Delete line:                <^u> or <^w>
+Redraw screen:              <^l> or <^r>
+Pause game:                 <^s>
+Resume game:                <^q> or <^s>
+Suspend game:               <^z>
+Give up on current cube:    <^d>
+Show remaining time:        <space> first thing on a line
+Show help:                  ? (Suspends timer until done)
+Exit game:                  <^c>
+
+(^u means "control u", etc.)
+
+Any time you are prompted while the board is displayed you can type:
+                           <esc>word
+to see where "word" is on the board.
+
+Usage:
+    boggle [-Bbcd] [-t time] [-w length] [+[+]] [boardspec]
+
+    -B: big boggle mode, uses a 5x5 board
+    -b: batch mode (boardspec must be present); dictionary read from stdin
+    -c: a challenge cube will be added to the board
+    -d: debug mode
+    -t time: time limit in seconds instead of default 180
+    -w length: minimum word length in letters instead of default 3
+    +: can reuse a cube, but not twice in succession
+    ++: can reuse cubes arbitrarily
+    boardspec: the first board to use (use 'q' for 'qu'); e.g.:
+          boggle nolezeebnqieegei
+.
+                            Default Rules
+
+A Boggle board is a four by four arrangement of Boggle cubes.
+You have 3 minutes to find as many words as possible in the Boggle board.
+Words are formed by finding a sequence of cubes (letters) that are in the
+game's dictionary.  The (N+1)th cube in the word must be horizontally,
+vertically, or diagonally adjacent to the Nth cube.  Cubes cannot be reused.
+Words consist solely of lower case letters and must be at least 3 letters long.
+.
+                           Options
+
+Command line flags can be given to change the rules of the game.
+The '+' flag allows a cube to be used multiple times, but not in succession.
+The '++' flag makes each cube adjacent to itself.
+The time limit can be changed from the default 3 minutes by using the
+flag '-t time' where 'time' is the duration (in seconds) of each game.
+The minimum word length can be changed from 3 letters by specifying
+'-w length' where 'length' is the minimum number of letters to use.
+.
+Copyright (c) 1988
+Barry Brachman           | UUCP:    {alberta,uw-beaver,uunet}!
+Dept. of Computer Science|           ubc-vision!ubc-csgrads!brachman
+Univ. of British Columbia| Internet: brachman@cs.ubc.ca
+Vancouver, B.C. V6T 1W5  |           brachman%ubc.csnet@csnet-relay.arpa
+(604) 228-5010           | brachman@ubc.csnet
+
+Boggle is a trademark of Parker Brothers.
diff --git a/games/boggle/boggle/mach.c b/games/boggle/boggle/mach.c
new file mode 100644 (file)
index 0000000..da18fe7
--- /dev/null
@@ -0,0 +1,701 @@
+/*     $OpenBSD: mach.c,v 1.22 2016/09/11 14:21:17 tb Exp $    */
+/*     $NetBSD: mach.c,v 1.5 1995/04/28 22:28:48 mycroft Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Terminal interface
+ *
+ * Input is raw and unechoed
+ */
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <err.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <time.h>
+
+#include "bog.h"
+#include "extern.h"
+
+static int gone;
+
+static int ccol, crow, maxw;
+static int colstarts[MAXCOLS], ncolstarts;
+static char *separator;
+int ncols, nlines, lastline;
+
+/*
+ * The following determine the screen layout
+ */
+int PROMPT_COL = 20;
+int PROMPT_LINE        = 3;
+
+int BOARD_COL  = 0;
+int BOARD_LINE = 0;
+
+int SCORE_COL  = 20;
+int SCORE_LINE = 0;
+
+int LIST_COL   = 0;
+int LIST_LINE  = 10;
+
+int TIMER_COL  = 20;
+int TIMER_LINE = 2;
+
+static void cont_catcher(int);
+static int prwidth(char **, int);
+static void prword(char **, int);
+static void stop_catcher(int);
+static void tty_cleanup(void);
+static int tty_setup(void);
+static void tty_showboard(char *);
+static void winch_catcher(int);
+
+/*
+ * Do system dependent initialization
+ * This is called once, when the program starts
+ */
+int
+setup(void)
+{
+       char *cp, *ep;
+
+       if (tty_setup() < 0)
+               return(-1);
+
+       separator = malloc(4 * grid + 2);
+       if (separator == NULL)
+               err(1, NULL);
+
+       ep = separator + 4 * grid;
+       for (cp = separator; cp < ep;) {
+               *cp++ = '+';
+               *cp++ = '-';
+               *cp++ = '-';
+               *cp++ = '-';
+       }
+       *cp++ = '+';
+       *cp = '\0';
+
+       SCORE_COL += (grid - 4) * 4;
+       TIMER_COL += (grid - 4) * 4;
+       PROMPT_COL += (grid - 4) * 4;
+       LIST_LINE += (grid - 4) * 2;
+
+       return(0);
+}
+
+/*
+ * Do system dependent clean up
+ * This is called once, just before the program terminates
+ */
+void
+cleanup(void)
+{
+       tty_cleanup();
+}
+
+/*
+ * Display the player's word list, the list of words not found, and the running
+ * stats
+ */
+void
+results(void)
+{
+       int col, row;
+       int denom1, denom2;
+
+       move(LIST_LINE, LIST_COL);
+       clrtobot();
+       printw("Words you found (%d):", npwords);
+       refresh();
+       move(LIST_LINE + 1, LIST_COL);
+       prtable(pword, npwords, 0, ncols, prword, prwidth);
+
+       getyx(stdscr, row, col);
+       move(row + 1, col);
+       printw("Words you missed (%d):", nmwords);
+       refresh();
+       move(row + 2, col);
+       prtable(mword, nmwords, 0, ncols, prword, prwidth);
+
+       denom1 = npwords + nmwords;
+       denom2 = tnpwords + tnmwords;
+
+       move(SCORE_LINE, SCORE_COL);
+       printw("Score: %d out of %d\n", npwords, denom1);
+       move(SCORE_LINE + 1, SCORE_COL);
+       printw("Percentage: %0.2f%% (%0.2f%% over %d game%s)\n",
+       denom1 ? (100.0 * npwords)  / (double) denom1 : 0.0,
+       denom2 ? (100.0 * tnpwords) / (double) denom2 : 0.0,
+       ngames, ngames > 1 ? "s" : "");
+       move(TIMER_LINE, TIMER_COL);
+       wclrtoeol(stdscr);
+}
+
+static void
+prword(char **base, int indx)
+{
+       printw("%s", base[indx]);
+}
+
+static int
+prwidth(char **base, int indx)
+{
+       return (strlen(base[indx]));
+}
+
+/*
+ * Main input routine
+ *
+ * - doesn't accept words longer than MAXWORDLEN or containing caps
+ */
+char *
+get_line(char *q)
+{
+       int ch, done;
+       char *p;
+       int row, col;
+
+       p = q;
+       done = 0;
+       while (!done) {
+               ch = timerch();
+               switch (ch) {
+               case '\n':
+               case '\r':
+               case ' ':
+                       done = 1;
+                       break;
+               case '\033':
+                       findword();
+                       break;
+               case '\177':                    /* <del> */
+               case '\010':                    /* <bs> */
+                       if (p == q)
+                               break;
+                       p--;
+                       getyx(stdscr, row, col);
+                       move(row, col - 1);
+                       clrtoeol();
+                       refresh();
+                       break;
+               case '\025':                    /* <^u> */
+               case '\027':                    /* <^w> */
+                       if (p == q)
+                               break;
+                       getyx(stdscr, row, col);
+                       move(row, col - (int) (p - q));
+                       p = q;
+                       clrtoeol();
+                       refresh();
+                       break;
+#ifdef SIGTSTP
+               case '\032':                    /* <^z> */
+                       stop_catcher(0);
+                       break;
+#endif
+               case '\023':                    /* <^s> */
+                       stoptime();
+                       printw("<PAUSE>");
+                       refresh();
+                       while ((ch = inputch()) != '\021' && ch != '\023')
+                               ;
+                       move(crow, ccol);
+                       clrtoeol();
+                       refresh();
+                       starttime();
+                       break;
+               case '\003':                    /* <^c> */
+                       cleanup();
+                       exit(0);
+               case '\004':                    /* <^d> */
+                       done = 1;
+                       ch = EOF;
+                       break;
+               case '\014':                    /* <^l> */
+               case '\022':                    /* <^r> */
+                       redraw();
+                       break;
+               case '?':
+                       stoptime();
+                       if (help() < 0)
+                               showstr("Can't open help file", 1);
+                       starttime();
+                       break;
+               default:
+                       if (!islower(ch))
+                               break;
+                       if ((int) (p - q) == MAXWORDLEN) {
+                               p = q;
+                               badword();
+                               break;
+                       }
+                       *p++ = ch;
+                       addch(ch);
+                       refresh();
+                       break;
+               }
+       }
+       *p = '\0';
+       if (ch == EOF)
+               return(NULL);
+       return(q);
+}
+
+int
+inputch(void)
+{
+       int ch;
+
+       if ((ch = getch()) == ERR)
+               err(1, "cannot read input");
+       return (ch & 0177);
+}
+
+void
+redraw(void)
+{
+       clearok(stdscr, 1);
+       refresh();
+}
+
+void
+flushin(FILE *fp)
+{
+       tcflush(fileno(fp), TCIFLUSH);
+}
+
+/*
+ * Stop the game timer
+ */
+void
+stoptime(void)
+{
+       time_t t;
+
+       time(&t);
+       gone = (int) (t - start_t);
+}
+
+/*
+ * Restart the game timer
+ */
+void
+starttime(void)
+{
+       time_t t;
+
+       time(&t);
+       start_t = t - (long) gone;
+}
+
+/*
+ * Initialize for the display of the player's words as they are typed
+ * This display starts at (LIST_LINE, LIST_COL) and goes "down" until the last
+ * line.  After the last line a new column is started at LIST_LINE
+ * Keep track of each column position for showword()
+ * There is no check for exceeding COLS
+ */
+void
+startwords(void)
+{
+       crow = LIST_LINE;
+       ccol = LIST_COL;
+       maxw = 0;
+       ncolstarts = 1;
+       colstarts[0] = LIST_COL;
+       move(LIST_LINE, LIST_COL);
+       refresh();
+}
+
+/*
+ * Add a word to the list and start a new column if necessary
+ * The maximum width of the current column is maintained so we know where
+ * to start the next column
+ */
+void
+addword(char *w)
+{
+       int n;
+
+       if (crow == lastline) {
+               crow = LIST_LINE;
+               ccol += (maxw + 5);
+               colstarts[ncolstarts++] = ccol;
+               maxw = 0;
+               move(crow, ccol);
+       }
+       else {
+               move(++crow, ccol);
+               if ((n = strlen(w)) > maxw)
+                       maxw = n;
+       }
+       refresh();
+}
+
+/*
+ * The current word is unacceptable so erase it
+ */
+void
+badword(void)
+{
+       move(crow, ccol);
+       clrtoeol();
+       refresh();
+}
+
+/*
+ * Highlight the nth word in the list (starting with word 0)
+ * No check for wild arg
+ */
+void
+showword(int n)
+{
+       int col, row;
+
+       row = LIST_LINE + n % (lastline - LIST_LINE + 1);
+       col = colstarts[n / (lastline - LIST_LINE + 1)];
+       move(row, col);
+       standout();
+       printw("%s", pword[n]);
+       standend();
+       move(crow, ccol);
+       refresh();
+       delay(15);
+       move(row, col);
+       printw("%s", pword[n]);
+       move(crow, ccol);
+       refresh();
+}
+
+/*
+ * Walk the path of a word, refreshing the letters,
+ * optionally pausing after each
+ */
+static void
+doword(int pause, int r, int c)
+{
+       int i, row, col;
+       unsigned char ch;
+
+       for (i = 0; wordpath[i] != -1; i++) {
+               row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1;
+               col = BOARD_COL + (wordpath[i] % 4) * 4 + 2;
+               move(row, col);
+               ch = board[wordpath[i]];
+               if (HISET(ch))
+                       attron(A_BOLD);
+               if (SEVENBIT(ch) == 'q')
+                       printw("Qu");
+               else
+                       printw("%c", toupper(SEVENBIT(ch)));
+               if (HISET(ch))
+                       attroff(A_BOLD);
+               if (pause) {
+                       move(r, c);
+                       refresh();
+                       delay(5);
+               }
+       }
+}
+
+/*
+ * Get a word from the user and check if it is in either of the two
+ * word lists
+ * If it's found, show the word on the board for a short time and then
+ * erase the word
+ *
+ * Note: this function knows about the format of the board
+ */
+void
+findword(void)
+{
+       int c, found, i, r;
+       char buf[MAXWORDLEN + 1];
+
+       getyx(stdscr, r, c);
+       getword(buf);
+       found = 0;
+       for (i = 0; i < npwords; i++) {
+               if (strcmp(buf, pword[i]) == 0) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (!found) {
+               for (i = 0; i < nmwords; i++) {
+                       if (strcmp(buf, mword[i]) == 0) {
+                               found = 1;
+                               break;
+                       }
+               }
+       }
+       for (i = 0; i < MAXWORDLEN; i++)
+               wordpath[i] = -1;
+       usedbits = 0;
+       if (!found || checkword(buf, -1, wordpath) == -1) {
+               move(r, c);
+               clrtoeol();
+               addstr("[???]");
+               refresh();
+               delay(10);
+               move(r, c);
+               clrtoeol();
+               refresh();
+               return;
+       }
+
+       standout();
+       doword(1, r, c);
+       standend();
+       doword(0, r, c);
+
+       move(r, c);
+       clrtoeol();
+       refresh();
+}
+
+/*
+ * Display a string at the current cursor position for the given number of secs
+ */
+void
+showstr(const char *str, int delaysecs)
+{
+       addstr(str);
+       refresh();
+       delay(delaysecs * 10);
+       move(crow, ccol);
+       clrtoeol();
+       refresh();
+}
+
+void
+putstr(char *s)
+{
+       addstr(s);
+}
+
+/*
+ * Get a valid word and put it in the buffer
+ */
+void
+getword(char *q)
+{
+       int ch, col, done, i, row;
+       char *p;
+
+       done = 0;
+       i = 0;
+       p = q;
+       addch('[');
+       refresh();
+       while (!done && i < MAXWORDLEN - 1) {
+               ch = inputch();
+               switch (ch) {
+               case '\177':                    /* <del> */
+               case '\010':                    /* <bs> */
+                       if (p == q)
+                               break;
+                       p--;
+                       getyx(stdscr, row, col);
+                       move(row, col - 1);
+                       clrtoeol();
+                       break;
+               case '\025':                    /* <^u> */
+               case '\027':                    /* <^w> */
+                       if (p == q)
+                               break;
+                       getyx(stdscr, row, col);
+                       move(row, col - (int) (p - q));
+                       p = q;
+                       clrtoeol();
+                       break;
+               case ' ':
+               case '\n':
+               case '\r':
+                       done = 1;
+                       break;
+               case '\014':                    /* <^l> */
+               case '\022':                    /* <^r> */
+                       clearok(stdscr, 1);
+                       refresh();
+                       break;
+               default:
+                       if (islower(ch)) {
+                               *p++ = ch;
+                               addch(ch);
+                               i++;
+                       }
+                       break;
+               }
+               refresh();
+       }
+       *p = '\0';
+       addch(']');
+       refresh();
+}
+
+void
+showboard(char *b)
+{
+       tty_showboard(b);
+}
+
+void
+prompt(const char *mesg)
+{
+       move(PROMPT_LINE, PROMPT_COL);
+       printw("%s", mesg);
+       move(PROMPT_LINE + 1, PROMPT_COL);
+       refresh();
+}
+
+static int
+tty_setup(void)
+{
+       initscr();
+       raw();
+       noecho();
+
+       /*
+        * Does curses look at the winsize structure?
+        * Should handle SIGWINCH ...
+        */
+       nlines = LINES;
+       lastline = nlines - 1;
+       ncols = COLS;
+
+       signal(SIGTSTP, stop_catcher);
+       signal(SIGCONT, cont_catcher);
+       signal(SIGWINCH, winch_catcher);
+       return(0);
+}
+
+static void
+stop_catcher(int signo __unused)
+{
+       sigset_t sigset, osigset;
+
+       stoptime();
+       noraw();
+       echo();
+       move(nlines - 1, 0);
+       refresh();
+
+       signal(SIGTSTP, SIG_DFL);
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGTSTP);
+       sigprocmask(SIG_UNBLOCK, &sigset, &osigset);
+       kill(0, SIGTSTP);
+       sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+       signal(SIGTSTP, stop_catcher);
+}
+
+static void
+cont_catcher(int signo __unused)
+{
+       noecho();
+       raw();
+       clearok(stdscr, 1);
+       move(crow, ccol);
+       refresh();
+       starttime();
+}
+
+/*
+ * The signal is caught but nothing is done about it...
+ * It would mean reformatting the entire display
+ */
+static void
+winch_catcher(int signo __unused)
+{
+       struct winsize win;
+
+       signal(SIGWINCH, winch_catcher);
+       ioctl(fileno(stdout), TIOCGWINSZ, &win);
+       /*
+       LINES = win.ws_row;
+       COLS = win.ws_col;
+       */
+}
+
+static void
+tty_cleanup(void)
+{
+       move(nlines - 1, 0);
+       refresh();
+       noraw();
+       echo();
+       endwin();
+}
+
+static void
+tty_showboard(char *b)
+{
+       unsigned int i;
+       int line;
+       char ch;
+
+       clear();
+       move(BOARD_LINE, BOARD_COL);
+       line = BOARD_LINE;
+       printw(separator);
+       move(++line, BOARD_COL);
+       for (i = 0; i < ncubes; i++) {
+               printw("| ");
+               ch = SEVENBIT(b[i]);
+               if (HISET(b[i]))
+                       attron(A_BOLD);
+               if (ch == 'q')
+                       printw("Qu");
+               else
+                       printw("%c ", toupper((unsigned char)ch));
+               if (HISET(b[i]))
+                       attroff(A_BOLD);
+               if ((i + 1) % grid == 0) {
+                       printw("|");
+                       move(++line, BOARD_COL);
+                       printw(separator);
+                       move(++line, BOARD_COL);
+               }
+       }
+       move(SCORE_LINE, SCORE_COL);
+       printw("Type '?' for help");
+       refresh();
+}
diff --git a/games/boggle/boggle/prtable.c b/games/boggle/boggle/prtable.c
new file mode 100644 (file)
index 0000000..ea63f3a
--- /dev/null
@@ -0,0 +1,135 @@
+/*     $OpenBSD: prtable.c,v 1.11 2008/08/04 18:42:09 millert Exp $    */
+/*     $NetBSD: prtable.c,v 1.2 1995/03/21 12:14:42 cgd Exp $  */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)prtable.c   8.1 (Berkeley) 6/11/93
+ */
+
+#include <curses.h>
+
+#include "extern.h"
+
+#define        NCOLS   5
+
+static int get_maxlen(char **, int, int (*)(char **, int));
+
+/*
+ * Routine to print a table
+ * Modified from 'ls.c' mods (BJB/83)
+ * Arguments:
+ *     base    - address of first entry
+ *     num     - number of entries
+ *     d_cols  - number of columns to use if > 0, "best" size if == 0
+ *     width   - max line width if not zero
+ *     prentry - address of the routine to call to print the string
+ *     length  - address of the routine to call to determine the length
+ *               of string to be printed
+ *
+ * prtable and length are called with the address of the base and
+ * an index
+ */
+void
+prtable(char **base, int num, int d_cols, int width,
+        void (*prentry)(char **, int), int (*length)(char **, int))
+{
+       int c, j;
+       int a, b, cols, loc, maxlen, nrows, z;
+       int col __unused, row;
+
+       if (num == 0)
+               return;
+       maxlen = get_maxlen(base, num, length) + 1;
+       if (d_cols > 0)
+               cols = d_cols;
+       else
+               cols = width / maxlen;
+       if (cols == 0)
+               cols = NCOLS;
+       nrows = (num - 1) / cols + 1;
+       for (a = 1; a <= nrows; a++) {
+               b = c = z = loc = 0;
+               for (j = 0; j < num; j++) {
+                       c++;
+                       if (c >= a + b)
+                               break;
+               }
+               while (j < num) {
+                       (*prentry)(base, j);
+                       loc += (*length)(base, j);
+                       z++;
+                       b += nrows;
+                       for (j++; j < num; j++) {
+                               c++;
+                               if (c >= a + b)
+                                       break;
+                       }
+                       if (j < num) {
+                               while (loc < z * maxlen) {
+                                       addch(' ');
+                                       loc++;
+                               }
+                       }
+               }
+               getyx(stdscr, row, col);
+               move(row + 1, 0);
+               if (row + 1 == lastline && a != nrows) {
+                       attron(A_REVERSE);
+                       printw("--More--");
+                       attroff(A_REVERSE);
+                       do {
+                           j = inputch();
+                       } while (j != ' ' && j != 'q' && j != 'Q');
+                       if (j == 'q' || j == 'Q') {
+                               move(row + 1, 0);
+                               wclrtoeol(stdscr);
+                               break;
+                       }
+                       move(LIST_LINE, LIST_COL);
+                       wclrtobot(stdscr);
+               }
+       }
+       refresh();
+}
+
+static int
+get_maxlen(char **base, int num, int (*length)(char **, int))
+{
+       int i, len, max;
+
+       max = (*length)(base, 0);
+       for (i = 0; i < num; i++) {
+               if ((len = (*length)(base, i)) > max)
+                       max = len;
+       }
+       return(max);
+}
diff --git a/games/boggle/boggle/timer.c b/games/boggle/boggle/timer.c
new file mode 100644 (file)
index 0000000..b7e0654
--- /dev/null
@@ -0,0 +1,104 @@
+/*     $OpenBSD: timer.c,v 1.15 2016/08/27 02:11:27 guenther Exp $     */
+/*     $NetBSD: timer.c,v 1.3 1995/04/24 12:22:45 cgd Exp $    */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/select.h>
+#include <curses.h>
+#include <setjmp.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static int waitch(long);
+
+/*
+ * Update the display of the remaining time while waiting for a character
+ * If time runs out do a longjmp() to the game controlling routine, returning
+ * non-zero; oth. return the character
+ * Leave the cursor where it was initially
+ */
+int
+timerch(void)
+{
+       time_t prevt, t;
+       int col, remaining, row;
+
+       getyx(stdscr, row, col);
+       prevt = 0L;
+       for (;;) {
+               if (waitch(1000L) == 1)
+                       break;
+               time(&t);
+               if (t == prevt)
+                       continue;
+               prevt = t;
+               remaining = tlimit - (int) (t - start_t);
+               if (remaining < 0) {
+                       longjmp(env, 1);
+               }
+               move(TIMER_LINE, TIMER_COL);
+               printw("%d:%02d", remaining / 60, remaining % 60);
+               move(row, col);
+               refresh();
+       }
+       return (inputch());
+}
+
+/*
+ * Wait up to 'delay' microseconds for input to appear
+ * Returns 1 if input is ready, 0 oth.
+ */
+static int
+waitch(long delay)
+{
+       fd_set fdbits;
+       struct timeval duration;
+
+       duration.tv_sec = 0;
+       duration.tv_usec = delay;
+       FD_ZERO(&fdbits);
+       FD_SET(STDIN_FILENO, &fdbits);
+       return (select(STDIN_FILENO+1, &fdbits, NULL, NULL, &duration));
+}
+
+void
+delay(int tenths)
+{
+       struct timeval duration;
+
+       duration.tv_usec = (tenths % 10 ) * 100000L;
+       duration.tv_sec = tenths / 10;
+       select(0, 0, 0, 0, &duration);
+}
diff --git a/games/boggle/boggle/word.c b/games/boggle/boggle/word.c
new file mode 100644 (file)
index 0000000..99cde91
--- /dev/null
@@ -0,0 +1,204 @@
+/*     $OpenBSD: word.c,v 1.8 2016/01/10 13:18:07 mestre Exp $ */
+/*     $NetBSD: word.c,v 1.2 1995/03/21 12:14:45 cgd Exp $     */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bog.h"
+#include "extern.h"
+
+static char *dictspace, *dictend;
+static char *sp;
+
+static int first = 1, lastch = 0;
+
+/*
+ * Return the next word in the compressed dictionary in 'buffer' or
+ * NULL on end-of-file
+ */
+char *
+nextword(FILE *fp)
+{
+       int ch, pcount;
+       char *p;
+       static char buf[MAXWORDLEN + 1];
+
+       if (fp == NULL) {
+               if (sp == dictend)
+                       return (NULL);
+
+               p = buf + (int) *sp++;
+
+               /*
+                * The dictionary ends with a null byte
+                */
+               while (*sp >= 'a')
+                       if ((*p++ = *sp++) == 'q')
+                               *p++ = 'u';
+       } else {
+               if (first) {
+                       if ((pcount = getc(fp)) == EOF)
+                               return (NULL);
+                       first = 0;
+               } else if ((pcount = lastch) == EOF)
+                       return (NULL);
+
+               p = buf + pcount;
+
+               while ((ch = getc(fp)) != EOF && ch >= 'a')
+                       if ((*p++ = ch) == 'q')
+                               *p++ = 'u';
+               lastch = ch;
+       }
+       wordlen = (int) (p - buf);
+       *p = '\0';
+       return (buf);
+}
+
+/*
+ * Reset the state of nextword() and do the fseek()
+ */
+long
+dictseek(FILE *fp, long offset, int ptrname)
+{
+       if (fp == NULL) {
+               if ((sp = dictspace + offset) >= dictend)
+                       return (-1);
+               return (0);
+       }
+
+       first = 1;
+       return (fseek(fp, offset, ptrname));
+}
+
+FILE *
+opendict(const char *dict)
+{
+       FILE *fp;
+
+       if ((fp = fopen(dict, "r")) == NULL)
+               return (NULL);
+       return (fp);
+}
+
+/*
+ * Load the given dictionary and initialize the pointers
+ */
+int
+loaddict(FILE *fp)
+{
+       struct stat statb;
+       long n;
+       int st;
+       char *p;
+
+       if (fstat(fileno(fp), &statb) < 0) {
+               fclose(fp);
+               return (-1);
+       }
+
+       /*
+        * An extra character (a sentinel) is allocated and set to null
+        * to improve the expansion loop in nextword().
+        */
+       if ((dictspace = malloc(statb.st_size + 1)) == NULL) {
+               fclose(fp);
+               return (-1);
+       }
+       n = (long)statb.st_size;
+       sp = dictspace;
+       dictend = dictspace + n;
+
+       p = dictspace;
+       st = -1;
+       while (n > 0 && (st = fread(p, 1, BUFSIZ, fp)) > 0) {
+               p += st;
+               n -= st;
+       }
+       if (st < 0) {
+               fclose(fp);
+               warnx("Error reading dictionary");
+               return (-1);
+       }
+       *p = '\0';
+       return (0);
+}
+
+/*
+ * Dependent on the exact format of the index file:
+ * Starting offset field begins in column 1 and length field in column 9
+ * Taking the easy way out, the input buffer is made "large" and a check
+ * is made for lines that are too long
+ */
+int
+loadindex(const char *indexfile)
+{
+       int i, j;
+       char buf[BUFSIZ];
+       FILE *fp;
+
+       if ((fp = fopen(indexfile, "r")) == NULL) {
+               warnx("Can't open '%s'", indexfile);
+               return (-1);
+       }
+       i = 0;
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               if (strchr(buf, '\n') == NULL) {
+                       warnx("A line in the index file is too long");
+                       fclose(fp);
+                       return(-1);
+               }
+               j = *buf - 'a';
+               if (i != j) {
+                       warnx("Bad index order");
+                       fclose(fp);
+                       return(-1);
+               }
+               dictindex[j].start = atol(buf + 1);
+               dictindex[j].length = atol(buf + 9) - dictindex[j].start;
+               i++;
+       }
+       if (i != 26) {
+               warnx("Bad index length");
+               fclose(fp);
+               return(-1);
+       }
+       fclose(fp);
+       return(0);
+}
diff --git a/games/boggle/dictfiles/Makefile b/games/boggle/dictfiles/Makefile
new file mode 100644 (file)
index 0000000..4cd6a8a
--- /dev/null
@@ -0,0 +1,21 @@
+.PATH: ${.CURDIR}/../boggle
+
+# XXX better word list selection
+.if exists(${.CURDIR}/../../../share/dict/web2)
+BOOGLEWORDS?=  ${.CURDIR}/../../../share/dict/web2
+.else
+BOOGLEWORDS?=  /usr/share/dict/words
+.endif
+
+FILES= dictindex dictionary helpfile
+FILESDIR=      ${SHAREDIR}/games/boggle
+
+CLEANFILES=    dictindex dictionary
+
+dictionary:
+       ${.OBJDIR}/../mkdict/mkdict.nx < ${BOOGLEWORDS} > ${.TARGET}
+
+dictindex: dictionary
+       ${.OBJDIR}/../mkindex/mkindex.nx < ${.ALLSRC} > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/games/boggle/mkdict/Makefile b/games/boggle/mkdict/Makefile
new file mode 100644 (file)
index 0000000..33e909d
--- /dev/null
@@ -0,0 +1,11 @@
+#      $OpenBSD: Makefile,v 1.4 2016/01/07 16:00:31 tb Exp $
+#      $NetBSD: Makefile,v 1.2 1995/03/21 12:14:47 cgd Exp $
+#      @(#)Makefile    8.1 (Berkeley) 6/11/93
+
+PROG=  mkdict
+CFLAGS+=-I${.CURDIR}/../boggle
+
+# this mkdict is used in place, it is not installed anywhere
+install:
+
+.include <bsd.hostprog.mk>
diff --git a/games/boggle/mkdict/mkdict.c b/games/boggle/mkdict/mkdict.c
new file mode 100644 (file)
index 0000000..befc88f
--- /dev/null
@@ -0,0 +1,118 @@
+/*     $OpenBSD: mkdict.c,v 1.13 2016/01/07 16:00:31 tb Exp $  */
+/*     $NetBSD: mkdict.c,v 1.2 1995/03/21 12:14:49 cgd Exp $   */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Filter out words that:
+ *     1) Are not completely made up of lower case letters
+ *     2) Contain a 'q' not immediately followed by a 'u'
+ *     3) Are less than 3 characters long
+ *     4) Are greater than MAXWORDLEN characters long
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bog.h"
+
+int
+main(int argc, char *argv[])
+{
+       char *p, *q;
+       const char *errstr;
+       int ch, common, nwords;
+       int n = 1;      /* suppress maybe stuff */
+       int current, len, prev, qcount;
+       char buf[2][MAXWORDLEN + 1];
+
+       prev = 0;
+       current = 1;
+       buf[prev][0] = '\0';
+       if (argc == 2) {
+               n = strtonum(argv[1], 1, INT_MAX, &errstr);
+               if (errstr)
+                       errx(1, "%s: %s", argv[1], errstr);
+       }
+
+       for (nwords = 1;
+           fgets(buf[current], MAXWORDLEN + 1, stdin) != NULL; ++nwords) {
+               if ((p = strchr(buf[current], '\n')) == NULL) {
+                       warnx("word too long: %s", buf[current]);
+                       while ((ch = getc(stdin)) != EOF && ch != '\n')
+                               ;
+                       if (ch == EOF)
+                               break;
+                       continue;
+               }
+               len = 0;
+               for (p = buf[current]; *p != '\n'; p++) {
+                       if (!islower((unsigned char)*p))
+                               break;
+                       if (*p == 'q') {
+                               q = p + 1;
+                               if (*q != 'u')
+                                       break;
+                               else {
+                                       while ((*q = *(q + 1)))
+                                               q++;
+                               }
+                               len++;
+                       }
+                       len++;
+               }
+               if (*p != '\n' || len < 3 || len > MAXWORDLEN)
+                       continue;
+               if (argc == 2 && nwords % n)
+                       continue;
+
+               *p = '\0';
+               p = buf[current];
+               q = buf[prev];
+               qcount = 0;
+               while ((ch = *p++) == *q++ && ch != '\0')
+                       if (ch == 'q')
+                               qcount++;
+               common = p - buf[current] - 1;
+               printf("%c%s", common + qcount, p - 1);
+               prev = !prev;
+               current = !current;
+       }
+       warnx("%d words", nwords);
+       return 0;
+}
diff --git a/games/boggle/mkindex/Makefile b/games/boggle/mkindex/Makefile
new file mode 100644 (file)
index 0000000..9361b9e
--- /dev/null
@@ -0,0 +1,11 @@
+#      $OpenBSD: Makefile,v 1.4 2016/01/07 16:00:31 tb Exp $
+#      $NetBSD: Makefile,v 1.2 1995/03/21 12:14:51 cgd Exp $
+#      @(#)Makefile    8.1 (Berkeley) 6/11/93
+
+PROG=  mkindex
+CFLAGS+=-I${.CURDIR}/../boggle
+
+# this mkindex is used in place, it is not installed anywhere
+install:
+
+.include <bsd.hostprog.mk>
diff --git a/games/boggle/mkindex/mkindex.c b/games/boggle/mkindex/mkindex.c
new file mode 100644 (file)
index 0000000..73c5020
--- /dev/null
@@ -0,0 +1,107 @@
+/*     $OpenBSD: mkindex.c,v 1.9 2016/01/07 16:00:31 tb Exp $  */
+/*     $NetBSD: mkindex.c,v 1.2 1995/03/21 12:14:52 cgd Exp $  */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "bog.h"
+
+char *nextword(FILE *, char *, int *, int *);
+
+int
+main(void)
+{
+       int clen, rlen, prev;
+       long off, start;
+       char buf[MAXWORDLEN + 1];
+
+       prev = '\0';
+       off = start = 0L;
+       while (nextword(stdin, buf, &clen, &rlen) != NULL) {
+               if (*buf != prev) {
+                       if (prev != '\0')
+                               printf("%c %6ld %6ld\n", prev, start, off - 1);
+                       prev = *buf;
+                       start = off;
+               }
+               off += clen + 1;
+       }
+       printf("%c %6ld %6ld\n", prev, start, off - 1);
+       return 0;
+}
+
+/*
+ * Return the next word in the compressed dictionary in 'buffer' or
+ * NULL on end-of-file
+ * Also set clen to the length of the compressed word (for mkindex) and
+ * rlen to the strlen() of the real word
+ */
+char *
+nextword(FILE *fp, char *buffer, int *clen, int *rlen)
+{
+       int ch, pcount;
+       char *p, *q;
+       static char buf[MAXWORDLEN + 1];
+       static int first = 1;
+       static int lastch = 0;
+
+       if (first) {
+               if ((pcount = getc(fp)) == EOF)
+                       return (NULL);
+               first = 0;
+       }
+       else if ((pcount = lastch) == EOF)
+               return (NULL);
+
+       p = buf + (*clen = pcount);
+
+       while ((ch = getc(fp)) != EOF && ch >= 'a')
+                       *p++ = ch;
+               lastch = ch;
+       *p = '\0';
+
+       *rlen = (int) (p - buf);
+       *clen = *rlen - *clen;
+
+       p = buf;
+       q = buffer;
+       while ((*q++ = *p) != '\0') {
+               if (*p++ == 'q')
+                       *q++ = 'u';
+       }
+       return (buffer);
+}