[net.sources] Anagram Utility

herndon@umn-cs.UUCP (03/05/86)

  The following is a program for computing anagrams.  It should
work on any system which has a dictionary & the curses package.
It's a moderately quick hack, put together for a friend who wanted
to find all the words which could be made out of a phrase, and
adapted so that it will work with phrases.  (An anagram is a
word or phrase made up from the letters of another word or phrase.)
  To compile, cut the program out and place in "ana.c".  Then
compile with
    cc ana.c -o ana -lcurses -ltermcap
To run, type
    ana /usr/dict/words <word> <word>...
(on my system, /usr/dict/words is the dictionary).  The program
will display all words in the dictionary which may be made from
the words on the command line.  After you type in each word, the
program will redisplay the screen showing only those words that
may be constructed from the letters remaining.  ^U will erase the
phrase you've entered so far, ^W will erase the last word entered,
^F will step forward to words not shown on the display, and ^B will
step backwards to words not shown on the display.  ? will display
a help message, and Q will cause the program to exit.

  Sample anagram:
     sheraton ==
	  rats on her
	| ashen rot
	| no hearts
	| the sonar
	| north sea
	| heros tan
	| near shot
	| hot snare
	| treason + h
	| ...

  A popular use for programs like this is to figure out words
that may be constructed from people's names.  If your work/school
place has posted name-directories made up of individual letters,
these may then be re-arranged to provide entertainment.

Disclaimer:
  Don't blame me if you get fired.


--------------------------------------------------------------Cut Here.
#include <ctype.h>
#include <curses.h>

/* maximum number of test words */
#define NLEVELS	30


char *words[NLEVELS][20000];
int letters[NLEVELS][26];
int savelet[26];
int maxword[NLEVELS];
int wordindex[NLEVELS];
int maxwordlen = 0;
char phrase[120];

char wordspace[100000];
char *nextwsp = &wordspace[0];




main(ac, av)
int ac;
char *av[];
{
	register int i;
	register char c;
	char *p;
	FILE *fp;
	char line[120];
	register int status;
	int lets[26], testw[26];

	if(ac < 3) {
		printf("Usage:     ana dict_file word [word ...]\n");
		exit(1);
	}
	for(i = 2; i < ac; i++) {
		printf("%s ", av[i]);
		p = av[i];
		while(*p) {
			if(*p < 'a' || *p > 'z') {
				printf("Bad character: %c.\n", *p);
			} else {
				lets[*p - 'a']++;
			}
			p++;
		}
	}
	printf("\n");
	for(i = 0; i < 26; i++) {
		if(lets[i] != 0) {
			printf("letter '%c': %d.\n", i+'a', lets[i]);
		}
	}

	if((fp = fopen(av[1], "r")) == NULL) {
		printf("Can't open %s.\n", av[1]);
		exit(1);
	}

	while(1) {
		for(i = 0; i < 26; i++)
			testw[i] = lets[i];
		i = 0;
		status = TRUE;
		do {
			c = getc(fp);
			if(c >= 'A' && c <= 'Z')
				c += 'a' - 'A';
			if(islower(c)) {
				if(--testw[c-'a'] < 0)
					status = FALSE;
			} else if(isdigit(c)) {
				status = FALSE;
			}
			line[i++] = c;
		} while(c != '\n' && c != EOF);
		line[i-1] = '\0';
		if(status == TRUE) {
			printf("%s\n", line);
			words[0][maxword[0]++] = nextwsp;
			i = 0;
			do {
				*nextwsp++ = line[i];
			} while(line[i++] != '\0');
			if(i-1 > maxwordlen)
				maxwordlen = i-1;
		}
nextword:	if(c == EOF) {
			fclose(fp);
			wordindex[0] = 0;
			lcopy(lets, letters[0]);
			lcopy(lets, savelet);
			anagram();
			exit(0);
		}
	}
}


