diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/games/ana.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/games/ana.c')
-rwxr-xr-x | sys/src/games/ana.c | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/sys/src/games/ana.c b/sys/src/games/ana.c new file mode 100755 index 000000000..90f753284 --- /dev/null +++ b/sys/src/games/ana.c @@ -0,0 +1,451 @@ +#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); +} |