say(6) from v10 Research UNIX
Latest update:
The source code for Tenth-Edition Research UNIX (1989) contains the
file games/say.c (timestamp is 1984-04-27, Friday). It's not a
speech synthesiser, but a simple little program that uses a pairs of
"EVERY MAN","HAS A PRICE",
"ANYTHING","GOES"
to generate new wisdom like "Anything has a price." Practically, a LLM
(no).
For some unknown reason, the man page for it is located in the file
man/mana/fortune.6
:
Those old parts are entertaining enough to be read on their own,
e.g.:
"CANDY","IS DANDY BUT LIQUOR IS QUICKER",
"A PLUMP BRIDE","MAKES A FAT WIFE",
"A ROSE BY ANY OTHER NAME","WOULD SMELL AS SWEET",
"TIME AND TIDE","WAITS FOR NO MAN",
The full list (185 pairs) is here. The main()
is somewhat
cryptic, as if somebody was training for the first IOCCC. Although GCC
13 still compiles it, say.c may print the same 'proverb' multiple
times if its argv[1]
is big enough.
I attempted to rewrite the game in a more 'modern' style
in order to address the issue of the 'same proverb' repetition, but
the code didn't become shorter. The relevant part:
char *PAIRS[] = {
"A ROLLING STONE","GATHERS NO MOSS",
"HISTORY","REPEATS ITSELF",
…
};
typedef struct { int left, right; } Proverb;
void mk_proverbs(char **src, int src_len, Proverb *dest) {
int dest_idx = 0;
for (int left = 0; left < src_len; left += 2) {
for (int right = 1; right < src_len; right += 2) {
if (right == left + 1) continue; /* ignore true wisdom */
dest[dest_idx].left = left;
dest[dest_idx].right = right;
dest_idx++;
}
}
}
void shuffle(Proverb *arr, int size) {
for (int i = size - 1; i > 0; i--) {
int j = random() % (i + 1);
Proverb temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
void lowercase(char *dest, char *src, int skip) {
while ( (*dest++ = skip-- < 1 ? tolower(*src++) : *src++))
;
}
void print(char **src, Proverb *proverbs, int slice_len) {
for (int idx = 0; idx < slice_len; ++idx) {
Proverb p = proverbs[idx];
char left[strlen(src[p.left])+1]; lowercase(left, src[p.left], 1);
char right[strlen(src[p.right])+1]; lowercase(right, src[p.right], 0);
printf("%s %s.\n", left, right);
}
}
int main(int argc, char **argv) {
srandom(time(0));
int pairs_len = sizeof(PAIRS) / sizeof(PAIRS[0]);
int templates_len = pairs_len / 2;
int proverbs_len = templates_len * (templates_len - 1); /* max possible */
int n = atoi(argc > 1 ? argv[1] : "1");
if (n < 1 ) n = 1;
if (n > proverbs_len) n = proverbs_len;
Proverb proverbs[proverbs_len];
mk_proverbs(PAIRS, pairs_len, proverbs);
shuffle(proverbs, proverbs_len);
print(PAIRS, proverbs, n);
}
It generates all the possible combinations (34040), shuffles them, and
prints a slice.
$ ./new-say 5
Procrastination is not seemly for a fool.
The lion is the last refuge of the unimaginative.
Life makes waste.
Bad breath doesn't love a wall.
A friend in need is more precious than rubies.
Old vs. new in terms of lines of code:
$ echo say.c new-say.c | xargs -n1 scc | grep ^C | awk '{print $6}'
232
237
Can you make it shorter while still retaining readability?
Tags: ойті
Authors: ag