[comp.sources.games] v02i064: anagram - program to solve word puzzles

games-request@tekred.TEK.COM (10/27/87)

Submitted by: Fred Blonder <fred@MIMSY.UMD.EDU>
Comp.sources.games: Volume 2, Issue 64
Archive-name: anagram

	[Following note is from the author.  See also v1i96 for the
	 game 'jumble' which is somewhat similar.  -br]

[[This program is most useful for unscrambling words as found in
  some puzzles.  There are other amusing uses to which it can be
  put as well.]]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  anagram.6 anagram.c
# Wrapped by billr@tekred on Tue Oct 27 11:44:02 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f anagram.6 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"anagram.6\"
else
echo shar: Extracting \"anagram.6\" \(2183 characters\)
sed "s/^X//" >anagram.6 <<'END_OF_anagram.6'
X.TH ANAGRAM 6L 11-Sep-1987
X.SH NAME
Xanagram \- search dictionary for anagrams
X.SH SYNOPSIS
X.I anagram
X[ -d <dictfile> ] [ -m <Nr> ] [ -l ] [ -r ] string1 [ string2 . . . ]
X.SH DESCRIPTION
XFor each of its arguments,
X.I anagram
Xsearches the dictionary file for words which contain the exact same letters as
Xthe argument, but in any order, and writes them to the standard output.
XDictionary entires which contain non-alphabetic characters are ignored.
X.sp
X.I Anagram
Xis useful for cheating on word-puzzles.
X.sp
XOptions are:
X.sp
X.in +10
X.ti -5
X.B -d
X.I <dictfile>
X\- Used to specify
X.I <dictfile>
Xas the file containing the word list, instead of the default.
X.sp
X.ti -5
X.B -m
X.I <Nr>
X\- Indicates the minimum length of words to be read from the dictionary file.
XThis is useful for avoiding lots of anagrams constructed from tiny little
Xconnective words and abbreviations. The default minimum is two.
X.sp
X.ti -5
X.B -l
X\- Allows
X.I anagram
Xto find partial matches, printing out the leftover letters between square
Xbrackets. This is useful for finding "near matches" or for deciphering a
Xscrambled multi-word phrase where one of the words isn't literally in the word
Xlist.
X.sp
X.ti -5
X.B -r
X\- Recursive mode. This one is real useful. In this mode, when
X.I anagram
Xfinds a match which doesn't use up all the letters in the argument string, it
Xtries to match recursively on the remainder of the string. This allows it to
Xunscramble an entire phrase into separate words. Be warned that
X.I anagram
Xoperates rather slowly in this mode.
X.in -10
X.SH EXAMPLES
X.nf
X.na
X\&
X% anagram stop
Xpost
Xspot
Xstop
X.sp
X% anagram -l stop
Xopt [s]
XPo [st]
Xpost
Xpot [s]
Xso [pt]
Xsop [t]
Xspot
XSt [op]
Xstop
Xto [ps]
Xtop [s]
X.sp
X% anagram -m 3 -r tfyoowtr
Xforty tow
Xforty two
X.fi
X.ad
X.SH AUTHOR
XFred Blonder <fred@mimsy.umd.edu>
X.SH FILES
X.in +5
X.ti -5
X/usr/dict/words \- default dictionary file
X.br
X.ti -5
X/usr/dict/web2 \- a more complete dictionary, which takes longer to search.
X.in -5
X.SH "SEE ALSO"
Xgrep(1)
X.SH DIAGNOSTICS
XThe only likely errors are problems opening or reading the dictionary file.
X.SH BUGS
XExtremely inefficient. Makes multiple passes through the dictionary file when
Xoperating in recursive mode.
END_OF_anagram.6
if test 2183 -ne `wc -c <anagram.6`; then
    echo shar: \"anagram.6\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f anagram.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"anagram.c\"
