452 lines
5.1 KiB
C
452 lines
5.1 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <bio.h>
|
|
#include <ctype.h>
|
|
|
|
#define NULL ((void *)0)
|
|
|
|
#define WORD_LIST "/sys/games/lib/anawords"
|
|
#define VOWELS "aeiouy"
|
|
#define ALPHAS 26
|
|
#define LENLIMIT 64
|
|
|
|
#define talloc(t) salloc(sizeof (t))
|
|
#define MAP(c) ((c) - 'a')
|
|
|
|
enum
|
|
{
|
|
in_fd,
|
|
out_fd,
|
|
err_fd,
|
|
};
|
|
|
|
typedef struct word word;
|
|
|
|
struct word
|
|
{
|
|
char *text;
|
|
int length;
|
|
ulong mask;
|
|
word *next;
|
|
};
|
|
|
|
typedef word *set[LENLIMIT];
|
|
|
|
typedef struct
|
|
{
|
|
int count[ALPHAS];
|
|
int length;
|
|
ulong mask;
|
|
}
|
|
target;
|
|
|
|
int fixed;
|
|
int maxwords;
|
|
set words;
|
|
word *list[LENLIMIT];
|
|
|
|
void
|
|
error(char *s)
|
|
{
|
|
fprint(err_fd, "fatal error: %s\n", s);
|
|
exits("fatal error");
|
|
}
|
|
|
|
void *
|
|
salloc(ulong z)
|
|
{
|
|
void *p;
|
|
|
|
if ((p = malloc(z)) == NULL)
|
|
error("ran out of memory");
|
|
|
|
return p;
|
|
}
|
|
|
|
void
|
|
clean_word(char *s)
|
|
{
|
|
while (*s && *s != '\n')
|
|
{
|
|
if (isupper(*s))
|
|
*s = tolower(*s);
|
|
|
|
s++;
|
|
}
|
|
if (*s == '\n')
|
|
*s = 0;
|
|
}
|
|
|
|
int
|
|
word_ok(word *w)
|
|
{
|
|
char *s;
|
|
int vowel;
|
|
|
|
if (w->length == 0 || w->length >= LENLIMIT)
|
|
return 0;
|
|
|
|
if (w->length == 1)
|
|
return strchr("aio", w->text[0]) != NULL;
|
|
|
|
vowel = 0;
|
|
s = w->text;
|
|
|
|
while (*s != '\0')
|
|
{
|
|
if (!isalpha(*s))
|
|
return 0;
|
|
|
|
switch (*s)
|
|
{
|
|
case 'a':
|
|
case 'e':
|
|
case 'i':
|
|
case 'o':
|
|
case 'u':
|
|
case 'y':
|
|
vowel = 1;
|
|
}
|
|
|
|
s++;
|
|
}
|
|
|
|
return vowel;
|
|
}
|
|
|
|
ulong
|
|
str_to_mask(char *s)
|
|
{
|
|
ulong m;
|
|
|
|
m = 0;
|
|
|
|
while (*s != '\0')
|
|
m |= 1 << MAP(*s++);
|
|
|
|
return m;
|
|
}
|
|
|
|
word *
|
|
get_word(Biobuf *bp)
|
|
{
|
|
char *s;
|
|
word *w;
|
|
|
|
retry:
|
|
if ((s = Brdline(bp, '\n')) == NULL)
|
|
return NULL;
|
|
|
|
clean_word(s);
|
|
|
|
w = talloc(word);
|
|
w->length = strlen(s);
|
|
w->text = strcpy(salloc(w->length+1), s);
|
|
w->mask = str_to_mask(s);
|
|
|
|
if (!word_ok(w))
|
|
{
|
|
free(w->text);
|
|
free(w);
|
|
goto retry;
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
void
|
|
build_wordlist(void)
|
|
{
|
|
Biobuf *bp;
|
|
word *w;
|
|
word **p;
|
|
|
|
bp = Bopen(WORD_LIST, OREAD);
|
|
if (!bp)
|
|
{
|
|
perror(WORD_LIST);
|
|
exits(WORD_LIST);
|
|
}
|
|
|
|
while ((w = get_word(bp)) != NULL)
|
|
{
|
|
p = &words[w->length];
|
|
w->next = *p;
|
|
*p = w;
|
|
}
|
|
}
|
|
|
|
void
|
|
count_letters(target *t, char *s)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ALPHAS; i++)
|
|
t->count[i] = 0;
|
|
|
|
t->length = 0;
|
|
|
|
while (*s != '\0')
|
|
{
|
|
t->count[MAP(*s++)]++;
|
|
t->length++;
|
|
}
|
|
}
|
|
|
|
int
|
|
contained(word *i, target *t)
|
|
{
|
|
int n;
|
|
char *v;
|
|
target it;
|
|
|
|
if ((i->mask & t->mask) != i->mask)
|
|
return 0;
|
|
|
|
count_letters(&it, i->text);
|
|
|
|
for (n = 0; n < ALPHAS; n++)
|
|
{
|
|
if (it.count[n] > t->count[n])
|
|
return 0;
|
|
}
|
|
|
|
if (it.length == t->length)
|
|
return 1;
|
|
|
|
for (v = VOWELS; *v != '\0'; v++)
|
|
{
|
|
if (t->count[MAP(*v)] > it.count[MAP(*v)])
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
prune(set in, int m, target *filter, set out)
|
|
{
|
|
word *i, *o, *t;
|
|
int n;
|
|
int nz;
|
|
|
|
nz = 0;
|
|
|
|
for (n = 1; n < LENLIMIT; n++)
|
|
{
|
|
if (n > m)
|
|
{
|
|
out[n] = NULL;
|
|
continue;
|
|
}
|
|
|
|
o = NULL;
|
|
|
|
for (i = in[n]; i != NULL; i = i->next)
|
|
{
|
|
if (contained(i, filter))
|
|
{
|
|
t = talloc(word);
|
|
*t = *i;
|
|
t->next = o;
|
|
o = t;
|
|
nz = 1;
|
|
}
|
|
}
|
|
|
|
out[n] = o;
|
|
}
|
|
|
|
return nz;
|
|
}
|
|
|
|
void
|
|
print_set(set s)
|
|
{
|
|
word *w;
|
|
int n;
|
|
|
|
for (n = 1; n < LENLIMIT; n++)
|
|
{
|
|
for (w = s[n]; w != NULL; w = w->next)
|
|
fprint(out_fd, "%s\n", w->text);
|
|
}
|
|
}
|
|
|
|
ulong
|
|
target_mask(int c[])
|
|
{
|
|
ulong m;
|
|
int i;
|
|
|
|
m = 0;
|
|
|
|
for (i = 0; i < ALPHAS; i++)
|
|
{
|
|
if (c[i] != 0)
|
|
m |= 1 << i;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
void
|
|
dump_list(word **e)
|
|
{
|
|
word **p;
|
|
|
|
fprint(out_fd, "%d", (int)(e - list + 1));
|
|
|
|
for (p = list; p <= e; p++)
|
|
fprint(out_fd, " %s", (*p)->text);
|
|
|
|
fprint(out_fd, "\n");
|
|
}
|
|
|
|
void
|
|
free_set(set s)
|
|
{
|
|
int i;
|
|
word *p, *q;
|
|
|
|
for (i = 1; i < LENLIMIT; i++)
|
|
{
|
|
for (p = s[i]; p != NULL; p = q)
|
|
{
|
|
q = p->next;
|
|
free(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
enumerate(word **p, target *i, set c)
|
|
{
|
|
target t;
|
|
set o;
|
|
word *w, *h;
|
|
char *s;
|
|
int l, m, b;
|
|
|
|
l = p - list;
|
|
b = (i->length + (maxwords - l - 1)) / (maxwords - l);
|
|
|
|
for (m = i->length; m >= b; m--)
|
|
{
|
|
h = c[m];
|
|
|
|
for (w = h; w != NULL; w = w->next)
|
|
{
|
|
if (i->length == m)
|
|
{
|
|
*p = w;
|
|
dump_list(p);
|
|
continue;
|
|
}
|
|
|
|
if (l == maxwords - 1)
|
|
continue;
|
|
|
|
t = *i;
|
|
t.length -= m;
|
|
|
|
for (s = w->text; *s != '\0'; s++)
|
|
t.count[MAP(*s)]--;
|
|
|
|
t.mask = target_mask(t.count);
|
|
c[m] = w->next;
|
|
|
|
if (prune(c, m, &t, o))
|
|
{
|
|
*p = w;
|
|
enumerate(p + 1, &t, o);
|
|
free_set(o);
|
|
}
|
|
}
|
|
|
|
c[m] = h;
|
|
}
|
|
}
|
|
|
|
void
|
|
clean(char *s)
|
|
{
|
|
char *p, *q;
|
|
|
|
for (p = s, q = s; *p != '\0'; p++)
|
|
{
|
|
if (islower(*p))
|
|
*q++ = *p;
|
|
else if (isupper(*p))
|
|
*q++ = tolower(*p);
|
|
}
|
|
|
|
*q = '\0';
|
|
}
|
|
|
|
void
|
|
anagramulate(char *s)
|
|
{
|
|
target t;
|
|
set subjects;
|
|
|
|
clean(s);
|
|
|
|
if (fixed)
|
|
maxwords = fixed;
|
|
else if ((maxwords = strlen(s) / 4) < 3)
|
|
maxwords = 3;
|
|
|
|
fprint(out_fd, "%s:\n", s);
|
|
t.mask = str_to_mask(s);
|
|
count_letters(&t, s);
|
|
|
|
if (!prune(words,t.length, &t, subjects))
|
|
return;
|
|
|
|
enumerate(&list[0], &t, subjects);
|
|
}
|
|
|
|
void
|
|
set_fixed(char *s)
|
|
{
|
|
if ((fixed = atoi(s)) < 1)
|
|
fixed = 1;
|
|
}
|
|
|
|
void
|
|
read_words(void)
|
|
{
|
|
char *s;
|
|
Biobuf b;
|
|
|
|
Binit(&b, in_fd, OREAD);
|
|
while ((s = Brdline(&b, '\n')) != NULL)
|
|
{
|
|
s[Blinelen(&b)-1] = '\0';
|
|
if (isdigit(s[0]))
|
|
{
|
|
set_fixed(s);
|
|
fprint(out_fd, "Fixed = %d.\n", fixed);
|
|
}
|
|
else
|
|
{
|
|
anagramulate(s);
|
|
fprint(out_fd, "Done.\n");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
main(int argc, char **argv)
|
|
{
|
|
build_wordlist();
|
|
|
|
if (argc > 1)
|
|
set_fixed(argv[1]);
|
|
|
|
read_words();
|
|
exits(0);
|
|
}
|