PYTHON 130
Napogenmo-01.py By cbettis on 31st March 2022 06:09:07 PM
  1. """Script #1 for April, 2022, National Poetry Writing Month (NaPoWriMo) and National Poetry Generating Month (NaPoGenMo)
  2.  
  3. by Basil Cartryte"""
  4. import argparse
  5. import misc_util as util
  6. import secrets
  7. import string
  8. import sys
  9. import time as t
  10.  
  11. SR = None
  12. OUTPATH = ""
  13. CONSONANTS = ['b','c','ch','d','f','g','j','k','l','m','n','p','r','s','sh','t','th','w','y','z']
  14. CWEIGHTS = []
  15. VOWELS = ['a', 'e', 'i', 'o', 'u', 'ai', 'au', 'ea','ee','ei','oa','oo','ou']
  16. VWEIGHTS = []
  17. PATTERNS = ['cv','cvc']
  18. PWEIGHTS = [4,1]
  19. TPUNCT = ['','.','!','?']
  20. TPWEIGHTS = [32,4,2,1]
  21. IPUNCT = ['',',',';',':','--']
  22. IPWEIGHTS = [32,8,4,2,1]
  23. WEIGHT_CHOICES = [1, 2, 3, 4, 5]
  24. WWEIGHTS = [1,3,5,4,2]
  25.  
  26. def configure():
  27.     """Shuffle the global lists of consonants and vowels, and assign random probability weights to each consonant/vowel."""
  28.     global CONSONANTS, CWEIGHTS, VOWELS, VWEIGHTS, WEIGHT_CHOICES, WWEIGHTS, SR
  29.     SR.shuffle(CONSONANTS)
  30.     for k in range(0,len(CONSONANTS)):
  31.         CWEIGHTS.append(SR.choices(WEIGHT_CHOICES,weights=WWEIGHTS)[0])
  32.     SR.shuffle(VOWELS)
  33.     for j in range(0,len(VOWELS)):
  34.         VWEIGHTS.append(SR.choices(WEIGHT_CHOICES,weights=WWEIGHTS)[0])
  35.  
  36. def get_pattern():
  37.     """Return a randomly selected syllable pattern. Currently the only possibilities are cv (consonant + vowel)
  38.    and cvc (consonant + vowel + consonant)."""
  39.     global PATTERNS, PWEIGHTS, SR
  40.     return SR.choices(PATTERNS,weights=PWEIGHTS)[0]
  41.  
  42. def generate_syllable():
  43.     """Generate a random syllable."""
  44.     global CONSONANTS, CWEIGHTS, VOWELS, VWEIGHTS,SR
  45.     pattern = get_pattern()
  46.     syllable = ''
  47.     for c in pattern:
  48.         if c == 'c':
  49.             syllable += SR.choices(CONSONANTS, weights=CWEIGHTS)[0]
  50.         else:
  51.             syllable += SR.choices(VOWELS, weights=VWEIGHTS)[0]
  52.     return syllable
  53.  
  54. def generate_word(min_syllables=1, max_syllables=5, capitalize=False):
  55.     """Generate a random word.
  56.  
  57.    Might also randomly add sentence-terminating punctuation or non-terminating punctuation.
  58.    
  59.    Returns a tuple: (string word[punctuation], int number of syllables, bool capitalize next word).
  60.    
  61.    Yes, that does indicate overly-close coupling. I knocked this out quickly. Just be happy there's documentation!"""
  62.     global SR, TPUNCT
  63.     number_of_syllables = min_syllables
  64.     if min_syllables != max_syllables:
  65.         number_of_syllables = SR.randint(min_syllables, max_syllables)
  66.     ret = ''
  67.     for syl in range(0, number_of_syllables):
  68.         ret += generate_syllable()
  69.     if capitalize:
  70.         ret = string.capwords(ret)
  71.     cap_next = False
  72.     punctuation = ''
  73.     if secrets.randbelow(3) == 0:
  74.         punctuation = get_tpunct()
  75.     else:
  76.         punctuation = get_ipunct()
  77.     if punctuation != '' and punctuation in TPUNCT:
  78.         cap_next = True
  79.     ret += punctuation
  80.     return (ret, number_of_syllables, cap_next)
  81.  
  82. def get_ipunct():
  83.     """Return a random punctuation mark that does NOT indicate the end of a sentence (internal punctuation).
  84.    Will more often than not be an empty string."""
  85.     global IPUNCT, IPWEIGHTS, SR
  86.     return SR.choices(IPUNCT, weights=IPWEIGHTS)[0]
  87.  
  88. def get_tpunct():
  89.     """Return a random punctuation mark that terminates a sentence, or (more often than not) an empty string."""
  90.     global TPUNCT, TPWEIGHTS, SR
  91.     return SR.choices(TPUNCT, weights=TPWEIGHTS)[0]
  92.  
  93. def generate_line(min_syllables=8, max_syllables=12):
  94.     """Generate a line of 'verse.'"""
  95.     global SR, TPUNCT
  96.     number_of_syllables = min_syllables
  97.     if min_syllables != max_syllables:
  98.         number_of_syllables = SR.randint(min_syllables, max_syllables)
  99.     syllable_count = 0
  100.     ret = ''
  101.     cap = True
  102.     while syllable_count < number_of_syllables:
  103.         word = generate_word(min_syllables=1,max_syllables = min(3, number_of_syllables - syllable_count),capitalize=cap)
  104.         ret += word[0]
  105.         syllable_count += word[1]
  106.         cap = word[2]
  107.         if len(ret) > 0 and ret[-1] != '-':
  108.             ret += ' '
  109.     return ret.strip()
  110.    
  111. def generate_lines(line_count, min_syllables=8, max_syllables=12):
  112.     """Generate `line_count` lines of 'verse' that make up a poem. Does not include a title or byline."""
  113.     lines = []
  114.     for k in range(0,line_count):
  115.         lines.append(generate_line(min_syllables, max_syllables))
  116.     return '\n'.join(lines)
  117.  
  118. def generate_poem(min_lines, max_lines, min_syllables=8, max_syllables=12):
  119.     """Generate a gibberish 'poem`. (There's a tiny but non-zero chance that it could make sense in some human language,
  120.    on the 'million monkeys at a million typewriters for a million years' principle.)"""
  121.     global SR, TPUNCT
  122.     number_of_lines = min_lines
  123.     if min_lines != max_lines:
  124.         number_of_lines = SR.randint(min_lines, max_lines)
  125.     poem = generate_lines(number_of_lines, min_syllables, max_syllables)
  126.     if len(poem) > 0 and poem[-1] == '-':
  127.         poem = poem[0:-2]
  128.     if len(poem) > 0 and poem[-1] not in TPUNCT:
  129.         p = ''
  130.         while p == '':
  131.             p = get_tpunct()
  132.         poem += p
  133.     return poem
  134.  
  135. def initialize(args):
  136.     """Initialize global variables."""
  137.     global SR, OUTPATH
  138.     SR = secrets.SystemRandom()
  139.     OUTPATH = util.if_null_or_whitespace(args.outfile, t.strftime('%Y%m%d_%H%M%S.txt'))
  140.     configure()
  141.  
  142. def write_text(text):
  143.     """Write the input text to globally-defined OUTPATH file, overwriting any existing file."""
  144.     global OUTPATH
  145.     with open(OUTPATH,'w') as f:
  146.         f.write(text)
  147.  
  148.  
  149. def main(argv):
  150.     """1. Parse command-line arguments, if any.
  151.    2. Initialize.
  152.    3. Generate a random 'poem.'
  153.    4. Write the poem to a text file."""
  154.     parser = argparse.ArgumentParser()
  155.     parser.add_argument('outfile', help='Path and filename for output text. If not provided, defaults to datetimestamped file in current working directory, e.g. 20220922_140003.txt', default=None)
  156.     parser.add_argument('-i','--isyllablemin', type=int, help='Minimum number of syllables per line; defaults to 8.', default=8)
  157.     parser.add_argument('-x', '--xsyllablemax', type=int, help='Maximum number of syllables per line; defaults to 12.', default=12)
  158.     parser.add_argument('-l', '--lines', type=int, help='Number of lines in the poem to be generated; defaults to 4.', default=4)
  159.     args = parser.parse_args()
  160.     initialize(args)
  161.     text = generate_poem(args.lines, args.lines, args.isyllablemin, args.xsyllablemax)
  162.     write_text(text)
  163.  
  164. if __name__ == "__main__":
  165.     main(sys.argv)

Caracabe Heap is for source code and general debugging text.

Login or Register to edit, delete and keep track of your pastes and more.

Raw Paste

Login or Register to edit or fork this paste. It's free.