#!/usr/bin/env python from itertools import batched from random import choice from re import compile, sub, split, match from evennia.utils import logger, dedent def paragraph(text): """ Removes initial spaces as well as final newlines. Blank lines, however, are preserved. """ # Shame we can chain this or _thread_, but: return sub(r";;", "\n\n", sub(r"\n", " ", sub(r"\n\n+", ";;", dedent(text)))).strip() def fix_msg(text): if text and isinstance(text, str) and len(text) > 0: return capitalize_line(text) elif text and isinstance(text, tuple): return (capitalize_line(text[0]), text[1]) else: return text def capitalize_line(line): """ Return a capitalized version of 'line'. """ # logger.info(f"Fixing {line}") if not line or line.strip() == "": return line elif match(r"^\|[bm][a-z].*", line): return "The " + line elif match(r"^\|[A-z][a-z].*", line): return line[0:2] + line[2].upper() + line[3:] else: return line[0].upper() + line[1:] def squish(text): "Remove series of spaces from the text." no_start = sub(r"\( +", "(", text) no_after = sub(r" +\)", ")", no_start) no_inside = sub(r"\" *(.*?) *\"", "\"\\1\"", no_after) return sub('[ \t]+', ' ', no_inside).strip() def _routput_choose(text): """ Pick a choice when text is: one ^ two ^ three Done by splitting the text, and calling random.choice(). """ choices = split(r" *\^ *", text) return choice(choices) def _routput_empty(entry): """ Return True if entry is empty, e.g. None or blank. False otherwise. Note: Spaces are ignored. """ if entry: if isinstance(entry, str) and entry.strip() == '': return True if isinstance(entry, (list, tuple)) and len(entry) == 0: return True return False return True def _routput_pair(no_choice, choices = None): """ Return a list based on the ^-separated options in 'choices'. Give a pair of split items, the 'no_choice' is the first section (before the << ... >>>), and the 'choices' is the section between the '<< ... >>' delimiter. """ # While unlikely, the text before << and the options between the # << ...>> text may be blank: if _routput_empty(no_choice) and _routput_empty(choices): return '' # If we start a string with <<...>>, then thte 'no_choice' option # is blank, so we only return a choice: if _routput_empty(no_choice): return _routput_choose(choices) # With text before, but not inside the <<...>> section, we just # return the first part: if _routput_empty(choices): return no_choice return no_choice + ' ' + _routput_choose(choices) def routput(text, *substitutions): """ Return string with internal word choices replaced randomly. For instance, the string: 'This feels <<^ very ^ quite>> <>.' Could return any of the following strings: 'This feels quite nice.' 'This feels cozy.' 'This feels very comfortable.' """ if text: # section is a list of some phrase followed by another phrase # that contains ^-separate choices: sections = split(r" *<< *(.*?) *>> *", text) # Parts are the first section followed by the _rendered_ # choices, so this^that is either 'this' or 'that': parts = [_routput_pair(*pair) for pair in batched(sections, 2)] # The parts may have empty strings, so we filter those out, # leaving on the phrases to join: phrases = [phrase for phrase in parts if not _routput_empty(phrase)] if substitutions: proposal = ' '.join(phrases).format(*substitutions) else: proposal = ' '.join(phrases) # If a choice is at the end of a sentence, and we could have # "no choice", as in: # "He searches <>." # We don't want a version that looks like: # "He searches # ." # with a space before the punctuation: # # Also, we should remove all double spaces that may show: return squish(sub(r"\s+([!?.,])", "\\1", proposal)) def choices(text, *substitutions): """ Returns a section of 'text' based on separating semicolons. Note that text can already be separated as a list or tuple. """ if isinstance(text, str): text = split(r"\s*;;\s*", text) if text: return routput(choice(text), *substitutions) def pluralize(noun): """Convert a singular noun to its plural form.""" # Basic pluralization rules # Change 'y' to 'ies' if preceded by a consonant if noun.endswith('y') and noun[-2] not in 'aeiou': return noun[:-1] + 'ies' # Add 'es' for words ending in 's', 'x', 'z', 'ch', or 'sh' elif noun.endswith('s') or noun.endswith('x') or \ noun.endswith('z') or noun.endswith('ch') or \ noun.endswith('sh'): return noun + 'es' # Default case for regular nouns else: return noun + 's' # print(pluralize("dog")) # Outputs: dogs # print(pluralize("candy")) # Outputs: candies # print(pluralize("box")) # Outputs: boxes # print(pluralize("bush")) # Outputs: bushes # print(pluralize("city")) # Outputs: cities