else
echo shar: Extracting \"anagram.c\" \(5905 characters\)
sed "s/^X//" >anagram.c <<'END_OF_anagram.c'
X#ifndef lint
Xstatic char sccsid[] = "@(#)anagram.c (U of Maryland) FLB 4-Sep-1987";
Xstatic char RCSid[] = "@(#)$Header: anagram.c,v 1.1 87/09/11 02:19:33 fred Exp $";
X#endif
X
X/*
X * anagram - search a dictionary for anagrams of a given string
X *
X *	Anagram takes each of its arguments in turn and searches
X *	a dictionary file for anagrams, printing any it finds to
X *	the standard output.
X *
X *	Options:
X *
X *		-d <dictionary-file>
X *			Specifies a file other than the default,
X *			to read the word list from.
X *
X *		-m <n>
X *			Indicates the minimum number of characters
X *			in an anagram, which will be acceptable.
X *			The default is 2.
X *
X *		-l
X *			Permits partial matches with 'leftover'
X *			letters which will be printed between
X *			square brackets.
X *
X *		-r
X *			Recursive mode: anagram will find
X *			partial mathces and then attempt to
X *			find matches for the remaning letters.
X *
X *	Fred Blonder <fred@mimsy.umd.edu>, September 4, 1987
X *
X * To compile:
X %	cc -s -O anagram.c -o anagram
X */
X
X#include <stdio.h>
X#include <sysexits.h>
X#include <ctype.h>
X
Xchar *index();
X
X#define	DICTIONARY	"/usr/dict/words"	/* Default dictionary */
X#define	LETTERS_IN_ALPHABET	26	/* This shouldn't change much. ;-) */
X
Xtypedef char histogram[LETTERS_IN_ALPHABET];
X
X/* Program arguments and defaults. */
Xchar *dictionary = DICTIONARY;
Xint leftovers_flag = 0, min_letters = 2, recurse_flag = 0;
X
X/*****************************************************************************/
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X
X#define	SHIFT	argc--; argv++
X
XSHIFT;
X
Xwhile (argc > 0 && **argv == '-') {	/* Munch on flag arguments. */
X	register char *cp;
X
X	for (cp = &argv[0][1]; *cp; cp++)
X		switch (*cp) {
X			case 'd':	/* User-specified dictionary */
X				SHIFT;
X				dictionary = *argv;
X				break;
X
X			case 'l':	/* Allow 'leftover' letters */
X				leftovers_flag++;
X				break;
X
X			case 'm':	/* Specify minimum word length */
X				SHIFT;
X				min_letters = atoi(*argv);
X				break;
X
X			case 'r':	/* Recursive mode */
X				recurse_flag++;
X				break;
X
X			default:
X				fprintf(stderr,
X					"anagram: unknown flag: %c\n", *cp);
X			}
X	SHIFT;
X	}
X
Xif (argc < 1) {
X	fprintf(stderr,
X	    "usage: anagram [ -d <dictionary> ] [ -m <number> ] [ -l ] ");
X	fprintf(stderr, "[ -r ] string1 [ string2 . . . ]\n");
X	exit(EX_USAGE);
X	}
X
Xdo {
X	unscramble(*argv);
X	SHIFT;
X	if (argc > 0)
X		putchar('\n');
X	} while(argc > 0);
X
Xexit (EX_OK);
X}
X
X/*****************************************************************************/
X
Xunscramble(word)
Xchar word[];
X{
Xhistogram w_h, d_h, diff_h;
Xregister FILE *dict_f;
Xchar dict_buff[BUFSIZ];
Xregister int diff;
Xlong p_loc = 0L;
X
Xmake_hist(word, w_h);
X
Xif ((dict_f = fopen(dictionary, "r")) == NULL) {
X	perror(dictionary);
X	exit(EX_OSFILE);
X	}
X
Xwhile (p_loc = ftell(dict_f), fgets(dict_buff, BUFSIZ, dict_f) != NULL) {
X	register char *cp;
X
X	if (cp = index(dict_buff, '\n'))
X		*cp = '\0';
X
X	if (strlen(dict_buff) < min_letters)
X		continue;
X
X	{
X	register char *cp;
X
X	for (cp = dict_buff; *cp && isalpha(*cp); cp++);
X	if (*cp)
X		continue;
X	}
X
X	make_hist(dict_buff, d_h);
X	diff = cmp_hist(w_h, d_h, diff_h);
X
X	if (!diff) {
X		printf("%s\n", dict_buff);
X		continue;
X		}
X
X	if (diff > 0) {
X		long f_loc;
X
X		f_loc = ftell(dict_f);
X		if (fseek(dict_f, p_loc, 0) < 0) {
X			perror("Seek error on dictionary file");
X			exit(EX_IOERR);
X			}
X		remainder(dict_buff, dict_f, diff_h);
X		if (fseek(dict_f, f_loc, 0) < 0) {
X			perror("Seek error on dictionary file");
X			exit(EX_IOERR);
X			}
X		}
X	}
X
X(void)fclose(dict_f);
X}
X
X/*****************************************************************************/
X
Xremainder(p_line, file, hist)
Xchar p_line[];
Xregister FILE *file;
Xhistogram hist;
X{
Xhistogram d_h, diff_h;
Xchar dict_buff[BUFSIZ];
Xregister int diff, found_match = 0;
Xlong p_loc = 0L;
X
Xif (recurse_flag)
X	while (p_loc = ftell(file), fgets(dict_buff, BUFSIZ, file) != NULL) {
X		register char *cp;
X
X		if (cp = index(dict_buff, '\n'))
X			*cp = '\0';
X
X		if (strlen(dict_buff) < min_letters)
X			continue;
X
X		{
X		register char *cp;
X
X		for (cp = dict_buff; *cp && isalpha(*cp); cp++);
X		if (*cp)
X			continue;
X		}
X
X		make_hist(dict_buff, d_h);
X		diff = cmp_hist(hist, d_h, diff_h);
X
X		if (!diff) {
X			printf("%s %s\n", p_line, dict_buff);
X			found_match++;
X			continue;
X			}
X
X		if (diff > 0) {
X			long f_loc;
X			char t_buf[BUFSIZ];
X
X			f_loc = ftell(file);
X			if (fseek(file, p_loc, 0) < 0) {
X				perror("Seek error on dictionary file");
X				exit(EX_IOERR);
X				}
X			(void)sprintf(t_buf, "%s %s", p_line, dict_buff);
X			remainder(t_buf, file, diff_h);
X			found_match++;
X			if (fseek(file, f_loc, 0) < 0) {
X				perror("Seek error on dictionary file");
X				exit(EX_IOERR);
X				}
X			}
X		}
X
Xif (!found_match && leftovers_flag) {
X	printf("%s ", p_line);
X	print_hist(hist);
X	putchar('\n');
X	}
X}
X
X/*****************************************************************************/
X
Xmake_hist(word, w_hist)
Xchar word[];
Xhistogram w_hist;
X{
Xregister char *cp;
X
Xfor (cp = (char *)w_hist; cp < (char *)&w_hist[LETTERS_IN_ALPHABET]; cp++)
X	*cp = (char)0;
X
Xfor (cp = word; *cp; cp++)
X	if (isalpha(*cp))
X		if (isupper(*cp))
X			w_hist[tolower(*cp)-'a']++;
X		else
X			w_hist[*cp-'a']++;
X}
X
X/*****************************************************************************/
X
Xcmp_hist(h1, h2, h3)
Xhistogram h1, h2, h3;
X{
Xregister char *cp1, *cp2, *cp3;
Xregister int c = LETTERS_IN_ALPHABET;
Xint ret_val = 0;
X
Xfor (cp1 = (char *)h1, cp2 = (char *)h2, cp3 = (char *)h3;
X					c-- > 0; cp1++, cp2++, cp3++)
X	if (*cp3 = (*cp1 - *cp2))
X		if (*cp3 > 0)
X			ret_val++;
X		else
X			return -1;
X
Xreturn ret_val;
X}
X
X/*****************************************************************************/
X
Xprint_hist(h)
Xhistogram h;
X{
Xregister int k;
X
Xputchar('[');
X
Xfor (k = 0; k < LETTERS_IN_ALPHABET; k++) {
X	register int i;
X
X	for (i = h[k]; i > 0; i--)
X		putchar('a'+k);
X	}
X
Xputchar(']');
X}
X
X/*****************************************************************************/
END_OF_anagram.c
if test 5905 -ne `wc -c <anagram.c`; then
    echo shar: \"anagram.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0