anagram()
{
	int i = 0;
	int level = 0;
	char c;

	initscr();
	noecho();
	crmode();
	display(level);
	while(c = getch()) {
		switch(c) {
		    case '?':
			printf("? - help\n");
			printf("Q - quit\n");
			printf("' ' - print remaining words\n");
			printf("^U - erase entered words\n");
			printf("^W - erase entered word\n");
			printf("^F - page forward in dictionary\n");
			printf("^B - page backward in dictionary\n");
			printf("lower case letters print, if in remaining letters\n");
			printf("-----> Hit any character to continue.");
			getch();
			display(level);
			break;
			
		    case 'Q':
			endwin();
			exit(0);
			/* You have reached the point of no return. */

		    case '\n':
			display(level);
			break;

		    case ' ':
			if(i > 0 && phrase[i-1] != ' ') {
				phrase[i++] = ' ';
				phrase[i] = '\0';
				compute(level);
				level++;
				display(level);
			}
			break;

		    case '\025':		/* ^u */
			i = 0;
			phrase[i] = '\0';
			level = 0;
			lcopy(savelet, letters[0]);
			display(level);
			break;

		    case '\b':
		    case '\0177':
			if(i > 0) {
				if(islower(phrase[i-1])) {
					++letters[level][phrase[i-1]-'a'];
					i--;
					mvaddch(LINES-1, i-1, ' ');
					phrase[i] = '\0';
				} else if(phrase[i-1] == ' ') {
					level--;
					i--;
					mvaddch(LINES-1, i-1, ' ');
					phrase[i] = '\0';
				} else {
					mvaddstr(LINES-1,60,"error in phrase!");
					putchar('\07');
				}
			}
			display(level);
			break;

		    case '\023':		/* ^w - erase word */
			if(i > 0) {
				if(phrase[i-1] == ' ') {
					level--;
					i--;
					phrase[i]= '\0';
				}
				while(i > 0 && (islower(phrase[i-1]))) {
					i--;
					phrase[i] = '\0';
				}
			}
			display(level);
			break;

		    case '\06':			/* ^f - page forward */
			wordindex[level] += 75;
			if(wordindex[level] >= maxword[level])
				wordindex[level] = maxword[level];
			display(level);
			break;

		    case '\02':			/* ^b - page back */
			wordindex[level] -= 75;
			if(wordindex[level] < 0)
				wordindex[level] = 0;
			display(level);
			break;

		    default:
			if(islower(c)) {
				if(--letters[level][c-'a'] >= 0) {
					mvaddch(LINES-1, i, c);
					phrase[i++] = c;
					phrase[i] = '\0';
				} else {
					++letters[level][c-'a'];
					putchar('\07');
				}
				refresh();
			} else {
				putchar('\07');
			}
		}
	}
}


lcopy(from, to)
int from[], to[];
{
	int i;

	for(i = 0; i < 26; i++)
		to[i] = from[i];
}


display(level)
int level;
{
	int i, row, col;

	clear();
	row = col = 0;
	for(i = wordindex[level]; i < maxword[level]; i++) {
		mvaddstr(row, col, words[level][i]);
		if(++row >= LINES-3) {
			row = 0;
			col += maxwordlen + 6;
			if(col+maxwordlen >= COLS-4)
				break;
		}
	}
	mvaddstr(LINES-1, 0, phrase);
	refresh();
}

compute(level)
int level;
{
	int testw[26];
	int i, gw, nextsave;
	char *p;
	int j;

	for(nextsave = i = 0; i < maxword[level]; i++) {
		lcopy(letters[level], testw);
		gw = TRUE;
		for(p = words[level][i]; *p != '\0'; p++) {
			if(islower(*p)) {
				if(--testw[*p-'a'] < 0) {
					gw = FALSE;
					break;
				}
			}
		}
		if(gw == TRUE)
			words[level+1][nextsave++] = words[level][i];
	}
	lcopy(letters[level], letters[level+1]);
	wordindex[level+1] = 0;
	maxword[level+1] = nextsave;
}