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