|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T c
Length: 8502 (0x2136)
Types: TextFile
Names: »compose.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/General/Sonnet/compose.c«
/*
** COMPOSE
** A routine to write some poetry.
*/
#include "sonnet.h"
#include <sys/types.h>
#ifndef lint
static char RCS[] =
"$Header: compose.c,v 2.0 89/02/08 16:29:51 rsalz Release1 $";
#endif /* lint */
/*
** Load the database. The list has a dummy entry at the end, hence
** the non-standard "-1" in NWORDS.
*/
#include "lex.c"
#define NWORDS (sizeof Words / sizeof Words[0] - 1)
/*
** A couple of magic percentages. Feel free to tweak them.
*/
#define PAD_FACTOR 32 /* For adding a pad word */
#define REV_FACTOR 3 /* For reversing first foot */
/*
** Generate a random number between 0 and n-1. This is stolen from
** hack, markov3, etc., etc., and should be portable. If you change it,
** make sure to read your manual pages carefully, and note that the bottom
** bits are usually not very random.
*/
#define ROLL(n) ((rand() >> 3) % (n))
/*
** Externally-available variables.
*/
LINETYPE *Lines; /* The poem */
int Lcount; /* Number of lines/poem */
int NumBeats; /* Number of beats/line */
/*
** Magic data structures.
*/
static char hendec[] = "01010101010";
static char revdec[] = "10010101010";
static char *OddRhythm[] = {
"0", "1", "01", "10", "11", "10", "01",
"010", "0101", "1010", "01010","1010", "101", "10101"
};
static char *EvenRhythm[] = {
"0", "01", "01", "10", "11", "10", "01",
"010", "0101", "1010", "01010","1010", "101", "10101"
};
\f
/*
** Return TRUE if the letter is a vowel.
*/
static int
IsVowel(c)
char c;
{
switch (c) {
case 'a': case 'e': case 'i': case 'o': case 'u':
case 'A': case 'E': case 'I': case 'O': case 'U':
return TRUE;
}
return FALSE;
}
/*
** Return a small "pad" word.
*/
static char *
GetPad(word)
char *word;
{
register int f;
f = ROLL(100);
if (f > 94)
return "of";
if (f > 83)
return "and";
if (f > 75)
return IsVowel(*word) ? "an" : "a";
if (f > 54)
return "no";
if (f > 53)
return "to";
if (f > 50)
return "with";
if (f > 45)
return IsVowel(*word) ? "in an" : "in a";
if (f > 44)
return IsVowel(*word) ? "for an" : "for a";
if (f > 43)
return IsVowel(*word) ? "by an" : "by a";
if (f > 42)
return IsVowel(*word) ? "to an" : "to a";
if (f > 41)
return IsVowel(*word) ? "with an" : "with a";
if (f > 40)
return "they";
if (f > 38)
return "for";
if (f > 37)
return "we";
if (f > 36)
return "O";
if (f > 35)
return "but";
if (f > 34)
return "thou";
return "the";
}
/*
** Return TRUE if string s ends with substring t. Not robuts, but
** that's okay.
*/
static int
EndsWith(s, t)
char *s;
char *t;
{
return EQ(&s[strlen(s)] - strlen(t), t);
}
/*
** Returns TRUE if ending with word would produce a perfect rhyme.
*/
static int
IsPerfectRhyme(L, word)
LINETYPE *L;
char *word;
{
return L->Line != L->Rhyme && EndsWith(Lines[L->Rhyme].Text, word);
}
/*
** Apend a word to the end of a line.
*/
static void
Append(L, word)
LINETYPE *L;
char *word;
{
if (L->Text[0])
(void)strcat(L->Text, " ");
(void)strcat(L->Text, word);
}
/*
** Get a word that meets our criteria.
*/
static WORDTYPE *
GetWord(L, i, RevOK, exact, syllables, pad)
register LINETYPE *L;
register int i;
int RevOK;
int exact;
int *syllables;
int *pad;
{
register WORDTYPE *W;
register int timeleft;
register int n;
char **RTable;
char *Template;
char *Rhythm;
int len;
int PadOK;
/* Clear the used bit, do some other setup. */
for (W = Words; W < &Words[NWORDS]; W++)
W->Used = FALSE;
PadOK = ROLL(100) < PAD_FACTOR;
RTable = i & 1 ? OddRhythm : EvenRhythm;
/* Search through all the words if we need to. */
for (timeleft = NWORDS; --timeleft >= 0; ) {
/* Find a word we haven't used. */
while (Words[n = ROLL(NWORDS)].Used)
;
W = &Words[n];
W->Used = TRUE;
/* Allow reversed foot if it is not a dactyl. */
Template = RevOK && W->Foot != FT_DF ? revdec : hendec;
len = strlen(RTable[W->Foot]);
if (EQn(&Template[i], RTable[W->Foot], len)) {
*pad = RTable == EvenRhythm && W->Foot == FT_HF;
*syllables = len;
if (*pad == FALSE || PadOK) {
if (i + len < NumBeats)
/* This isn't the last word, so don't check the rhyme. */
return W;
/* Filter out feminine rhymes if at end of stanza. */
Rhythm = RTable[W->Foot];
if (((L->Active % 2) == 0 || L->Active == 13)
&& Rhythm[strlen(Rhythm) - 1] == '0')
continue;
if (L->vowel == '\0' || L->cons == '\0') {
L->vowel = W->vowel;
L->cons = W->cons;
}
if (L->vowel == W->vowel
&& (!exact || L->cons == W->cons)
&& !IsPerfectRhyme(L, W->Text))
return W;
}
}
}
return NULL;
}
\f
/*
** Compose a line of poetry.
*/
ComposeLine(L)
register LINETYPE *L;
{
register WORDTYPE *W;
register LINETYPE *Rhymer;
int RevOK;
int exact;
int i;
int pad;
int syllables;
if (L->Marked)
/* User doesn't want this line touched. */
return;
if (L->Rhyme < 0) {
/* A blank filler line. */
L->Text[0] = '\0';
return;
}
/* Zap the text. */
L->Text[0] = '\0';
/* Coordinate rhymes. */
if (L->Rhyme == L->Line) {
/* If the second line with this rhyme has been saved, make the first
* one agree with it. */
for (Rhymer = L + 1; Rhymer < &Lines[Lcount]; Rhymer++)
if (Rhymer->Rhyme == L->Rhyme)
break;
if (Rhymer < &Lines[Lcount] && Rhymer->Marked) {
L->vowel = Rhymer->vowel;
L->cons = Rhymer->cons;
}
else {
L->vowel = 0;
L->cons = 0;
}
}
else {
L->vowel = Lines[L->Rhyme].vowel;
L->cons = Lines[L->Rhyme].cons;
}
/* Get enough syllables, try for exact match for time through. */
for (exact = TRUE, i = 0; i < NumBeats; ) {
pad = 0;
RevOK = i == 0
&& ROLL(100) < REV_FACTOR * ((L->Active - 1) % 4 == 0 ? 3 : 1);
if (W = GetWord(L, i, RevOK, exact, &syllables, &pad)) {
if (pad)
Append(L, GetPad(W->Text));
Append(L, W->Text);
i += syllables;
}
else {
/* Couldn't find anything, try again. */
i = 0;
exact = FALSE;
L->Text[0] = '\0';
}
}
/* Make line start with a capital letter. */
if (islower(L->Text[0]))
L->Text[0] = toupper(L->Text[0]);
}
/*
** Set the desired rhyme scheme of a line. These is the only time we
** loop through the whole word list, and this routine is only called at
** start-up, so it just doesn't seem worth keeping the list sorted and
** using a binary search.
*/
SetRhyme(L)
register LINETYPE *L;
{
register WORDTYPE *W;
register char *p;
register char *q;
register int i;
if (L->Text[0] == '\0')
return;
/* Find last word, see if it's in our list. */
p = RDX(L->Text, ' ') + 1;
for (W = Words, i = NWORDS; --i >= 0; W++)
if (EQ(W->Text, p)) {
L->vowel = W->vowel;
L->cons = W->cons;
return;
}
/* Could be a two-word word, add in second-to-last word and search. */
q = --p;
*p = '\0';
p = RDX(L->Text, ' ') + 1;
*q = ' ';
for (W = Words, i = NWORDS; --i >= 0; W++)
if (EQ(W->Text, p)) {
L->vowel = W->vowel;
L->cons = W->cons;
return;
}
/* Can't happen, unless fed a poem that we didn't write. */
Printf("WORD %s NOT FOUND", p);
abort();
}
/*
** Set up the rhyme pattern, seed the generator, etc.
*/
ComposeInit(pattern, beats)
register char *pattern;
int beats;
{
register LINETYPE *L;
register int i;
register int j;
register int Active;
/* NOSTRICT "warning: long assignment may lose accuracy" */
(void)srand((int)time((time_t *)NULL));
if (pattern == NULL)
pattern = "ABAB CDCD EFEF GG";
NumBeats = beats ? beats : 10;
Lcount = strlen(pattern);
/* NOSTRICT "warning: possible pointer alignment problem" */
Lines = (LINETYPE *)malloc((unsigned int)Lcount * sizeof Lines[0]);
for (Active = 0, L = Lines, i = 0; i < Lcount; i++, L++) {
L->Text[0] = '\0';
L->Line = i;
if (pattern[i] == ' ')
L->Rhyme = -1;
else
for (L->Active = ++Active, j = 0; j < Lcount; j++)
if (pattern[i] == pattern[j]) {
L->Rhyme = j;
break;
}
}
}
#ifdef STANDALONE
main()
{
register LINETYPE *L;
register int i;
ComposeInit((char *)NULL, 0);
for (L = Lines, i = Lcount; --i >= 0; L++) {
ComposeLine(L);
Printf("%s\n", L->Text);
}
exit(0);
}
#endif /* STANDALONE */