DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T c

⟦abff46de3⟧ TextFile

    Length: 8502 (0x2136)
    Types: TextFile
    Names: »compose.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Sonnet/compose.c« 

TextFile

/*
**  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 */