initial import
This commit is contained in:
commit
45f4d6e401
24
LICENSE
Normal file
24
LICENSE
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <https://unlicense.org>
|
129
README.md
Normal file
129
README.md
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
# D&D Language Generator
|
||||||
|
|
||||||
|
This package is a fantasy language generator. By defining a number of characteristics about your imagined language -- the graphemes, their relative frequency distributions, the construction of syllables, and so on -- you can generate random but internally consistent gibberish that feels distinct, evocative, and appropriate to your setting.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```
|
||||||
|
>>> from language imported supported_languages
|
||||||
|
>>> common = supported_languages['common']
|
||||||
|
>>> common.word(2)
|
||||||
|
['apsoo', 'nirtoet']
|
||||||
|
>>> common.text()
|
||||||
|
Proitsiiiy be itkif eesof detytaen. Ojaot tyskuaz apsoo nirtoet prenao.
|
||||||
|
>>> commoner = f"{common.Name}"
|
||||||
|
"Quiet" Gushi Murk Lirpusome
|
||||||
|
>>> common.Name.name()
|
||||||
|
{
|
||||||
|
name: ['Gushi', 'Murk'],
|
||||||
|
surname: ['Lirpusome'],
|
||||||
|
adjective: ["Quiet"],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported Languages
|
||||||
|
|
||||||
|
A number of D&D languages are defined, with rules built according to the
|
||||||
|
conventions established by my D&D group over several years of play in our
|
||||||
|
homebrew setting. You can find all supported languages [in the languages
|
||||||
|
submodule](language/languages/); each submodule contains a README that
|
||||||
|
describes the basic characteristics of the language, along with examples.
|
||||||
|
|
||||||
|
## Defining a Language
|
||||||
|
|
||||||
|
### Layout
|
||||||
|
|
||||||
|
A language consists of several submodules:
|
||||||
|
|
||||||
|
* `base.py`, which contains grapheme definitions and the `Language` subclasses;
|
||||||
|
* `names.py`, which defines the `NameGenerator` subclasses; and
|
||||||
|
* `rules.py`, which is optional but defines the rules all words in the language must follow.
|
||||||
|
|
||||||
|
|
||||||
|
### Language Construction
|
||||||
|
|
||||||
|
Let's look at a simple example, the Gnomish language. Here's the `Language`
|
||||||
|
subclass that would be defined in `base.py`:
|
||||||
|
|
||||||
|
```
|
||||||
|
from language import defaults, types
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
consonants = types.WeightedSet(
|
||||||
|
("b", 0.5), ("d", 0.5), ("f", 0.3), ("g", 0.3),
|
||||||
|
("h", 0.5), ("j", 0.2), ("l", 1.0), ("m", 0.5),
|
||||||
|
("n", 1.0), ("p", 0.5), ("r", 1.0), ("s", 1.0),
|
||||||
|
("t", 1.0), ("v", 0.3), ("w", 0.2), ("z", 0.1),
|
||||||
|
)
|
||||||
|
|
||||||
|
suffixes = types.equal_weights(["a", "e", "i", "o", "y"], 1.0, blank=False)
|
||||||
|
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="gnomish",
|
||||||
|
vowels=defaults.vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=suffixes,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="consonant,vowel,vowel"), 1.0),
|
||||||
|
(types.Syllable(template="consonant,vowel"), 0.33),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=2,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Defining Graphemes
|
||||||
|
|
||||||
|
A Language definition includes *graphemes*, the basic building blocks of any
|
||||||
|
language. We start with **vowels**, **consonants**, which are required in every
|
||||||
|
language; Gnomish also includes **suffixes**, but no **prefixes**. Each
|
||||||
|
grapheme is a `WeightedSet`, which is like a regular set except its members
|
||||||
|
consist of a tuple of a string and a relative weight from 0.0 to 1.0. These
|
||||||
|
weights will be used when selecting a random grapheme.
|
||||||
|
|
||||||
|
Gnomish uses the default vowels, defined in [the
|
||||||
|
language.defaults](language/defaults.py) submodule, but define our consonants
|
||||||
|
with a subset of English consonants. By experimenting with different sets and
|
||||||
|
different weights, you can generate radically different feeling text output!
|
||||||
|
|
||||||
|
Gnomish also uses suffixes, which are graphemes added to the ends of words.
|
||||||
|
Here we use the helper function `types.equal_weights()`, which returns
|
||||||
|
a `WeightedSet` where each member is given the same weight. Normally this
|
||||||
|
function also inserts the grapheme `("", 1.0)` into the set, but we disable
|
||||||
|
this behaviour by specifying `blank=False`.
|
||||||
|
|
||||||
|
#### Defining Syllables
|
||||||
|
|
||||||
|
A syllable is a collection of graphemes, including at least one vowel. When we
|
||||||
|
create words, we select a random syllable template from a `SyllableSet`, which
|
||||||
|
is just a `WeightedSet` whose members are `Syllable` instances. Each `Syllable`
|
||||||
|
declares a `template`, and like graphemes, has a weight associated with it that
|
||||||
|
will make it more or less likely to be chosen for a word.
|
||||||
|
|
||||||
|
A syllable's template consists of a comma-separated string of grapheme names.
|
||||||
|
In Gnomish, we have two possible syllable templates, `consonant,vowel,vowel`
|
||||||
|
and the shorter `consonant,vowel`, which will be selected one third as often.
|
||||||
|
|
||||||
|
Templates can also support randomly-selected graphemes by joining two or more
|
||||||
|
grapheme types with a vertical bar, for example `vowel|consonant` would choose
|
||||||
|
one or the other; `vowel|consonant,vowel` would result in a vowel or
|
||||||
|
a consonant followed by a vowel.
|
||||||
|
|
||||||
|
### How Words Are Constructed:
|
||||||
|
|
||||||
|
The main interface for callers is word(), which returns a randomly-generated
|
||||||
|
word in the language according to the following algorithm:
|
||||||
|
|
||||||
|
1. Choose a random syllable from the syllable set
|
||||||
|
2. For each grapheme in the syllable
|
||||||
|
3. Choose a random grapheme template
|
||||||
|
4. Choose a random sequence from the language for that grapheme
|
||||||
|
5. Validate the word against the language rules
|
||||||
|
6. Repeat 1-5 until a valid word is generated
|
||||||
|
7. Add a prefix and suffix, if they are defined
|
||||||
|
|
||||||
|
When graphemes are chosen, the following rules are applied:
|
||||||
|
* Every syllable must have at least one vowel; and
|
||||||
|
* A syllable may never have three consecutive consonants.
|
1
language/__init__.py
Normal file
1
language/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from .languages import supported_languages
|
72
language/cli.py
Normal file
72
language/cli.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from enum import Enum
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
|
|
||||||
|
import typer
|
||||||
|
from rich.logging import RichHandler
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.markdown import Markdown
|
||||||
|
|
||||||
|
from language import supported_languages
|
||||||
|
|
||||||
|
app = typer.Typer()
|
||||||
|
|
||||||
|
app_state = {}
|
||||||
|
|
||||||
|
Supported = Enum("Supported", ((k, k) for k in supported_languages.keys()))
|
||||||
|
|
||||||
|
|
||||||
|
def print_sample(lang: str, module: ModuleType) -> None:
|
||||||
|
summary = module.__doc__.replace("\n", " \n")
|
||||||
|
print(f"{summary}")
|
||||||
|
print(f" Text: {module.Language.text(10)}")
|
||||||
|
print(f" Names: {module.Name}")
|
||||||
|
print(f" {module.Name}")
|
||||||
|
print(f" {module.Name}")
|
||||||
|
if module.NobleName != module.Name:
|
||||||
|
print(f" Noble Names: {module.NobleName}")
|
||||||
|
print(f" {module.NobleName}")
|
||||||
|
print(f" {module.NobleName}")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
@app.callback()
|
||||||
|
def main(
|
||||||
|
language: Supported = typer.Option("common", help="The language to use."),
|
||||||
|
):
|
||||||
|
app_state["language"] = supported_languages[language.name]
|
||||||
|
|
||||||
|
debug = os.getenv("DEBUG", None)
|
||||||
|
logging.basicConfig(
|
||||||
|
format="%(message)s",
|
||||||
|
level=logging.DEBUG if debug else logging.INFO,
|
||||||
|
handlers=[RichHandler(rich_tracebacks=True, tracebacks_suppress=[typer])],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def words(count: int = typer.Option(50, help="The number of words to generate.")):
|
||||||
|
print(" ".join(list(app_state["language"].Language.word(count))))
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def names(
|
||||||
|
count: int = typer.Option(50, help="The number of names to generate."),
|
||||||
|
noble: bool = typer.Option(False, help="Generate noble names."),
|
||||||
|
):
|
||||||
|
generator = app_state["language"].Name if not noble else app_state["language"].NobleName
|
||||||
|
for name in generator.name(count):
|
||||||
|
print(name["fullname"])
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def list():
|
||||||
|
console = Console(width=80)
|
||||||
|
for lang, module in supported_languages.items():
|
||||||
|
console.print(Markdown(module.__doc__))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app()
|
864
language/defaults.py
Normal file
864
language/defaults.py
Normal file
|
@ -0,0 +1,864 @@
|
||||||
|
from language import types
|
||||||
|
|
||||||
|
vowels = types.equal_weights(["a", "e", "i", "o", "u"], 1.0)
|
||||||
|
|
||||||
|
consonants = types.equal_weights(
|
||||||
|
["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"], 1.0
|
||||||
|
)
|
||||||
|
|
||||||
|
adjectives = types.equal_weights(
|
||||||
|
["big", "tiny", "wild", "crazy", "quiet", "red", "black", "blue", "sick", "dancing", "jumping"], 0.05
|
||||||
|
)
|
||||||
|
|
||||||
|
titles = types.equal_weights(["sir", "dame", "capt.", "sgt.", "miss", "mrs.", "mr.", "dr."], 0.01) + types.WeightedSet(
|
||||||
|
("lord", 0.05), ("lady", 0.05)
|
||||||
|
)
|
||||||
|
|
||||||
|
counts = types.equal_weights(["II", "III", "IV", "V", "VI", "VII", "VIII", "IX"], 0.01)
|
||||||
|
|
||||||
|
|
||||||
|
# source: http://ideonomy.mit.edu/essays/traits.html
|
||||||
|
personality = types.equal_weights(
|
||||||
|
[
|
||||||
|
"Accessible",
|
||||||
|
"Active",
|
||||||
|
"Adaptable",
|
||||||
|
"Admirable",
|
||||||
|
"Adventurous",
|
||||||
|
"Agreeable",
|
||||||
|
"Alert",
|
||||||
|
"Allocentric",
|
||||||
|
"Amiable",
|
||||||
|
"Anticipative",
|
||||||
|
"Appreciative",
|
||||||
|
"Articulate",
|
||||||
|
"Aspiring",
|
||||||
|
"Athletic",
|
||||||
|
"Attractive",
|
||||||
|
"Balanced",
|
||||||
|
"Benevolent",
|
||||||
|
"Brilliant",
|
||||||
|
"Calm",
|
||||||
|
"Capable",
|
||||||
|
"Captivating",
|
||||||
|
"Caring",
|
||||||
|
"Challenging",
|
||||||
|
"Charismatic",
|
||||||
|
"Charming",
|
||||||
|
"Cheerful",
|
||||||
|
"Clean",
|
||||||
|
"Clear-headed",
|
||||||
|
"Clever",
|
||||||
|
"Colorful",
|
||||||
|
"Companionly",
|
||||||
|
"Compassionate",
|
||||||
|
"Conciliatory",
|
||||||
|
"Confident",
|
||||||
|
"Conscientious",
|
||||||
|
"Considerate",
|
||||||
|
"Constant",
|
||||||
|
"Contemplative",
|
||||||
|
"Cooperative",
|
||||||
|
"Courageous",
|
||||||
|
"Courteous",
|
||||||
|
"Creative",
|
||||||
|
"Cultured",
|
||||||
|
"Curious",
|
||||||
|
"Daring",
|
||||||
|
"Debonair",
|
||||||
|
"Decent",
|
||||||
|
"Decisive",
|
||||||
|
"Dedicated",
|
||||||
|
"Deep",
|
||||||
|
"Dignified",
|
||||||
|
"Directed",
|
||||||
|
"Disciplined",
|
||||||
|
"Discreet",
|
||||||
|
"Dramatic",
|
||||||
|
"Dutiful",
|
||||||
|
"Dynamic",
|
||||||
|
"Earnest",
|
||||||
|
"Ebullient",
|
||||||
|
"Educated",
|
||||||
|
"Efficient",
|
||||||
|
"Elegant",
|
||||||
|
"Eloquent",
|
||||||
|
"Empathetic",
|
||||||
|
"Energetic",
|
||||||
|
"Enthusiastic",
|
||||||
|
"Esthetic",
|
||||||
|
"Exciting",
|
||||||
|
"Extraordinary",
|
||||||
|
"Fair",
|
||||||
|
"Faithful",
|
||||||
|
"Farsighted",
|
||||||
|
"Felicific",
|
||||||
|
"Firm",
|
||||||
|
"Flexible",
|
||||||
|
"Focused",
|
||||||
|
"Forecful",
|
||||||
|
"Forgiving",
|
||||||
|
"Forthright",
|
||||||
|
"Freethinking",
|
||||||
|
"Friendly",
|
||||||
|
"Fun-loving",
|
||||||
|
"Gallant",
|
||||||
|
"Generous",
|
||||||
|
"Gentle",
|
||||||
|
"Genuine",
|
||||||
|
"Good-natured",
|
||||||
|
"Gracious",
|
||||||
|
"Hardworking",
|
||||||
|
"Healthy",
|
||||||
|
"Hearty",
|
||||||
|
"Helpful",
|
||||||
|
"Herioc",
|
||||||
|
"High-minded",
|
||||||
|
"Honest",
|
||||||
|
"Honorable",
|
||||||
|
"Humble",
|
||||||
|
"Humorous",
|
||||||
|
"Idealistic",
|
||||||
|
"Imaginative",
|
||||||
|
"Impressive",
|
||||||
|
"Incisive",
|
||||||
|
"Incorruptible",
|
||||||
|
"Independent",
|
||||||
|
"Individualistic",
|
||||||
|
"Innovative",
|
||||||
|
"Inoffensive",
|
||||||
|
"Insightful",
|
||||||
|
"Insouciant",
|
||||||
|
"Intelligent",
|
||||||
|
"Intuitive",
|
||||||
|
"Invulnerable",
|
||||||
|
"Kind",
|
||||||
|
"Knowledge",
|
||||||
|
"Leaderly",
|
||||||
|
"Leisurely",
|
||||||
|
"Liberal",
|
||||||
|
"Logical",
|
||||||
|
"Lovable",
|
||||||
|
"Loyal",
|
||||||
|
"Lyrical",
|
||||||
|
"Magnanimous",
|
||||||
|
"Many-sided",
|
||||||
|
"Masculine",
|
||||||
|
"Manly",
|
||||||
|
"Mature",
|
||||||
|
"Methodical",
|
||||||
|
"Maticulous",
|
||||||
|
"Moderate",
|
||||||
|
"Modest",
|
||||||
|
"Multi-leveled",
|
||||||
|
"Neat",
|
||||||
|
"Nonauthoritarian",
|
||||||
|
"Objective",
|
||||||
|
"Observant",
|
||||||
|
"Open",
|
||||||
|
"Optimistic",
|
||||||
|
"Orderly",
|
||||||
|
"Organized",
|
||||||
|
"Original",
|
||||||
|
"Painstaking",
|
||||||
|
"Passionate",
|
||||||
|
"Patient",
|
||||||
|
"Patriotic",
|
||||||
|
"Peaceful",
|
||||||
|
"Perceptive",
|
||||||
|
"Perfectionist",
|
||||||
|
"Personable",
|
||||||
|
"Persuasive",
|
||||||
|
"Planful",
|
||||||
|
"Playful",
|
||||||
|
"Polished",
|
||||||
|
"Popular",
|
||||||
|
"Practical",
|
||||||
|
"Precise",
|
||||||
|
"Principled",
|
||||||
|
"Profound",
|
||||||
|
"Protean",
|
||||||
|
"Protective",
|
||||||
|
"Providential",
|
||||||
|
"Prudent",
|
||||||
|
"Punctual",
|
||||||
|
"Pruposeful",
|
||||||
|
"Rational",
|
||||||
|
"Realistic",
|
||||||
|
"Reflective",
|
||||||
|
"Relaxed",
|
||||||
|
"Reliable",
|
||||||
|
"Resourceful",
|
||||||
|
"Respectful",
|
||||||
|
"Responsible",
|
||||||
|
"Responsive",
|
||||||
|
"Reverential",
|
||||||
|
"Romantic",
|
||||||
|
"Rustic",
|
||||||
|
"Sage",
|
||||||
|
"Sane",
|
||||||
|
"Scholarly",
|
||||||
|
"Scrupulous",
|
||||||
|
"Secure",
|
||||||
|
"Selfless",
|
||||||
|
"Self-critical",
|
||||||
|
"Self-defacing",
|
||||||
|
"Self-denying",
|
||||||
|
"Self-reliant",
|
||||||
|
"Self-sufficent",
|
||||||
|
"Sensitive",
|
||||||
|
"Sentimental",
|
||||||
|
"Seraphic",
|
||||||
|
"Serious",
|
||||||
|
"Sexy",
|
||||||
|
"Sharing",
|
||||||
|
"Shrewd",
|
||||||
|
"Simple",
|
||||||
|
"Skillful",
|
||||||
|
"Sober",
|
||||||
|
"Sociable",
|
||||||
|
"Solid",
|
||||||
|
"Sophisticated",
|
||||||
|
"Spontaneous",
|
||||||
|
"Sporting",
|
||||||
|
"Stable",
|
||||||
|
"Steadfast",
|
||||||
|
"Steady",
|
||||||
|
"Stoic",
|
||||||
|
"Strong",
|
||||||
|
"Studious",
|
||||||
|
"Suave",
|
||||||
|
"Subtle",
|
||||||
|
"Sweet",
|
||||||
|
"Sympathetic",
|
||||||
|
"Systematic",
|
||||||
|
"Tasteful",
|
||||||
|
"Teacherly",
|
||||||
|
"Thorough",
|
||||||
|
"Tidy",
|
||||||
|
"Tolerant",
|
||||||
|
"Tractable",
|
||||||
|
"Trusting",
|
||||||
|
"Uncomplaining",
|
||||||
|
"Understanding",
|
||||||
|
"Undogmatic",
|
||||||
|
"Unfoolable",
|
||||||
|
"Upright",
|
||||||
|
"Urbane",
|
||||||
|
"Venturesome",
|
||||||
|
"Vivacious",
|
||||||
|
"Warm",
|
||||||
|
"Well-bred",
|
||||||
|
"Well-read",
|
||||||
|
"Well-rounded",
|
||||||
|
"Winning",
|
||||||
|
"Wise",
|
||||||
|
"Witty",
|
||||||
|
"Youthful",
|
||||||
|
"Absentminded",
|
||||||
|
"Aggressive",
|
||||||
|
"Ambitious",
|
||||||
|
"Amusing",
|
||||||
|
"Artful",
|
||||||
|
"Ascetic",
|
||||||
|
"Authoritarian",
|
||||||
|
"Big-thinking",
|
||||||
|
"Boyish",
|
||||||
|
"Breezy",
|
||||||
|
"Businesslike",
|
||||||
|
"Busy",
|
||||||
|
"Casual",
|
||||||
|
"Crebral",
|
||||||
|
"Chummy",
|
||||||
|
"Circumspect",
|
||||||
|
"Competitive",
|
||||||
|
"Complex",
|
||||||
|
"Confidential",
|
||||||
|
"Conservative",
|
||||||
|
"Contradictory",
|
||||||
|
"Crisp",
|
||||||
|
"Cute",
|
||||||
|
"Deceptive",
|
||||||
|
"Determined",
|
||||||
|
"Dominating",
|
||||||
|
"Dreamy",
|
||||||
|
"Driving",
|
||||||
|
"Droll",
|
||||||
|
"Dry",
|
||||||
|
"Earthy",
|
||||||
|
"Effeminate",
|
||||||
|
"Emotional",
|
||||||
|
"Enigmatic",
|
||||||
|
"Experimental",
|
||||||
|
"Familial",
|
||||||
|
"Folksy",
|
||||||
|
"Formal",
|
||||||
|
"Freewheeling",
|
||||||
|
"Frugal",
|
||||||
|
"Glamorous",
|
||||||
|
"Guileless",
|
||||||
|
"High-spirited",
|
||||||
|
"Huried",
|
||||||
|
"Hypnotic",
|
||||||
|
"Iconoclastic",
|
||||||
|
"Idiosyncratic",
|
||||||
|
"Impassive",
|
||||||
|
"Impersonal",
|
||||||
|
"Impressionable",
|
||||||
|
"Intense",
|
||||||
|
"Invisible",
|
||||||
|
"Irreligious",
|
||||||
|
"Irreverent",
|
||||||
|
"Maternal",
|
||||||
|
"Mellow",
|
||||||
|
"Modern",
|
||||||
|
"Moralistic",
|
||||||
|
"Mystical",
|
||||||
|
"Neutral",
|
||||||
|
"Noncommittal",
|
||||||
|
"Noncompetitive",
|
||||||
|
"Obedient",
|
||||||
|
"Old-fashined",
|
||||||
|
"Ordinary",
|
||||||
|
"Outspoken",
|
||||||
|
"Paternalistic",
|
||||||
|
"Physical",
|
||||||
|
"Placid",
|
||||||
|
"Political",
|
||||||
|
"Predictable",
|
||||||
|
"Preoccupied",
|
||||||
|
"Private",
|
||||||
|
"Progressive",
|
||||||
|
"Proud",
|
||||||
|
"Pure",
|
||||||
|
"Questioning",
|
||||||
|
"Quiet",
|
||||||
|
"Religious",
|
||||||
|
"Reserved",
|
||||||
|
"Restrained",
|
||||||
|
"Retiring",
|
||||||
|
"Sarcastic",
|
||||||
|
"Self-conscious",
|
||||||
|
"Sensual",
|
||||||
|
"Skeptical",
|
||||||
|
"Smooth",
|
||||||
|
"Soft",
|
||||||
|
"Solemn",
|
||||||
|
"Solitary",
|
||||||
|
"Stern",
|
||||||
|
"Stoiid",
|
||||||
|
"Strict",
|
||||||
|
"Stubborn",
|
||||||
|
"Stylish",
|
||||||
|
"Subjective",
|
||||||
|
"Surprising",
|
||||||
|
"Soft",
|
||||||
|
"Tough",
|
||||||
|
"Unaggressive",
|
||||||
|
"Unambitious",
|
||||||
|
"Unceremonious",
|
||||||
|
"Unchanging",
|
||||||
|
"Undemanding",
|
||||||
|
"Unfathomable",
|
||||||
|
"Unhurried",
|
||||||
|
"Uninhibited",
|
||||||
|
"Unpatriotic",
|
||||||
|
"Unpredicatable",
|
||||||
|
"Unreligious",
|
||||||
|
"Unsentimental",
|
||||||
|
"Whimsical",
|
||||||
|
"Abrasive",
|
||||||
|
"Abrupt",
|
||||||
|
"Agonizing",
|
||||||
|
"Aimless",
|
||||||
|
"Airy",
|
||||||
|
"Aloof",
|
||||||
|
"Amoral",
|
||||||
|
"Angry",
|
||||||
|
"Anxious",
|
||||||
|
"Apathetic",
|
||||||
|
"Arbitrary",
|
||||||
|
"Argumentative",
|
||||||
|
"Arrogantt",
|
||||||
|
"Artificial",
|
||||||
|
"Asocial",
|
||||||
|
"Assertive",
|
||||||
|
"Astigmatic",
|
||||||
|
"Barbaric",
|
||||||
|
"Bewildered",
|
||||||
|
"Bizarre",
|
||||||
|
"Bland",
|
||||||
|
"Blunt",
|
||||||
|
"Biosterous",
|
||||||
|
"Brittle",
|
||||||
|
"Brutal",
|
||||||
|
"Calculating",
|
||||||
|
"Callous",
|
||||||
|
"Cantakerous",
|
||||||
|
"Careless",
|
||||||
|
"Cautious",
|
||||||
|
"Charmless",
|
||||||
|
"Childish",
|
||||||
|
"Clumsy",
|
||||||
|
"Coarse",
|
||||||
|
"Cold",
|
||||||
|
"Colorless",
|
||||||
|
"Complacent",
|
||||||
|
"Complaintive",
|
||||||
|
"Compulsive",
|
||||||
|
"Conceited",
|
||||||
|
"Condemnatory",
|
||||||
|
"Conformist",
|
||||||
|
"Confused",
|
||||||
|
"Contemptible",
|
||||||
|
"Conventional",
|
||||||
|
"Cowardly",
|
||||||
|
"Crafty",
|
||||||
|
"Crass",
|
||||||
|
"Crazy",
|
||||||
|
"Criminal",
|
||||||
|
"Critical",
|
||||||
|
"Crude",
|
||||||
|
"Cruel",
|
||||||
|
"Cynical",
|
||||||
|
"Decadent",
|
||||||
|
"Deceitful",
|
||||||
|
"Delicate",
|
||||||
|
"Demanding",
|
||||||
|
"Dependent",
|
||||||
|
"Desperate",
|
||||||
|
"Destructive",
|
||||||
|
"Devious",
|
||||||
|
"Difficult",
|
||||||
|
"Dirty",
|
||||||
|
"Disconcerting",
|
||||||
|
"Discontented",
|
||||||
|
"Discouraging",
|
||||||
|
"Discourteous",
|
||||||
|
"Dishonest",
|
||||||
|
"Disloyal",
|
||||||
|
"Disobedient",
|
||||||
|
"Disorderly",
|
||||||
|
"Disorganized",
|
||||||
|
"Disputatious",
|
||||||
|
"Disrespectful",
|
||||||
|
"Disruptive",
|
||||||
|
"Dissolute",
|
||||||
|
"Dissonant",
|
||||||
|
"Distractible",
|
||||||
|
"Disturbing",
|
||||||
|
"Dogmatic",
|
||||||
|
"Domineering",
|
||||||
|
"Dull",
|
||||||
|
"Easily Discouraged",
|
||||||
|
"Egocentric",
|
||||||
|
"Enervated",
|
||||||
|
"Envious",
|
||||||
|
"Erratic",
|
||||||
|
"Escapist",
|
||||||
|
"Excitable",
|
||||||
|
"Expedient",
|
||||||
|
"Extravagant",
|
||||||
|
"Extreme",
|
||||||
|
"Faithless",
|
||||||
|
"False",
|
||||||
|
"Fanatical",
|
||||||
|
"Fanciful",
|
||||||
|
"Fatalistic",
|
||||||
|
"Fawning",
|
||||||
|
"Fearful",
|
||||||
|
"Fickle",
|
||||||
|
"Fiery",
|
||||||
|
"Fixed",
|
||||||
|
"Flamboyant",
|
||||||
|
"Foolish",
|
||||||
|
"Forgetful",
|
||||||
|
"Fraudulent",
|
||||||
|
"Frightening",
|
||||||
|
"Frivolous",
|
||||||
|
"Gloomy",
|
||||||
|
"Graceless",
|
||||||
|
"Grand",
|
||||||
|
"Greedy",
|
||||||
|
"Grim",
|
||||||
|
"Gullible",
|
||||||
|
"Hateful",
|
||||||
|
"Haughty",
|
||||||
|
"Hedonistic",
|
||||||
|
"Hesitant",
|
||||||
|
"Hidebound",
|
||||||
|
"High-handed",
|
||||||
|
"Hostile",
|
||||||
|
"Ignorant",
|
||||||
|
"Imitative",
|
||||||
|
"Impatient",
|
||||||
|
"Impractical",
|
||||||
|
"Imprudent",
|
||||||
|
"Impulsive",
|
||||||
|
"Inconsiderate",
|
||||||
|
"Incurious",
|
||||||
|
"Indecisive",
|
||||||
|
"Indulgent",
|
||||||
|
"Inert",
|
||||||
|
"Inhibited",
|
||||||
|
"Insecure",
|
||||||
|
"Insensitive",
|
||||||
|
"Insincere",
|
||||||
|
"Insulting",
|
||||||
|
"Intolerant",
|
||||||
|
"Irascible",
|
||||||
|
"Irrational",
|
||||||
|
"Irresponsible",
|
||||||
|
"Irritable",
|
||||||
|
"Lazy",
|
||||||
|
"Libidinous",
|
||||||
|
"Loquacious",
|
||||||
|
"Malicious",
|
||||||
|
"Mannered",
|
||||||
|
"Mannerless",
|
||||||
|
"Mawkish",
|
||||||
|
"Mealymouthed",
|
||||||
|
"Mechanical",
|
||||||
|
"Meddlesome",
|
||||||
|
"Melancholic",
|
||||||
|
"Meretricious",
|
||||||
|
"Messy",
|
||||||
|
"Miserable",
|
||||||
|
"Miserly",
|
||||||
|
"Misguided",
|
||||||
|
"Mistaken",
|
||||||
|
"Money-minded",
|
||||||
|
"Monstrous",
|
||||||
|
"Moody",
|
||||||
|
"Morbid",
|
||||||
|
"Muddle-headed",
|
||||||
|
"Naive",
|
||||||
|
"Narcissistic",
|
||||||
|
"Narrow",
|
||||||
|
"Narrow-minded",
|
||||||
|
"Natty",
|
||||||
|
"Negativistic",
|
||||||
|
"Neglectful",
|
||||||
|
"Neurotic",
|
||||||
|
"Nihilistic",
|
||||||
|
"Obnoxious",
|
||||||
|
"Obsessive",
|
||||||
|
"Obvious",
|
||||||
|
"Odd",
|
||||||
|
"Offhand",
|
||||||
|
"One-dimensional",
|
||||||
|
"One-sided",
|
||||||
|
"Opinionated",
|
||||||
|
"Opportunistic",
|
||||||
|
"Oppressed",
|
||||||
|
"Outrageous",
|
||||||
|
"Overimaginative",
|
||||||
|
"Paranoid",
|
||||||
|
"Passive",
|
||||||
|
"Pedantic",
|
||||||
|
"Perverse",
|
||||||
|
"Petty",
|
||||||
|
"Pharissical",
|
||||||
|
"Phlegmatic",
|
||||||
|
"Plodding",
|
||||||
|
"Pompous",
|
||||||
|
"Possessive",
|
||||||
|
"Power-hungry",
|
||||||
|
"Predatory",
|
||||||
|
"Prejudiced",
|
||||||
|
"Presumptuous",
|
||||||
|
"Pretentious",
|
||||||
|
"Prim",
|
||||||
|
"Procrastinating",
|
||||||
|
"Profligate",
|
||||||
|
"Provocative",
|
||||||
|
"Pugnacious",
|
||||||
|
"Puritanical",
|
||||||
|
"Quirky",
|
||||||
|
"Reactionary",
|
||||||
|
"Reactive",
|
||||||
|
"Regimental",
|
||||||
|
"Regretful",
|
||||||
|
"Repentant",
|
||||||
|
"Repressed",
|
||||||
|
"Resentful",
|
||||||
|
"Ridiculous",
|
||||||
|
"Rigid",
|
||||||
|
"Ritualistic",
|
||||||
|
"Rowdy",
|
||||||
|
"Ruined",
|
||||||
|
"Sadistic",
|
||||||
|
"Sanctimonious",
|
||||||
|
"Scheming",
|
||||||
|
"Scornful",
|
||||||
|
"Secretive",
|
||||||
|
"Sedentary",
|
||||||
|
"Selfish",
|
||||||
|
"Self-indulgent",
|
||||||
|
"Shallow",
|
||||||
|
"Shortsighted",
|
||||||
|
"Shy",
|
||||||
|
"Silly",
|
||||||
|
"Single-minded",
|
||||||
|
"Sloppy",
|
||||||
|
"Slow",
|
||||||
|
"Sly",
|
||||||
|
"Small-thinking",
|
||||||
|
"Softheaded",
|
||||||
|
"Sordid",
|
||||||
|
"Steely",
|
||||||
|
"Stiff",
|
||||||
|
"Strong-willed",
|
||||||
|
"Stupid",
|
||||||
|
"Submissive",
|
||||||
|
"Superficial",
|
||||||
|
"Superstitious",
|
||||||
|
"Suspicious",
|
||||||
|
"Tactless",
|
||||||
|
"Tasteless",
|
||||||
|
"Tense",
|
||||||
|
"Thievish",
|
||||||
|
"Thoughtless",
|
||||||
|
"Timid",
|
||||||
|
"Transparent",
|
||||||
|
"Treacherous",
|
||||||
|
"Trendy",
|
||||||
|
"Troublesome",
|
||||||
|
"Unappreciative",
|
||||||
|
"Uncaring",
|
||||||
|
"Uncharitable",
|
||||||
|
"Unconvincing",
|
||||||
|
"Uncooperative",
|
||||||
|
"Uncreative",
|
||||||
|
"Uncritical",
|
||||||
|
"Unctuous",
|
||||||
|
"Undisciplined",
|
||||||
|
"Unfriendly",
|
||||||
|
"Ungrateful",
|
||||||
|
"Unhealthy",
|
||||||
|
"Unimaginative",
|
||||||
|
"Unimpressive",
|
||||||
|
"Unlovable",
|
||||||
|
"Unpolished",
|
||||||
|
"Unprincipled",
|
||||||
|
"Unrealistic",
|
||||||
|
"Unreflective",
|
||||||
|
"Unreliable",
|
||||||
|
"Unrestrained",
|
||||||
|
"Unself-critical",
|
||||||
|
"Unstable",
|
||||||
|
"Vacuous",
|
||||||
|
"Vague",
|
||||||
|
"Venal",
|
||||||
|
"Venomous",
|
||||||
|
"Vindictive",
|
||||||
|
"Vulnerable",
|
||||||
|
"Weak",
|
||||||
|
"Weak-willed",
|
||||||
|
"Well-meaning",
|
||||||
|
"Willful",
|
||||||
|
"Wishful",
|
||||||
|
"Zany",
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
positive_adjectives = types.equal_weights(
|
||||||
|
[
|
||||||
|
"able",
|
||||||
|
"clean",
|
||||||
|
"enthusiastic",
|
||||||
|
"heartening",
|
||||||
|
"meek",
|
||||||
|
"reasonable",
|
||||||
|
"talented",
|
||||||
|
"accommodating",
|
||||||
|
"clever",
|
||||||
|
"ethical",
|
||||||
|
"helpful",
|
||||||
|
"meritorious",
|
||||||
|
"refined",
|
||||||
|
"temperate",
|
||||||
|
"accomplished",
|
||||||
|
"commendable",
|
||||||
|
"excellent",
|
||||||
|
"moral",
|
||||||
|
"reliable",
|
||||||
|
"terrific",
|
||||||
|
"adept",
|
||||||
|
"compassionate",
|
||||||
|
"exceptional",
|
||||||
|
"honest",
|
||||||
|
"neat",
|
||||||
|
"remarkable",
|
||||||
|
"tidy",
|
||||||
|
"admirable",
|
||||||
|
"composed",
|
||||||
|
"exemplary",
|
||||||
|
"honorable",
|
||||||
|
"noble",
|
||||||
|
"resilient",
|
||||||
|
"quality",
|
||||||
|
"agreeable",
|
||||||
|
"considerate",
|
||||||
|
"exquisite",
|
||||||
|
"hopeful",
|
||||||
|
"obliging",
|
||||||
|
"respectable",
|
||||||
|
"tremendous",
|
||||||
|
"amazing",
|
||||||
|
"consummate",
|
||||||
|
"extraordinary",
|
||||||
|
"humble",
|
||||||
|
"observant",
|
||||||
|
"respectful",
|
||||||
|
"trustworthy",
|
||||||
|
"appealing",
|
||||||
|
"cooperative",
|
||||||
|
"fabulous",
|
||||||
|
"important",
|
||||||
|
"optimistic",
|
||||||
|
"resplendent",
|
||||||
|
"trusty",
|
||||||
|
"astute",
|
||||||
|
"correct",
|
||||||
|
"faithful",
|
||||||
|
"impressive",
|
||||||
|
"organized",
|
||||||
|
"responsible",
|
||||||
|
"truthful",
|
||||||
|
"attractive",
|
||||||
|
"courageous",
|
||||||
|
"fantastic",
|
||||||
|
"incisive",
|
||||||
|
"outstanding",
|
||||||
|
"robust",
|
||||||
|
"unbeatable",
|
||||||
|
"awesome",
|
||||||
|
"courteous",
|
||||||
|
"fascinating",
|
||||||
|
"incredible",
|
||||||
|
"peaceful",
|
||||||
|
"selfless",
|
||||||
|
"understanding",
|
||||||
|
"beautiful",
|
||||||
|
"dazzling",
|
||||||
|
"fine",
|
||||||
|
"innocent",
|
||||||
|
"perceptive",
|
||||||
|
"sensational",
|
||||||
|
"unequaled",
|
||||||
|
"benevolent",
|
||||||
|
"decent",
|
||||||
|
"classy",
|
||||||
|
"insightful",
|
||||||
|
"perfect",
|
||||||
|
"sensible",
|
||||||
|
"unparalleled",
|
||||||
|
"brave",
|
||||||
|
"delightful",
|
||||||
|
"fortitudinous",
|
||||||
|
"inspiring",
|
||||||
|
"pleasant",
|
||||||
|
"serene",
|
||||||
|
"upbeat",
|
||||||
|
"breathtaking",
|
||||||
|
"dependable",
|
||||||
|
"gallant",
|
||||||
|
"intelligent",
|
||||||
|
"pleasing",
|
||||||
|
"sharp",
|
||||||
|
"valiant",
|
||||||
|
"bright",
|
||||||
|
"devoted",
|
||||||
|
"generous",
|
||||||
|
"joyful",
|
||||||
|
"polite",
|
||||||
|
"shining",
|
||||||
|
"valuable",
|
||||||
|
"brilliant",
|
||||||
|
"diplomatic",
|
||||||
|
"gentle",
|
||||||
|
"judicious",
|
||||||
|
"positive",
|
||||||
|
"shrewd",
|
||||||
|
"vigilant",
|
||||||
|
"bubbly",
|
||||||
|
"discerning",
|
||||||
|
"gifted",
|
||||||
|
"just",
|
||||||
|
"praiseworthy",
|
||||||
|
"smart",
|
||||||
|
"vigorous",
|
||||||
|
"buoyant",
|
||||||
|
"disciplined",
|
||||||
|
"giving",
|
||||||
|
"kindly",
|
||||||
|
"precious",
|
||||||
|
"sparkling",
|
||||||
|
"virtuous",
|
||||||
|
"calm",
|
||||||
|
"elegant",
|
||||||
|
"gleaming",
|
||||||
|
"laudable",
|
||||||
|
"priceless",
|
||||||
|
"spectacular",
|
||||||
|
"well mannered",
|
||||||
|
"capable",
|
||||||
|
"elevating",
|
||||||
|
"glowing",
|
||||||
|
"likable",
|
||||||
|
"principled",
|
||||||
|
"splendid",
|
||||||
|
"wholesome",
|
||||||
|
"charitable",
|
||||||
|
"enchanting",
|
||||||
|
"good",
|
||||||
|
"lovable",
|
||||||
|
"prompt",
|
||||||
|
"steadfast",
|
||||||
|
"wise",
|
||||||
|
"charming",
|
||||||
|
"encouraging",
|
||||||
|
"gorgeous",
|
||||||
|
"lovely",
|
||||||
|
"prudent",
|
||||||
|
"stunning",
|
||||||
|
"witty",
|
||||||
|
"chaste",
|
||||||
|
"endearing",
|
||||||
|
"graceful",
|
||||||
|
"loyal",
|
||||||
|
"punctual",
|
||||||
|
"super",
|
||||||
|
"wonderful",
|
||||||
|
"cheerful",
|
||||||
|
"energetic",
|
||||||
|
"gracious",
|
||||||
|
"luminous",
|
||||||
|
"pure",
|
||||||
|
"superb",
|
||||||
|
"worthy",
|
||||||
|
"chivalrous",
|
||||||
|
"engaging",
|
||||||
|
"great",
|
||||||
|
"magnanimous",
|
||||||
|
"quick",
|
||||||
|
"superior",
|
||||||
|
"zesty",
|
||||||
|
"gallant",
|
||||||
|
"enhanced",
|
||||||
|
"happy",
|
||||||
|
"magnificent",
|
||||||
|
"radiant",
|
||||||
|
"supportive",
|
||||||
|
"civil",
|
||||||
|
"enjoyable",
|
||||||
|
"hardy",
|
||||||
|
"marvelous",
|
||||||
|
"rational",
|
||||||
|
"supreme",
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
)
|
14
language/languages/__init__.py
Normal file
14
language/languages/__init__.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import importlib
|
||||||
|
import pkgutil
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def import_submodules(module):
|
||||||
|
pkgs = pkgutil.iter_modules(module.__path__)
|
||||||
|
for loader, module_name, is_pkg in pkgs:
|
||||||
|
yield importlib.import_module(f"{module.__name__}.{module_name}")
|
||||||
|
|
||||||
|
|
||||||
|
supported_languages = dict(
|
||||||
|
(module.__name__.split(".")[-1], module) for module in list(import_submodules(sys.modules[__name__]))
|
||||||
|
)
|
16
language/languages/abyssal/README.md
Normal file
16
language/languages/abyssal/README.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
### Abyssal
|
||||||
|
|
||||||
|
Low-ranking demons speak in a guttural, broken dialect that is louder and more
|
||||||
|
consonant, like dogs barking. The written language and naming conventions
|
||||||
|
reflect the more refined language of demons, a quiet, sibilant susurrus that
|
||||||
|
sounds at times like waves, at others like buzzing, keening insects.
|
||||||
|
|
||||||
|
*Whuewhezoawh ..uesewhaza whs..uwhiezowhisoi aizi..wheuz, whoshesowhewh
|
||||||
|
she..iwhzzos..ozi iuzu..ueshes izsha..ua..uashezshuesas, ..awho....uza
|
||||||
|
whuashoowhsz!*
|
||||||
|
|
||||||
|
**Abyssal Names:**
|
||||||
|
|
||||||
|
* Aa..Whaazwhis
|
||||||
|
* Az..Zazzou
|
||||||
|
* Uzeushsoshe..Osezawhwh
|
7
language/languages/abyssal/__init__.py
Normal file
7
language/languages/abyssal/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Abyssal
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
18
language/languages/abyssal/base.py
Normal file
18
language/languages/abyssal/base.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from language import defaults, types
|
||||||
|
|
||||||
|
vowels = types.equal_weights(["a", "e", "i"], 1.0, blank=False)
|
||||||
|
consonants = types.equal_weights(["z", "sh", "s", "wh", ".."], 1.0, blank=False)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="celstial",
|
||||||
|
vowels=defaults.vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=None,
|
||||||
|
rules=[],
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="vowel|consonant") * 10, 1.0),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 15, 1.0),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 20, 1.0),
|
||||||
|
),
|
||||||
|
)
|
11
language/languages/abyssal/names.py
Normal file
11
language/languages/abyssal/names.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from language import types
|
||||||
|
from language.languages.abyssal import Language
|
||||||
|
|
||||||
|
Name = types.NameGenerator(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("name"), 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
NobleName = Name
|
15
language/languages/celestial/README.md
Normal file
15
language/languages/celestial/README.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
### Celestial
|
||||||
|
|
||||||
|
Celestial is sung, not spoken, and sounds to mortal ears like stacked harmonies
|
||||||
|
of an impossible choir. Written Celestial is akin to sheet music; it records
|
||||||
|
the grammar and vocabulary, but cannot convey meaning.
|
||||||
|
|
||||||
|
*U̇ôeȧäuueüiäûoîịėeöäueaȯịo äuäuiaîiüoûoėeäoîeȧü öoaịäu̇ôuüöeiääoȧėu
|
||||||
|
ôaîaêaịeiîäoöiôoėöi, ueûuêûiüȧuuȧaüeeêeuȯ oääuêaüâiîȧiûeu̇ėaâaȧ
|
||||||
|
uîoėu̇au̇oîäeiäüuȧoêaiêu̇u uaäeîeoäuiaịôoėȯîiäiȧė îûȧäuaûėuoäôiûäuaȧ!
|
||||||
|
Âȧuȯüiêịiäaiėöaöoi.*
|
||||||
|
|
||||||
|
**Celestial Names:**
|
||||||
|
* Ịaêäuuȧoeėuäuoaịooȯîoäu̇Au̇Aû
|
||||||
|
* Ûịịeịuȧoȯuȯaäâeeu̇Ėuiüaị
|
||||||
|
* Au̇Üaoịėuüöaâaėoöȧeäėu
|
7
language/languages/celestial/__init__.py
Normal file
7
language/languages/celestial/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Celestial
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
21
language/languages/celestial/base.py
Normal file
21
language/languages/celestial/base.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from language import defaults, types
|
||||||
|
|
||||||
|
vowels = types.equal_weights(["a", "e", "i", "o", "u"], 1.0, blank=False)
|
||||||
|
consonants = types.equal_weights(
|
||||||
|
["î", "ê", "â", "û", "ô", "ä", "ö", "ü", "äu", "ȧ", "ė", "ị", "ȯ", "u̇"], 1.0, blank=False
|
||||||
|
)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="celstial",
|
||||||
|
vowels=defaults.vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=None,
|
||||||
|
rules=[],
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="vowel|consonant") * 20, 1.0),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 25, 1.0),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 30, 1.0),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 35, 1.0),
|
||||||
|
),
|
||||||
|
)
|
11
language/languages/celestial/names.py
Normal file
11
language/languages/celestial/names.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from language import types
|
||||||
|
from language.languages.celestial import Language
|
||||||
|
|
||||||
|
Name = types.NameGenerator(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("name"), 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
NobleName = Name
|
16
language/languages/common/README.md
Normal file
16
language/languages/common/README.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
### Common
|
||||||
|
|
||||||
|
Common is a complicated pidgin of influences with multiple regional dialects. Written Common is the language of
|
||||||
|
traders, and can be relied upon to be understood by most peoples to a greater or lesser degree.
|
||||||
|
|
||||||
|
*Proitsiiiy be itkif eesof detytaen. Ojaot tyskuaz apsoo nirtoet prenao.*
|
||||||
|
|
||||||
|
**Common Names:**
|
||||||
|
* Rubi Ca Momaman
|
||||||
|
* "Quiet" Gushi Murk Lirpusome
|
||||||
|
* Fewse Kerloborg
|
||||||
|
|
||||||
|
**Noble Common Names:**
|
||||||
|
* Lord Pasti Quusi Maghiheim
|
||||||
|
* Lady Gotki Lane Lopileigh III
|
||||||
|
* Dame Cu Lehaberry IX
|
7
language/languages/common/__init__.py
Normal file
7
language/languages/common/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Common
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
158
language/languages/common/base.py
Normal file
158
language/languages/common/base.py
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
from language import types
|
||||||
|
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
vowels = types.WeightedSet(
|
||||||
|
("a", 1.0),
|
||||||
|
("e", 1.0),
|
||||||
|
("i", 1.0),
|
||||||
|
("o", 0.8),
|
||||||
|
("u", 0.7),
|
||||||
|
("y", 0.01),
|
||||||
|
)
|
||||||
|
consonants = types.WeightedSet(
|
||||||
|
("b", 0.5),
|
||||||
|
("c", 0.5),
|
||||||
|
("d", 0.5),
|
||||||
|
("f", 0.3),
|
||||||
|
("g", 0.3),
|
||||||
|
("h", 0.5),
|
||||||
|
("j", 0.2),
|
||||||
|
("k", 0.3),
|
||||||
|
("l", 1.0),
|
||||||
|
("m", 0.5),
|
||||||
|
("n", 1.0),
|
||||||
|
("p", 0.5),
|
||||||
|
("q", 0.05),
|
||||||
|
("r", 1.0),
|
||||||
|
("s", 1.0),
|
||||||
|
("t", 1.0),
|
||||||
|
("v", 0.3),
|
||||||
|
("w", 0.2),
|
||||||
|
("x", 0.2),
|
||||||
|
("y", 0.01),
|
||||||
|
("z", 0.1),
|
||||||
|
("bs", 0.3),
|
||||||
|
("ct", 0.4),
|
||||||
|
("ch", 0.4),
|
||||||
|
("ck", 0.4),
|
||||||
|
("dd", 0.2),
|
||||||
|
("ff", 0.2),
|
||||||
|
("gh", 0.3),
|
||||||
|
("gs", 0.2),
|
||||||
|
("ms", 0.4),
|
||||||
|
("ns", 0.4),
|
||||||
|
("ps", 0.3),
|
||||||
|
("qu", 0.2),
|
||||||
|
("rb", 0.1),
|
||||||
|
("rd", 0.2),
|
||||||
|
("rf", 0.1),
|
||||||
|
("rk", 0.2),
|
||||||
|
("rl", 0.2),
|
||||||
|
("rm", 0.2),
|
||||||
|
("rn", 0.2),
|
||||||
|
("rp", 0.1),
|
||||||
|
("rs", 0.75),
|
||||||
|
("rt", 0.75),
|
||||||
|
("ry", 0.5),
|
||||||
|
("sh", 1.0),
|
||||||
|
("sk", 0.5),
|
||||||
|
("ss", 0.75),
|
||||||
|
("st", 1.0),
|
||||||
|
("sy", 0.5),
|
||||||
|
("th", 1.0),
|
||||||
|
("tk", 0.5),
|
||||||
|
("ts", 1.0),
|
||||||
|
("tt", 1.0),
|
||||||
|
("ty", 1.0),
|
||||||
|
("ws", 0.5),
|
||||||
|
)
|
||||||
|
|
||||||
|
prefixes = types.equal_weights(["ex", "re", "pre", "de", "pro", "anti"], 0.05)
|
||||||
|
|
||||||
|
suffixes = types.equal_weights(
|
||||||
|
[
|
||||||
|
"ad",
|
||||||
|
"ed",
|
||||||
|
"id",
|
||||||
|
"od",
|
||||||
|
"ud",
|
||||||
|
"af",
|
||||||
|
"ef",
|
||||||
|
"if",
|
||||||
|
"of",
|
||||||
|
"uf",
|
||||||
|
"ah",
|
||||||
|
"eh",
|
||||||
|
"ih",
|
||||||
|
"oh",
|
||||||
|
"uh",
|
||||||
|
"al",
|
||||||
|
"el",
|
||||||
|
"il",
|
||||||
|
"ol",
|
||||||
|
"ul",
|
||||||
|
"am",
|
||||||
|
"em",
|
||||||
|
"im",
|
||||||
|
"om",
|
||||||
|
"um",
|
||||||
|
"an",
|
||||||
|
"en",
|
||||||
|
"in",
|
||||||
|
"on",
|
||||||
|
"un",
|
||||||
|
"ar",
|
||||||
|
"er",
|
||||||
|
"ir",
|
||||||
|
"or",
|
||||||
|
"ur",
|
||||||
|
"as",
|
||||||
|
"es",
|
||||||
|
"is",
|
||||||
|
"os",
|
||||||
|
"us",
|
||||||
|
"at",
|
||||||
|
"et",
|
||||||
|
"it",
|
||||||
|
"ot",
|
||||||
|
"ut",
|
||||||
|
"ax",
|
||||||
|
"ex",
|
||||||
|
"ix",
|
||||||
|
"ox",
|
||||||
|
"ux",
|
||||||
|
"ay",
|
||||||
|
"ey",
|
||||||
|
"iy",
|
||||||
|
"oy",
|
||||||
|
"uy",
|
||||||
|
"az",
|
||||||
|
"ez",
|
||||||
|
"iz",
|
||||||
|
"oz",
|
||||||
|
"uz",
|
||||||
|
"ing",
|
||||||
|
],
|
||||||
|
0.05,
|
||||||
|
)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="common",
|
||||||
|
vowels=vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=prefixes,
|
||||||
|
suffixes=suffixes,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="vowel|consonant"), 0.01),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 2, 0.2),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 3, 0.4),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 3, 0.5),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 4, 1.0),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 5, 0.3),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 6, 0.2),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 7, 0.05),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=1,
|
||||||
|
)
|
82
language/languages/common/names.py
Normal file
82
language/languages/common/names.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
from language import defaults, types
|
||||||
|
from language.languages.common import Language
|
||||||
|
|
||||||
|
suffixes = types.equal_weights(
|
||||||
|
[
|
||||||
|
"berg",
|
||||||
|
"borg",
|
||||||
|
"borough",
|
||||||
|
"bury",
|
||||||
|
"berry",
|
||||||
|
"by",
|
||||||
|
"ford",
|
||||||
|
"gard",
|
||||||
|
"grave",
|
||||||
|
"grove",
|
||||||
|
"gren",
|
||||||
|
"hardt",
|
||||||
|
"hart",
|
||||||
|
"heim",
|
||||||
|
"holm",
|
||||||
|
"land",
|
||||||
|
"leigh",
|
||||||
|
"ley",
|
||||||
|
"ly",
|
||||||
|
"lof",
|
||||||
|
"love",
|
||||||
|
"lund",
|
||||||
|
"man",
|
||||||
|
"mark",
|
||||||
|
"ness",
|
||||||
|
"olf",
|
||||||
|
"olph",
|
||||||
|
"quist",
|
||||||
|
"rop",
|
||||||
|
"rup",
|
||||||
|
"stad",
|
||||||
|
"stead",
|
||||||
|
"stein",
|
||||||
|
"strom",
|
||||||
|
"thal",
|
||||||
|
"thorpe",
|
||||||
|
"ton",
|
||||||
|
"vall",
|
||||||
|
"wich",
|
||||||
|
"win",
|
||||||
|
"some",
|
||||||
|
"smith",
|
||||||
|
"bridge",
|
||||||
|
"cope",
|
||||||
|
"town",
|
||||||
|
"er",
|
||||||
|
"don",
|
||||||
|
"den",
|
||||||
|
"dell",
|
||||||
|
"son",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
Name = types.NameGenerator(
|
||||||
|
language=Language,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="vowel|consonant"), 0.01),
|
||||||
|
(types.Syllable(template="consonant,vowel"), 0.2),
|
||||||
|
(types.Syllable(template="consonant,vowel") * 2, 1.0),
|
||||||
|
),
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("adjective,title,name,surname,count"), 1.0),
|
||||||
|
(types.NameTemplate("title,name,name,surname,count"), 1.0),
|
||||||
|
(types.NameTemplate("title,name,name,surname,surname,count"), 1.0),
|
||||||
|
),
|
||||||
|
names=None,
|
||||||
|
surnames=None,
|
||||||
|
nicknames=None,
|
||||||
|
adjectives=defaults.adjectives,
|
||||||
|
titles=defaults.titles,
|
||||||
|
counts=defaults.counts,
|
||||||
|
suffixes=suffixes,
|
||||||
|
)
|
||||||
|
Name.language.prefixes = None
|
||||||
|
Name.language.suffixes = None
|
||||||
|
|
||||||
|
NobleName = Name
|
115
language/languages/common/rules.py
Normal file
115
language/languages/common/rules.py
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from language.rules import default_rules
|
||||||
|
from language.types import Language
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
permitted_starting_clusters = [
|
||||||
|
"bh",
|
||||||
|
"bl",
|
||||||
|
"br",
|
||||||
|
"bw",
|
||||||
|
"by",
|
||||||
|
"ch",
|
||||||
|
"cl",
|
||||||
|
"cr",
|
||||||
|
"cw",
|
||||||
|
"cy",
|
||||||
|
"dh",
|
||||||
|
"dj",
|
||||||
|
"dr",
|
||||||
|
"dw",
|
||||||
|
"dy",
|
||||||
|
"fl",
|
||||||
|
"fn",
|
||||||
|
"fr",
|
||||||
|
"fw",
|
||||||
|
"fy",
|
||||||
|
"gh",
|
||||||
|
"gl",
|
||||||
|
"gn",
|
||||||
|
"gr",
|
||||||
|
"gw",
|
||||||
|
"gy",
|
||||||
|
"hy",
|
||||||
|
"jh",
|
||||||
|
"jy",
|
||||||
|
"kh",
|
||||||
|
"kl",
|
||||||
|
"kr",
|
||||||
|
"kw",
|
||||||
|
"ky",
|
||||||
|
"ll",
|
||||||
|
"ly",
|
||||||
|
"mw",
|
||||||
|
"my",
|
||||||
|
"ny",
|
||||||
|
"ph",
|
||||||
|
"pl",
|
||||||
|
"pn",
|
||||||
|
"pr",
|
||||||
|
"pw",
|
||||||
|
"py",
|
||||||
|
"rh",
|
||||||
|
"ry",
|
||||||
|
"sb",
|
||||||
|
"sc",
|
||||||
|
"sd",
|
||||||
|
"sf",
|
||||||
|
"sg",
|
||||||
|
"sh",
|
||||||
|
"sj",
|
||||||
|
"sk",
|
||||||
|
"sl",
|
||||||
|
"sm",
|
||||||
|
"sn",
|
||||||
|
"sp",
|
||||||
|
"sr",
|
||||||
|
"st",
|
||||||
|
"sv",
|
||||||
|
"sw",
|
||||||
|
"sy",
|
||||||
|
"th",
|
||||||
|
"tr",
|
||||||
|
"tw",
|
||||||
|
"ty",
|
||||||
|
"vy",
|
||||||
|
"wh",
|
||||||
|
"wr",
|
||||||
|
"wy",
|
||||||
|
"xh",
|
||||||
|
"xy",
|
||||||
|
"yh",
|
||||||
|
"zb",
|
||||||
|
"zc",
|
||||||
|
"zd",
|
||||||
|
"zh",
|
||||||
|
"zl",
|
||||||
|
"zm",
|
||||||
|
"zn",
|
||||||
|
"zr",
|
||||||
|
"zw",
|
||||||
|
"zy",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def cannot_start_with_two_consonants(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"(^[bcdfghjklmnpqrstvwxz]{2})").search(word)
|
||||||
|
if not found:
|
||||||
|
return True
|
||||||
|
first, second = found.group(1)
|
||||||
|
|
||||||
|
if first == second:
|
||||||
|
logger.debug(f"{word} starts with a repeated consonant.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return found in permitted_starting_clusters
|
||||||
|
|
||||||
|
|
||||||
|
rules = default_rules.union(
|
||||||
|
{
|
||||||
|
cannot_start_with_two_consonants,
|
||||||
|
}
|
||||||
|
)
|
23
language/languages/draconic/README.md
Normal file
23
language/languages/draconic/README.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
### Draconic
|
||||||
|
|
||||||
|
The tongue of dragons and the Dragonborn. It is punctuated by low, gutteral
|
||||||
|
grunts denoted by an apostrophe ('), the particular inflections of which can
|
||||||
|
change the meaning of an utterance dramatically. When shouting, a speaker of
|
||||||
|
Draconic should accentuate these grunts with spurts of flame, acid, ice, and so
|
||||||
|
on.
|
||||||
|
|
||||||
|
The high form is reserved for the names of nobility -- that is, dragons -- who
|
||||||
|
typically choose an appelation for themselves in the common tongue, the better
|
||||||
|
to spread the terror of their name.
|
||||||
|
|
||||||
|
*Sey'so'jsii ysuut 'sei'gk gf'seiv 'se'su'rx 'se'yv hsi', sahsei' k'si'saa' f'si''see?*
|
||||||
|
|
||||||
|
**Draconic Names:**
|
||||||
|
* V'Sei'Sey'a
|
||||||
|
* Sir''Seytza
|
||||||
|
* 'Soh'Suutus
|
||||||
|
|
||||||
|
**Noble (Dragon) Names:**
|
||||||
|
* Seytthix the Willful
|
||||||
|
* Saseezex the Enervated
|
||||||
|
* Sahkzis the Miserly
|
7
language/languages/draconic/__init__.py
Normal file
7
language/languages/draconic/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Draconic
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
47
language/languages/draconic/base.py
Normal file
47
language/languages/draconic/base.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
from language import types
|
||||||
|
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
vowels = types.equal_weights(
|
||||||
|
["sa", "saa", "sah", "se", "see", "sei", "sey", "si", "sii", "sir", "so", "su", "suu"], 1.0, blank=False
|
||||||
|
)
|
||||||
|
|
||||||
|
consonants = types.WeightedSet(
|
||||||
|
("d", 1.0),
|
||||||
|
("f", 0.5),
|
||||||
|
("g", 0.5),
|
||||||
|
("h", 1.0),
|
||||||
|
("j", 1.0),
|
||||||
|
("k", 1.0),
|
||||||
|
("l", 0.3),
|
||||||
|
("n", 0.3),
|
||||||
|
("r", 0.2),
|
||||||
|
("t", 1.0),
|
||||||
|
("v", 1.0),
|
||||||
|
("x", 1.0),
|
||||||
|
("y", 1.0),
|
||||||
|
("z", 1.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DraconicLanguage(types.Language):
|
||||||
|
stops = types.equal_weights(["'"], 1.0)
|
||||||
|
|
||||||
|
def get_grapheme_vowel(self) -> str:
|
||||||
|
return self.stops.random() + self.vowels.random() + self.stops.random()
|
||||||
|
|
||||||
|
|
||||||
|
Language = DraconicLanguage(
|
||||||
|
name="draconic",
|
||||||
|
vowels=vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=None,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="consonant|vowel") * 2, 0.2),
|
||||||
|
(types.Syllable(template="consonant|vowel") * 3, 1.0),
|
||||||
|
(types.Syllable(template="consonant|vowel") * 4, 1.0),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=2,
|
||||||
|
)
|
88
language/languages/draconic/names.py
Normal file
88
language/languages/draconic/names.py
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
from language import defaults, types
|
||||||
|
from language.languages.draconic import Language
|
||||||
|
|
||||||
|
# dragon_titles = types.equal_weights([
|
||||||
|
# ], 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
class DraconicNameGenerator(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("name"), 1.0),
|
||||||
|
(types.NameTemplate("adjective,name"), 1.0),
|
||||||
|
),
|
||||||
|
adjectives=defaults.adjectives,
|
||||||
|
)
|
||||||
|
self.language.minimum_grapheme_count = 2
|
||||||
|
self.suffixes = types.equal_weights(["us", "ius", "eus", "a", "an", "is"], 1.0, blank=False)
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
return super().get_name() + self.suffixes.random()
|
||||||
|
|
||||||
|
|
||||||
|
class NobleDraconicNameGenerator(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("surname,the,title"), 1.0),
|
||||||
|
),
|
||||||
|
# titles=dragon_titles,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="consonant|vowel") * 2, 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.language.minimum_grapheme_count = 2
|
||||||
|
self.suffixes = types.equal_weights(
|
||||||
|
[
|
||||||
|
"thus",
|
||||||
|
"thux",
|
||||||
|
"thas",
|
||||||
|
"thax",
|
||||||
|
"this",
|
||||||
|
"thix",
|
||||||
|
"thes",
|
||||||
|
"thex",
|
||||||
|
"xus",
|
||||||
|
"xux",
|
||||||
|
"xas",
|
||||||
|
"xax",
|
||||||
|
"xis",
|
||||||
|
"xix",
|
||||||
|
"xes",
|
||||||
|
"xex",
|
||||||
|
"ssus",
|
||||||
|
"ssux",
|
||||||
|
"ssas",
|
||||||
|
"ssax",
|
||||||
|
"ssis",
|
||||||
|
"ssix",
|
||||||
|
"sses",
|
||||||
|
"ssex",
|
||||||
|
"zus",
|
||||||
|
"zux",
|
||||||
|
"zas",
|
||||||
|
"zax",
|
||||||
|
"zis",
|
||||||
|
"zix",
|
||||||
|
"zes",
|
||||||
|
"zex",
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
blank=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
p = ""
|
||||||
|
while not p:
|
||||||
|
p = defaults.personality.random()
|
||||||
|
return p
|
||||||
|
|
||||||
|
def get_surname(self) -> str:
|
||||||
|
return super().get_name().replace("'", "").title() + self.suffixes.random()
|
||||||
|
|
||||||
|
|
||||||
|
Name = DraconicNameGenerator()
|
||||||
|
NobleName = NobleDraconicNameGenerator()
|
7
language/languages/draconic/rules.py
Normal file
7
language/languages/draconic/rules.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from language.rules import default_rules
|
||||||
|
|
||||||
|
logger = logging.getLogger("draconic-rules")
|
||||||
|
|
||||||
|
rules = default_rules
|
14
language/languages/dwarvish/README.md
Normal file
14
language/languages/dwarvish/README.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
### Dwarvish
|
||||||
|
|
||||||
|
Dwarvish words are short, sharp, and to the point. Much like the Dwarves
|
||||||
|
themselves. This is "Low Dwarvish"; an ancient form of High Dwarvish still
|
||||||
|
exists in some regions, notably the Dewa Q'Asos area of southern Vosh, but it
|
||||||
|
is considered "dead" by most scholars and is reserved for arcane legal
|
||||||
|
contracts (and those signifying a high social status).
|
||||||
|
|
||||||
|
*Dowêchâ khâ, natu phe tu, futê dêdachî pê she wu?*
|
||||||
|
|
||||||
|
**Dwarvish Names:**
|
||||||
|
* "Black" Yzh Se
|
||||||
|
* Ka Shidothir
|
||||||
|
* Zhâ Syzhon
|
7
language/languages/dwarvish/__init__.py
Normal file
7
language/languages/dwarvish/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Dwarvish
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
54
language/languages/dwarvish/base.py
Normal file
54
language/languages/dwarvish/base.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
from language import types
|
||||||
|
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
vowels = types.WeightedSet(
|
||||||
|
("a", 1.0),
|
||||||
|
("e", 1.0),
|
||||||
|
("i", 0.3),
|
||||||
|
("o", 0.8),
|
||||||
|
("u", 0.7),
|
||||||
|
("y", 0.3),
|
||||||
|
("j", 0.05),
|
||||||
|
("î", 0.3),
|
||||||
|
("ê", 1.0),
|
||||||
|
("â", 1.0),
|
||||||
|
("û", 1.0),
|
||||||
|
)
|
||||||
|
consonants = types.WeightedSet(
|
||||||
|
("b", 0.3),
|
||||||
|
("c", 0.5),
|
||||||
|
("d", 1.0),
|
||||||
|
("f", 0.5),
|
||||||
|
("k", 1.0),
|
||||||
|
("l", 0.3),
|
||||||
|
("m", 0.3),
|
||||||
|
("n", 0.3),
|
||||||
|
("p", 1.0),
|
||||||
|
("s", 1.0),
|
||||||
|
("t", 1.0),
|
||||||
|
("v", 0.5),
|
||||||
|
("w", 0.5),
|
||||||
|
("y", 0.3),
|
||||||
|
("ph", 1.0),
|
||||||
|
("th", 1.0),
|
||||||
|
("ch", 1.0),
|
||||||
|
("kh", 1.0),
|
||||||
|
("zh", 1.0),
|
||||||
|
("sh", 1.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="dwarvish",
|
||||||
|
vowels=vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=None,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="consonant,vowel|consonant") * 1, 1.0),
|
||||||
|
(types.Syllable(template="consonant,vowel|consonant") * 2, 0.5),
|
||||||
|
(types.Syllable(template="consonant,vowel|consonant") * 3, 0.2),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=1,
|
||||||
|
)
|
25
language/languages/dwarvish/names.py
Normal file
25
language/languages/dwarvish/names.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from language import defaults, types
|
||||||
|
from language.languages.dwarvish import Language
|
||||||
|
|
||||||
|
|
||||||
|
class DwarvishNameGenerator(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
# (types.NameTemplate("adjective,name,nickname,surname"), 1.0),
|
||||||
|
(types.NameTemplate("adjective,name,surname"), 1.0),
|
||||||
|
),
|
||||||
|
affixes=None,
|
||||||
|
adjectives=defaults.adjectives,
|
||||||
|
titles=defaults.titles,
|
||||||
|
)
|
||||||
|
self.language.minimum_grapheme_count = 2
|
||||||
|
self.suffixes = types.equal_weights(["son", "sson", "zhon", "dottir", "dothir", "dottyr"], 1.0)
|
||||||
|
|
||||||
|
def get_surname(self) -> str:
|
||||||
|
return super().get_surname() + self.suffixes.random()
|
||||||
|
|
||||||
|
|
||||||
|
Name = DwarvishNameGenerator()
|
||||||
|
NobleName = Name
|
24
language/languages/dwarvish/rules.py
Normal file
24
language/languages/dwarvish/rules.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from language.rules import default_rules
|
||||||
|
from language.types import Language
|
||||||
|
|
||||||
|
logger = logging.getLogger("dwarvish-rules")
|
||||||
|
|
||||||
|
|
||||||
|
def cannot_start_with_repeated_consonants(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"(^[bcdfghklmnpqrstvwxz]{2})").search(word)
|
||||||
|
if not found:
|
||||||
|
return True
|
||||||
|
|
||||||
|
first, second = found.group(1)
|
||||||
|
if first == second:
|
||||||
|
logger.debug(f"{word} starts with a repeated consonant.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
rules = default_rules
|
||||||
|
rules.add(cannot_start_with_repeated_consonants)
|
20
language/languages/elvish/README.md
Normal file
20
language/languages/elvish/README.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
### Elvish
|
||||||
|
|
||||||
|
> The Elvish tongue flows like a river and pools upon the lips like sweetest wine. -- Anwin am Vakaralithien
|
||||||
|
|
||||||
|
Elvish is an ornate, multisyllabic language featuring a complex and nuanced set
|
||||||
|
of vowel sounds that can take non-native speakers decades to master. Surnames
|
||||||
|
are typically derived from place names; the more noble the name, the more
|
||||||
|
baroque its construction.
|
||||||
|
|
||||||
|
*Aaclysmior uewhön fyaeiath nöagefniath iedyglion ryavies, eamwien acyöe, uëstr udrocrem!*
|
||||||
|
|
||||||
|
**Elvish Names:**
|
||||||
|
* Afydyioth um Envyien
|
||||||
|
* Wjillias am Aofll
|
||||||
|
* Anyir um Edyoieth
|
||||||
|
|
||||||
|
**Noble Elvish Names:**
|
||||||
|
* Uebs Sviiies um Udjsmierios
|
||||||
|
* Nytyuia Oudryhn um Ioltier
|
||||||
|
* Emyea Whonoel an Ofyediar
|
7
language/languages/elvish/__init__.py
Normal file
7
language/languages/elvish/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Elvish
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
129
language/languages/elvish/base.py
Normal file
129
language/languages/elvish/base.py
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
from language import types
|
||||||
|
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
vowels = types.WeightedSet(
|
||||||
|
("a", 1.0),
|
||||||
|
("e", 1.0),
|
||||||
|
("i", 0.3),
|
||||||
|
("o", 0.8),
|
||||||
|
("u", 0.7),
|
||||||
|
("y", 0.3),
|
||||||
|
("j", 0.05),
|
||||||
|
("ä", 0.1),
|
||||||
|
("ë", 0.1),
|
||||||
|
("ö", 0.1),
|
||||||
|
("ü", 0.1),
|
||||||
|
)
|
||||||
|
consonants = types.WeightedSet(
|
||||||
|
("b", 0.5),
|
||||||
|
("c", 0.3),
|
||||||
|
("d", 0.3),
|
||||||
|
("f", 0.4),
|
||||||
|
("g", 0.3),
|
||||||
|
("h", 0.5),
|
||||||
|
("k", 0.05),
|
||||||
|
("l", 1.0),
|
||||||
|
("m", 1.0),
|
||||||
|
("n", 1.0),
|
||||||
|
("p", 0.5),
|
||||||
|
("r", 1.0),
|
||||||
|
("s", 1.0),
|
||||||
|
("t", 0.7),
|
||||||
|
("v", 0.3),
|
||||||
|
("w", 0.2),
|
||||||
|
("y", 0.3),
|
||||||
|
) + types.equal_weights(
|
||||||
|
[
|
||||||
|
"ch",
|
||||||
|
"cl",
|
||||||
|
"cr",
|
||||||
|
"cw",
|
||||||
|
"cy",
|
||||||
|
"dh",
|
||||||
|
"dj",
|
||||||
|
"dr",
|
||||||
|
"dw",
|
||||||
|
"dy",
|
||||||
|
"fl",
|
||||||
|
"fn",
|
||||||
|
"fr",
|
||||||
|
"fw",
|
||||||
|
"fy",
|
||||||
|
"gl",
|
||||||
|
"ll",
|
||||||
|
"ly",
|
||||||
|
"ml",
|
||||||
|
"mw",
|
||||||
|
"my",
|
||||||
|
"ny",
|
||||||
|
"rh",
|
||||||
|
"ry",
|
||||||
|
"sh",
|
||||||
|
"sl",
|
||||||
|
"sm",
|
||||||
|
"sn",
|
||||||
|
"st",
|
||||||
|
"sv",
|
||||||
|
"sw",
|
||||||
|
"sy",
|
||||||
|
"th",
|
||||||
|
"tr",
|
||||||
|
"tw",
|
||||||
|
"ty",
|
||||||
|
"vy",
|
||||||
|
"wh",
|
||||||
|
"wr",
|
||||||
|
"wy",
|
||||||
|
"yh",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
suffixes = types.equal_weights(
|
||||||
|
[
|
||||||
|
"a",
|
||||||
|
"i",
|
||||||
|
"e",
|
||||||
|
"t",
|
||||||
|
"s",
|
||||||
|
"m",
|
||||||
|
"n",
|
||||||
|
"l",
|
||||||
|
"r",
|
||||||
|
"d",
|
||||||
|
"a",
|
||||||
|
"th",
|
||||||
|
"ss",
|
||||||
|
"ieth",
|
||||||
|
"ies",
|
||||||
|
"ier",
|
||||||
|
"ien",
|
||||||
|
"iath",
|
||||||
|
"ias",
|
||||||
|
"iar",
|
||||||
|
"ian",
|
||||||
|
"ioth",
|
||||||
|
"ios",
|
||||||
|
"ior",
|
||||||
|
"ion",
|
||||||
|
],
|
||||||
|
weight=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="elvish",
|
||||||
|
vowels=vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=suffixes,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="vowel|consonant") * 3, 0.4),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 3, 0.5),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 4, 1.0),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 5, 0.5),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 6, 0.1),
|
||||||
|
(types.Syllable(template="vowel|consonant") * 7, 0.1),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=3,
|
||||||
|
)
|
77
language/languages/elvish/names.py
Normal file
77
language/languages/elvish/names.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
from language import defaults, types
|
||||||
|
from language.languages.elvish import Language
|
||||||
|
from language.languages.elvish.base import suffixes
|
||||||
|
|
||||||
|
PlaceName = types.NameGenerator(
|
||||||
|
language=Language,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="vowel,vowel|consonant,vowel|consonant"), 1.0),
|
||||||
|
(types.Syllable(template="consonant,vowel|consonant,vowel|consonant"), 0.3),
|
||||||
|
),
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("affix,name"), 1.0),
|
||||||
|
),
|
||||||
|
affixes=types.WeightedSet(("el", 1.0)),
|
||||||
|
adjectives=defaults.adjectives,
|
||||||
|
suffixes=suffixes,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ElvishNameGenerator(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
syllables=Language.syllables,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("name,affix,surname"), 1.0),
|
||||||
|
),
|
||||||
|
affixes=types.equal_weights(["am", "an", "al", "um"], weight=1.0, blank=False),
|
||||||
|
adjectives=defaults.adjectives,
|
||||||
|
titles=defaults.titles,
|
||||||
|
suffixes=suffixes,
|
||||||
|
)
|
||||||
|
self.language.minimum_grapheme_count = 2
|
||||||
|
self.place_generator = PlaceName
|
||||||
|
|
||||||
|
def get_surname(self) -> str:
|
||||||
|
return self.place_generator.name()[0]["name"][0]
|
||||||
|
|
||||||
|
|
||||||
|
class NobleElvishNameGenerator(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
syllables=Language.syllables,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("name,name,affix,surname"), 1.0),
|
||||||
|
),
|
||||||
|
affixes=types.equal_weights(["am", "an", "al", "um"], weight=1.0, blank=False),
|
||||||
|
adjectives=defaults.adjectives,
|
||||||
|
titles=defaults.titles,
|
||||||
|
)
|
||||||
|
self.language.minimum_grapheme_count = 2
|
||||||
|
self.place_generator = PlaceName
|
||||||
|
self.suffixes = types.equal_weights(
|
||||||
|
[
|
||||||
|
"ieth",
|
||||||
|
"ies",
|
||||||
|
"ier",
|
||||||
|
"ien",
|
||||||
|
"iath",
|
||||||
|
"ias",
|
||||||
|
"iar",
|
||||||
|
"ian",
|
||||||
|
"ioth",
|
||||||
|
"ios",
|
||||||
|
"ior",
|
||||||
|
"ion",
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_surname(self) -> str:
|
||||||
|
return self.place_generator.name()[0]["name"][0] + self.suffixes.random()
|
||||||
|
|
||||||
|
|
||||||
|
Name = ElvishNameGenerator()
|
||||||
|
NobleName = NobleElvishNameGenerator()
|
109
language/languages/elvish/rules.py
Normal file
109
language/languages/elvish/rules.py
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from language.types import Language
|
||||||
|
|
||||||
|
logger = logging.getLogger("elvish-rules")
|
||||||
|
|
||||||
|
permitted_starting_clusters = [
|
||||||
|
"ch",
|
||||||
|
"cl",
|
||||||
|
"cr",
|
||||||
|
"cw",
|
||||||
|
"cy",
|
||||||
|
"dh",
|
||||||
|
"dj",
|
||||||
|
"dr",
|
||||||
|
"dw",
|
||||||
|
"dy",
|
||||||
|
"fl",
|
||||||
|
"fn",
|
||||||
|
"fr",
|
||||||
|
"fw",
|
||||||
|
"fy",
|
||||||
|
"gl",
|
||||||
|
"ll",
|
||||||
|
"ly",
|
||||||
|
"ml",
|
||||||
|
"mw",
|
||||||
|
"my",
|
||||||
|
"ny",
|
||||||
|
"rh",
|
||||||
|
"ry",
|
||||||
|
"sh",
|
||||||
|
"sl",
|
||||||
|
"sm",
|
||||||
|
"sn",
|
||||||
|
"st",
|
||||||
|
"sv",
|
||||||
|
"sw",
|
||||||
|
"sy",
|
||||||
|
"th",
|
||||||
|
"tr",
|
||||||
|
"tw",
|
||||||
|
"ty",
|
||||||
|
"vy",
|
||||||
|
"wh",
|
||||||
|
"wr",
|
||||||
|
"wy",
|
||||||
|
"yh",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def cannot_start_with_two_consonants(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"(^[bcdfghklmnpqrstvwxz]{2})").search(word)
|
||||||
|
if not found:
|
||||||
|
return True
|
||||||
|
|
||||||
|
first, second = found.group(1)
|
||||||
|
if first == second and first != "l":
|
||||||
|
logger.debug(f"{word} starts with a repeated consonant.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if found.group(1) not in permitted_starting_clusters:
|
||||||
|
logger.debug("f{word} cannot start with {first}{second}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def too_many_vowels(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"[" + "".join(language.vowels.members) + "]{4}").findall(word)
|
||||||
|
if found == []:
|
||||||
|
return True
|
||||||
|
logger.debug(f"{word} has too many contiguous vowels: {found}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def too_many_consonants(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"[bcdfghklmnprstvw]{3}").findall(word)
|
||||||
|
if found == []:
|
||||||
|
return True
|
||||||
|
logger.debug(f"{word} has too many contiguous consonants: {found}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def cannot_have_just_repeated_vowels(language: Language, word: str) -> bool:
|
||||||
|
if len(word) == 1:
|
||||||
|
return True
|
||||||
|
uniq = {letter for letter in word}
|
||||||
|
if len(uniq) > 1:
|
||||||
|
return True
|
||||||
|
logger.debug(f"{word} consists of only one repeated letter.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def must_have_a_vowel(language: Language, word: str) -> bool:
|
||||||
|
for vowel in language.vowels.members:
|
||||||
|
if vowel in word:
|
||||||
|
return True
|
||||||
|
logger.debug(f"{word} does not contain a vowel.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
rules = {
|
||||||
|
must_have_a_vowel,
|
||||||
|
too_many_vowels,
|
||||||
|
too_many_consonants,
|
||||||
|
cannot_have_just_repeated_vowels,
|
||||||
|
cannot_start_with_two_consonants,
|
||||||
|
}
|
13
language/languages/gnomish/README.md
Normal file
13
language/languages/gnomish/README.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
### Gnomish
|
||||||
|
|
||||||
|
Several distinct dialects of Gnomish exist; a provincial Rock Gnome may not
|
||||||
|
understand a Forest Gnome at all! This is "Golden Gnomish," the mother tongue
|
||||||
|
of these localized variants, and functions much like Common -- a trader's
|
||||||
|
tongue of common understanding.
|
||||||
|
|
||||||
|
*Nuio duy lao guey houe lo nouo, pia roa tai.*
|
||||||
|
|
||||||
|
**Gnomish Names:**
|
||||||
|
* Fuey Voii
|
||||||
|
* Juio See
|
||||||
|
* Tuai Reaa
|
7
language/languages/gnomish/__init__.py
Normal file
7
language/languages/gnomish/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Gnomish
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
48
language/languages/gnomish/base.py
Normal file
48
language/languages/gnomish/base.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
from language import defaults, types
|
||||||
|
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
consonants = types.WeightedSet(
|
||||||
|
("b", 0.5),
|
||||||
|
("d", 0.5),
|
||||||
|
("f", 0.3),
|
||||||
|
("g", 0.3),
|
||||||
|
("h", 0.5),
|
||||||
|
("j", 0.2),
|
||||||
|
("l", 1.0),
|
||||||
|
("m", 0.5),
|
||||||
|
("n", 1.0),
|
||||||
|
("p", 0.5),
|
||||||
|
("r", 1.0),
|
||||||
|
("s", 1.0),
|
||||||
|
("t", 1.0),
|
||||||
|
("v", 0.3),
|
||||||
|
("w", 0.2),
|
||||||
|
("z", 0.1),
|
||||||
|
)
|
||||||
|
|
||||||
|
suffixes = types.equal_weights(
|
||||||
|
[
|
||||||
|
"a",
|
||||||
|
"e",
|
||||||
|
"i",
|
||||||
|
"o",
|
||||||
|
"y",
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
blank=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="gnomish",
|
||||||
|
vowels=defaults.vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=suffixes,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="consonant,vowel,vowel"), 1.0),
|
||||||
|
(types.Syllable(template="consonant,vowel"), 0.33),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=2,
|
||||||
|
)
|
10
language/languages/gnomish/names.py
Normal file
10
language/languages/gnomish/names.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from language import types
|
||||||
|
from language.languages.gnomish import Language
|
||||||
|
|
||||||
|
Name = types.NameGenerator(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("name,surname"), 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
NobleName = Name
|
7
language/languages/gnomish/rules.py
Normal file
7
language/languages/gnomish/rules.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from language.rules import default_rules
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
rules = default_rules
|
13
language/languages/halfling/README.md
Normal file
13
language/languages/halfling/README.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
### Halfling
|
||||||
|
|
||||||
|
Halfings speak a trilling, lilting language with meaning and inflection rooted
|
||||||
|
in meter. It has proven to be nearly impossisble for most non-Halflings to
|
||||||
|
master, to the point that most cosmopolitan Halfings choose a Common nickname
|
||||||
|
by which they are known.
|
||||||
|
|
||||||
|
*Siheme'li'ba'e lo'neno'ne'o, mo'tepa'ga'i wezyropi'a lo'se'ni'metyo zevypiwee vyta'mo'a, da'na'jo'lee ti'soe fe'soe!*
|
||||||
|
|
||||||
|
**Halfling Names:**
|
||||||
|
* Hi'rina'pa'no'y Nylao Pe'lawyle'lo'a "Temperate"
|
||||||
|
* Jo'la'rogi'o Mi'li're'ne'pe'y Terinite've'e "Wise"
|
||||||
|
* Sa'la're'ta'je'o Re'dya Fetobo'a "Meritorious"
|
7
language/languages/halfling/__init__.py
Normal file
7
language/languages/halfling/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Halfling
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
54
language/languages/halfling/base.py
Normal file
54
language/languages/halfling/base.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
from language import types
|
||||||
|
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
vowels = types.equal_weights(["a'", "e'", "i'", "o'"], 1.0, blank=False) + types.equal_weights(
|
||||||
|
["a", "e", "i", "o", "y"], 0.5, blank=False
|
||||||
|
)
|
||||||
|
|
||||||
|
consonants = types.WeightedSet(
|
||||||
|
("b", 0.5),
|
||||||
|
("d", 0.5),
|
||||||
|
("f", 0.3),
|
||||||
|
("g", 0.3),
|
||||||
|
("h", 0.5),
|
||||||
|
("j", 0.2),
|
||||||
|
("l", 1.0),
|
||||||
|
("m", 0.5),
|
||||||
|
("n", 1.0),
|
||||||
|
("p", 0.5),
|
||||||
|
("r", 1.0),
|
||||||
|
("s", 1.0),
|
||||||
|
("t", 1.0),
|
||||||
|
("v", 0.3),
|
||||||
|
("w", 0.2),
|
||||||
|
("z", 0.1),
|
||||||
|
)
|
||||||
|
|
||||||
|
suffixes = types.equal_weights(
|
||||||
|
[
|
||||||
|
"a",
|
||||||
|
"e",
|
||||||
|
"i",
|
||||||
|
"o",
|
||||||
|
"y",
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
blank=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="halfling",
|
||||||
|
vowels=vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=suffixes,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="consonant,vowel") * 2, 0.5),
|
||||||
|
(types.Syllable(template="consonant,vowel") * 3, 0.75),
|
||||||
|
(types.Syllable(template="consonant,vowel") * 4, 1.0),
|
||||||
|
(types.Syllable(template="consonant,vowel") * 5, 1.0),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=2,
|
||||||
|
)
|
17
language/languages/halfling/names.py
Normal file
17
language/languages/halfling/names.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from language import defaults, types
|
||||||
|
from language.languages.halfling import Language
|
||||||
|
|
||||||
|
|
||||||
|
class HalflingNameGenerator(types.NameGenerator):
|
||||||
|
def get_name(self) -> str:
|
||||||
|
return super().get_name().lower().capitalize()
|
||||||
|
|
||||||
|
|
||||||
|
Name = HalflingNameGenerator(
|
||||||
|
language=Language,
|
||||||
|
nicknames=defaults.positive_adjectives,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("name,name,name,nickname"), 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
NobleName = Name
|
7
language/languages/halfling/rules.py
Normal file
7
language/languages/halfling/rules.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from language.rules import default_rules
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
rules = default_rules
|
19
language/languages/infernal/README.md
Normal file
19
language/languages/infernal/README.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
### Infernal
|
||||||
|
|
||||||
|
The language of devils is a collection of gutteral barks that is unmistakable,
|
||||||
|
and unmistakably terrifying, to the ear of common folk. Traditional names
|
||||||
|
typically begin with the name of the ruler of whatever circle of Hell the devil
|
||||||
|
calls home, but some Tieflings (and others of Infernal origin) reject their
|
||||||
|
Infernal legacies and choose less noble names.
|
||||||
|
|
||||||
|
*Z'zait cocf k'd z'xz t'ugoo! Yipd k'j x'euy, z'edu k'oczu!*
|
||||||
|
|
||||||
|
**Infernal Names:**
|
||||||
|
* P'Utufus
|
||||||
|
* Golden Bdaeus
|
||||||
|
* Timeless Ujius
|
||||||
|
|
||||||
|
**Noble Infernal Names:**
|
||||||
|
* Mephistopheles T'Xieis
|
||||||
|
* Asmodeus T'Opakan
|
||||||
|
* Mammon K'Nusan
|
7
language/languages/infernal/__init__.py
Normal file
7
language/languages/infernal/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Infernal
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
52
language/languages/infernal/base.py
Normal file
52
language/languages/infernal/base.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
from language import defaults, types
|
||||||
|
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
consonants = types.WeightedSet(
|
||||||
|
("b", 1.0),
|
||||||
|
("c", 1.0),
|
||||||
|
("d", 1.0),
|
||||||
|
("f", 0.5),
|
||||||
|
("g", 0.5),
|
||||||
|
("j", 1.0),
|
||||||
|
("k", 1.0),
|
||||||
|
("l", 0.3),
|
||||||
|
("m", 0.3),
|
||||||
|
("n", 0.3),
|
||||||
|
("p", 1.0),
|
||||||
|
("r", 0.2),
|
||||||
|
("s", 0.1),
|
||||||
|
("t", 1.0),
|
||||||
|
("v", 1.0),
|
||||||
|
("x", 1.0),
|
||||||
|
("y", 1.0),
|
||||||
|
("z", 1.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
prefixes = types.equal_weights(
|
||||||
|
[
|
||||||
|
"t'",
|
||||||
|
"x'",
|
||||||
|
"k'",
|
||||||
|
"p'",
|
||||||
|
"z'",
|
||||||
|
],
|
||||||
|
0.5,
|
||||||
|
)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="infernal",
|
||||||
|
vowels=defaults.vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=prefixes,
|
||||||
|
suffixes=None,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="consonant|vowel") * 2, 0.05),
|
||||||
|
(types.Syllable(template="consonant|vowel") * 3, 1.0),
|
||||||
|
(types.Syllable(template="consonant|vowel") * 4, 0.75),
|
||||||
|
(types.Syllable(template="consonant|vowel") * 5, 0.5),
|
||||||
|
(types.Syllable(template="consonant|vowel") * 6, 0.25),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=2,
|
||||||
|
)
|
87
language/languages/infernal/names.py
Normal file
87
language/languages/infernal/names.py
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
from language import types
|
||||||
|
from language.languages.infernal import Language
|
||||||
|
|
||||||
|
adjectives = types.equal_weights(
|
||||||
|
[
|
||||||
|
"eternal",
|
||||||
|
"wondrous",
|
||||||
|
"luminous",
|
||||||
|
"perfect",
|
||||||
|
"essential",
|
||||||
|
"golden",
|
||||||
|
"unfailing",
|
||||||
|
"perpetual",
|
||||||
|
"infinite",
|
||||||
|
"exquisite",
|
||||||
|
"sinless",
|
||||||
|
"ultimate",
|
||||||
|
"flawless",
|
||||||
|
"timeless",
|
||||||
|
"glorious",
|
||||||
|
"absolute",
|
||||||
|
"boundless",
|
||||||
|
"true",
|
||||||
|
"incredible",
|
||||||
|
"virtuous",
|
||||||
|
"supreme",
|
||||||
|
"enchanted",
|
||||||
|
"magnificent",
|
||||||
|
"superior",
|
||||||
|
"spectacular",
|
||||||
|
"divine",
|
||||||
|
],
|
||||||
|
0.25,
|
||||||
|
)
|
||||||
|
|
||||||
|
bloodlines = types.equal_weights(
|
||||||
|
[
|
||||||
|
"Asmodeus",
|
||||||
|
"Baalzebul",
|
||||||
|
"Rimmon",
|
||||||
|
"Dispater",
|
||||||
|
"Fierna",
|
||||||
|
"Glasya",
|
||||||
|
"Levistus",
|
||||||
|
"Mammon",
|
||||||
|
"Mephistopheles",
|
||||||
|
"Zariel",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class InfernalNameGenerator(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
# (types.NameTemplate("adjective,name,nickname,surname"), 1.0),
|
||||||
|
(types.NameTemplate("name"), 0.25),
|
||||||
|
(types.NameTemplate("adjective,name"), 0.25),
|
||||||
|
),
|
||||||
|
adjectives=adjectives,
|
||||||
|
)
|
||||||
|
self.language.minimum_grapheme_count = 2
|
||||||
|
self.suffixes = types.equal_weights(["us", "ius", "eus", "a", "an", "is"], 1.0, blank=False)
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
return super().get_name() + self.suffixes.random()
|
||||||
|
|
||||||
|
|
||||||
|
class NobleInfernalNameGenerator(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("adjective,name"), 1.0),
|
||||||
|
),
|
||||||
|
adjectives=bloodlines,
|
||||||
|
)
|
||||||
|
self.language.minimum_grapheme_count = 2
|
||||||
|
self.suffixes = types.equal_weights(["us", "ius", "to", "tro", "eus", "a", "an", "is"], 1.0, blank=False)
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
return super().get_name() + self.suffixes.random()
|
||||||
|
|
||||||
|
|
||||||
|
Name = InfernalNameGenerator()
|
||||||
|
NobleName = NobleInfernalNameGenerator()
|
7
language/languages/infernal/rules.py
Normal file
7
language/languages/infernal/rules.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from language.rules import default_rules
|
||||||
|
|
||||||
|
logger = logging.getLogger("infernal-rules")
|
||||||
|
|
||||||
|
rules = default_rules
|
14
language/languages/lizardfolk/README.md
Normal file
14
language/languages/lizardfolk/README.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
### Lizardfolk
|
||||||
|
|
||||||
|
The Lizardfolk language is a unique form of telepathy borne by pheramonne
|
||||||
|
exchange. It is literally impossible for non-Lizardfolk to speak it, though
|
||||||
|
with practice they can learn to recognize particular smells. Lizardfolk names
|
||||||
|
are collections of scent pattterns, the first of which denotes the Lizardfolk's
|
||||||
|
family origins.
|
||||||
|
|
||||||
|
*[No written language]*
|
||||||
|
|
||||||
|
**Lizardfolk Name Scents:**
|
||||||
|
* Carmelized Roast Pomegranate
|
||||||
|
* Rotting Hazelnut Citric
|
||||||
|
* Pepper Broth Grape
|
7
language/languages/lizardfolk/__init__.py
Normal file
7
language/languages/lizardfolk/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Lizardfolk
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
118
language/languages/lizardfolk/base.py
Normal file
118
language/languages/lizardfolk/base.py
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
from language import types
|
||||||
|
|
||||||
|
scents = types.equal_weights(
|
||||||
|
[
|
||||||
|
"honey",
|
||||||
|
"caramel",
|
||||||
|
"maple syrup",
|
||||||
|
"molasses",
|
||||||
|
"dark chocolate",
|
||||||
|
"chocolate",
|
||||||
|
"almond",
|
||||||
|
"hazelnut",
|
||||||
|
"peanut",
|
||||||
|
"clove",
|
||||||
|
"cinnamon",
|
||||||
|
"nutmeg",
|
||||||
|
"anise",
|
||||||
|
"malt",
|
||||||
|
"grain",
|
||||||
|
"roast",
|
||||||
|
"smoke",
|
||||||
|
"ash",
|
||||||
|
"acrid",
|
||||||
|
"rubber",
|
||||||
|
"skunk",
|
||||||
|
"petroleum",
|
||||||
|
"medicine",
|
||||||
|
"salt",
|
||||||
|
"bitter",
|
||||||
|
"phrenolic",
|
||||||
|
"meat",
|
||||||
|
"broth",
|
||||||
|
"animal",
|
||||||
|
"musty",
|
||||||
|
"earth",
|
||||||
|
"mould",
|
||||||
|
"damp",
|
||||||
|
"wood",
|
||||||
|
"paper",
|
||||||
|
"cardboard",
|
||||||
|
"stale",
|
||||||
|
"herb",
|
||||||
|
"hay",
|
||||||
|
"grass",
|
||||||
|
"peapod",
|
||||||
|
"whisky",
|
||||||
|
"wine",
|
||||||
|
"malic",
|
||||||
|
"citric",
|
||||||
|
"isovaleric",
|
||||||
|
"butyric",
|
||||||
|
"acetic",
|
||||||
|
"lime",
|
||||||
|
"lemon",
|
||||||
|
"orange",
|
||||||
|
"grapefruit",
|
||||||
|
"pear",
|
||||||
|
"peach",
|
||||||
|
"apple",
|
||||||
|
"grape",
|
||||||
|
"pineapple",
|
||||||
|
"pomegranate",
|
||||||
|
"cherry",
|
||||||
|
"coconut",
|
||||||
|
"prune",
|
||||||
|
"raisin",
|
||||||
|
"strawberry",
|
||||||
|
"blueberry",
|
||||||
|
"raspberry",
|
||||||
|
"blackberry",
|
||||||
|
"jasmine",
|
||||||
|
"rose",
|
||||||
|
"camomile",
|
||||||
|
"tobacco",
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
blank=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
family = types.equal_weights(
|
||||||
|
[
|
||||||
|
"sweet",
|
||||||
|
"floral",
|
||||||
|
"fruity",
|
||||||
|
"sour",
|
||||||
|
"fermented",
|
||||||
|
"green",
|
||||||
|
"vegetal",
|
||||||
|
"old",
|
||||||
|
"roasted",
|
||||||
|
"spiced",
|
||||||
|
"nutty",
|
||||||
|
"cocoa",
|
||||||
|
"pepper",
|
||||||
|
"pungent",
|
||||||
|
"burnt",
|
||||||
|
"carmelized",
|
||||||
|
"raw",
|
||||||
|
"rotting",
|
||||||
|
"dead",
|
||||||
|
"young",
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
blank=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="lizardfolk",
|
||||||
|
vowels=scents,
|
||||||
|
consonants=family,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=None,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="vowel"), 1.0),
|
||||||
|
),
|
||||||
|
rules=(),
|
||||||
|
minimum_grapheme_count=1,
|
||||||
|
)
|
19
language/languages/lizardfolk/names.py
Normal file
19
language/languages/lizardfolk/names.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
from language import types
|
||||||
|
from language.languages.lizardfolk import Language
|
||||||
|
|
||||||
|
|
||||||
|
class LizardfolkNameGenerator(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("surname,name,name"), 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_surname(self) -> str:
|
||||||
|
return self.language.consonants.random().title()
|
||||||
|
|
||||||
|
|
||||||
|
Name = LizardfolkNameGenerator()
|
||||||
|
NobleName = Name
|
13
language/languages/orcish/README.md
Normal file
13
language/languages/orcish/README.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
### Orcish
|
||||||
|
|
||||||
|
Spoken by Full- and Half-Orcs alike, Orcish bears some similarlities to
|
||||||
|
Dwarvish's clipped, mono-syllablic construction. They differ in Half-Orc's
|
||||||
|
prominent use of sibilants, perhaps as a result of being spoken by people with
|
||||||
|
large, protruding tusks.
|
||||||
|
|
||||||
|
*Dbod hanot neb, hushi dupo shiza fesha keke fucha tpu.*
|
||||||
|
|
||||||
|
**Orcish Names:**
|
||||||
|
* "Mad" Nha Pazk
|
||||||
|
* Zashi Dizh
|
||||||
|
* Pik Decht
|
7
language/languages/orcish/__init__.py
Normal file
7
language/languages/orcish/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Orcish
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
36
language/languages/orcish/base.py
Normal file
36
language/languages/orcish/base.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from language import defaults, types
|
||||||
|
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
consonants = types.WeightedSet(
|
||||||
|
("b", 1.0),
|
||||||
|
("c", 1.0),
|
||||||
|
("d", 1.0),
|
||||||
|
("f", 0.5),
|
||||||
|
("h", 1.0),
|
||||||
|
("k", 1.0),
|
||||||
|
("m", 0.3),
|
||||||
|
("n", 0.3),
|
||||||
|
("p", 1.0),
|
||||||
|
("r", 0.2),
|
||||||
|
("s", 0.1),
|
||||||
|
("t", 1.0),
|
||||||
|
("z", 1.0),
|
||||||
|
("ch", 1.0),
|
||||||
|
("sh", 0.7),
|
||||||
|
("br", 1.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="orcish",
|
||||||
|
vowels=defaults.vowels,
|
||||||
|
consonants=consonants,
|
||||||
|
prefixes=None,
|
||||||
|
suffixes=None,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="consonant,vowel") * 2, 1.0),
|
||||||
|
(types.Syllable(template="consonant,vowel,consonant,vowel,consonant"), 0.5),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=1,
|
||||||
|
)
|
60
language/languages/orcish/names.py
Normal file
60
language/languages/orcish/names.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
from language import defaults, types
|
||||||
|
from language.languages.orcish import Language
|
||||||
|
|
||||||
|
|
||||||
|
class OrcishNameGenerator(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
templates=types.NameSet(
|
||||||
|
# (types.NameTemplate("adjective,name,nickname,surname"), 1.0),
|
||||||
|
(types.NameTemplate("adjective,name,surname"), 1.0),
|
||||||
|
),
|
||||||
|
affixes=None,
|
||||||
|
adjectives=defaults.adjectives,
|
||||||
|
titles=defaults.titles,
|
||||||
|
)
|
||||||
|
self.language.minimum_grapheme_count = 2
|
||||||
|
self.suffixes = types.equal_weights(
|
||||||
|
[
|
||||||
|
"acht",
|
||||||
|
"echt",
|
||||||
|
"icht",
|
||||||
|
"ocht",
|
||||||
|
"ucht",
|
||||||
|
"ak",
|
||||||
|
"ek",
|
||||||
|
"ik",
|
||||||
|
"ok",
|
||||||
|
"uk",
|
||||||
|
"ach",
|
||||||
|
"ech",
|
||||||
|
"ich",
|
||||||
|
"och",
|
||||||
|
"uch",
|
||||||
|
"atch",
|
||||||
|
"etch",
|
||||||
|
"itch",
|
||||||
|
"otch",
|
||||||
|
"utch",
|
||||||
|
"azk",
|
||||||
|
"ezk",
|
||||||
|
"izk",
|
||||||
|
"ozk",
|
||||||
|
"uzk",
|
||||||
|
"azh",
|
||||||
|
"ezh",
|
||||||
|
"izh",
|
||||||
|
"ozh",
|
||||||
|
"uzh",
|
||||||
|
],
|
||||||
|
1.0,
|
||||||
|
blank=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_surname(self) -> str:
|
||||||
|
return self.language.add_grapheme(word="", template="consonant").strip().title() + self.suffixes.random()
|
||||||
|
|
||||||
|
|
||||||
|
Name = OrcishNameGenerator()
|
||||||
|
NobleName = Name
|
24
language/languages/orcish/rules.py
Normal file
24
language/languages/orcish/rules.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from language.rules import default_rules
|
||||||
|
from language.types import Language
|
||||||
|
|
||||||
|
logger = logging.getLogger("orcish-rules")
|
||||||
|
|
||||||
|
|
||||||
|
def cannot_start_with_repeated_consonants(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"(^[bcdfghklmnpqrstvwxz]{3})").search(word)
|
||||||
|
if not found:
|
||||||
|
return True
|
||||||
|
|
||||||
|
first, second, third = found.group(1)
|
||||||
|
if first == second == third:
|
||||||
|
logger.debug(f"{word} starts with a repeated consonant.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
rules = default_rules
|
||||||
|
rules.add(cannot_start_with_repeated_consonants)
|
18
language/languages/undercommon/README.md
Normal file
18
language/languages/undercommon/README.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
### Undercommon
|
||||||
|
|
||||||
|
The language of the Drow is the defacto language of the Underdark, and is
|
||||||
|
spoken by most peoples there. Like the Drow themselves, Undercommon diverged
|
||||||
|
from an Elvish dialect in ancient times. The two still bear some resemblance,
|
||||||
|
notably in the construction of names.
|
||||||
|
|
||||||
|
*Okvösaa licopod lohiwia uüyötüe uywubit uegäsit uäpisoa, caquyee redoxöd, mraomoa.*
|
||||||
|
|
||||||
|
**Undercommon Names:**
|
||||||
|
* Tfubam Umaiuhüen
|
||||||
|
* Nanosd Aneedaöbaas
|
||||||
|
* Rerwus Amzuremolr
|
||||||
|
|
||||||
|
**Noble Undercommon (Drow) Names:**
|
||||||
|
* Uopiyie Alzejakäurn
|
||||||
|
* Opyäulol Elchöläss
|
||||||
|
* Iqyäuwed Anlcoduir
|
7
language/languages/undercommon/__init__.py
Normal file
7
language/languages/undercommon/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Undercommon
|
||||||
|
"""
|
||||||
|
from .base import Language
|
||||||
|
from .names import Name, NobleName
|
||||||
|
|
||||||
|
__all__ = [Language, Name, NobleName]
|
21
language/languages/undercommon/base.py
Normal file
21
language/languages/undercommon/base.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from language import defaults, types
|
||||||
|
|
||||||
|
from .rules import rules
|
||||||
|
|
||||||
|
vowels = defaults.vowels + types.equal_weights(["ä", "ö", "ü", "äu"], 0.5, blank=False)
|
||||||
|
prefixes = defaults.vowels + types.equal_weights(["c", "g", "l", "m", "n", "r", "s", "t", "v", "z"], 1.0, blank=False)
|
||||||
|
suffixes = types.equal_weights(["a", "e", "i", "t", "s", "m", "n", "l", "r", "d", "a", "th"], 1.0, blank=False)
|
||||||
|
|
||||||
|
Language = types.Language(
|
||||||
|
name="undercommon",
|
||||||
|
vowels=vowels,
|
||||||
|
consonants=defaults.consonants,
|
||||||
|
prefixes=prefixes,
|
||||||
|
suffixes=suffixes,
|
||||||
|
syllables=types.SyllableSet(
|
||||||
|
(types.Syllable(template="vowel,consonant,vowel") * 2, 0.15),
|
||||||
|
(types.Syllable(template="consonant|vowel,consonant,vowel,consonant,vowel"), 1.0),
|
||||||
|
),
|
||||||
|
rules=rules,
|
||||||
|
minimum_grapheme_count=2,
|
||||||
|
)
|
37
language/languages/undercommon/names.py
Normal file
37
language/languages/undercommon/names.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import random
|
||||||
|
|
||||||
|
from language import defaults, types
|
||||||
|
from language.languages.undercommon import Language
|
||||||
|
|
||||||
|
PlaceName = types.NameGenerator(
|
||||||
|
language=Language,
|
||||||
|
syllables=Language.syllables,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("affix,name"), 1.0),
|
||||||
|
),
|
||||||
|
affixes=types.WeightedSet(("el", 1.0)),
|
||||||
|
adjectives=defaults.adjectives,
|
||||||
|
suffixes=Language.suffixes,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DrowName(types.NameGenerator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
language=Language,
|
||||||
|
syllables=Language.syllables,
|
||||||
|
templates=types.NameSet(
|
||||||
|
(types.NameTemplate("name,surname"), 1.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.language.minimum_grapheme_count = 2
|
||||||
|
self.place_generator = PlaceName
|
||||||
|
self.affixes = types.equal_weights(["am", "an", "al", "um"], weight=1.0, blank=False)
|
||||||
|
|
||||||
|
def get_surname(self) -> str:
|
||||||
|
name = self.place_generator.name()[0]["name"][0]
|
||||||
|
return (self.affixes.random() + name + random.choice(["th", "s", "r", "n"])).title()
|
||||||
|
|
||||||
|
|
||||||
|
Name = DrowName()
|
||||||
|
NobleName = Name
|
88
language/languages/undercommon/rules.py
Normal file
88
language/languages/undercommon/rules.py
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from language.rules import default_rules
|
||||||
|
from language.types import Language
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
valid_consonant_sequences = [
|
||||||
|
"cc",
|
||||||
|
"ht",
|
||||||
|
"kd",
|
||||||
|
"kl",
|
||||||
|
"km",
|
||||||
|
"kp",
|
||||||
|
"kt",
|
||||||
|
"kv",
|
||||||
|
"kw",
|
||||||
|
"ky",
|
||||||
|
"lc",
|
||||||
|
"ld",
|
||||||
|
"lf",
|
||||||
|
"ll",
|
||||||
|
"lm",
|
||||||
|
"lp",
|
||||||
|
"lt",
|
||||||
|
"lv",
|
||||||
|
"lw",
|
||||||
|
"ly",
|
||||||
|
"mb",
|
||||||
|
"mm",
|
||||||
|
"mp",
|
||||||
|
"my",
|
||||||
|
"nc",
|
||||||
|
"nd",
|
||||||
|
"ng",
|
||||||
|
"nn",
|
||||||
|
"nt",
|
||||||
|
"nw",
|
||||||
|
"ny",
|
||||||
|
"ps",
|
||||||
|
"pt",
|
||||||
|
"rc",
|
||||||
|
"rd",
|
||||||
|
"rm",
|
||||||
|
"rn",
|
||||||
|
"rp",
|
||||||
|
"rr",
|
||||||
|
"rs",
|
||||||
|
"rt",
|
||||||
|
"rw",
|
||||||
|
"ry",
|
||||||
|
"sc",
|
||||||
|
"ss",
|
||||||
|
"ts",
|
||||||
|
"tt",
|
||||||
|
"th",
|
||||||
|
"tw",
|
||||||
|
"ty",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def valid_sequences(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"([bcdfghjklmnpqrstvwxz]{2})").findall(word)
|
||||||
|
if not found:
|
||||||
|
return True
|
||||||
|
|
||||||
|
invalid = [seq for seq in found if seq not in valid_consonant_sequences]
|
||||||
|
if invalid:
|
||||||
|
logger.debug(f"{word} contains invalid consonant sequences: {invalid}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def too_many_vowels(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"[" + "".join(language.vowels.members) + r"]{3}").findall(word)
|
||||||
|
if found == []:
|
||||||
|
return True
|
||||||
|
logger.debug(f"{word} has too many contiguous vowels: {found}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
rules = default_rules.union(
|
||||||
|
{
|
||||||
|
valid_sequences,
|
||||||
|
too_many_vowels,
|
||||||
|
}
|
||||||
|
)
|
43
language/rules.py
Normal file
43
language/rules.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from language.types import Language
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
def too_many_vowels(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"[aeiou]{3}").findall(word)
|
||||||
|
if found == []:
|
||||||
|
return True
|
||||||
|
logger.debug(f"{word} has too many contiguous vowels: {found}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def too_many_consonants(language: Language, word: str) -> bool:
|
||||||
|
found = re.compile(r"[bcdfghjklmnpqrstvwxz]{3}").findall(word)
|
||||||
|
if found == []:
|
||||||
|
return True
|
||||||
|
logger.debug(f"{word} has too many contiguous consonants: {found}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def cannot_have_just_repeated_vowels(language: Language, word: str) -> bool:
|
||||||
|
if len(word) == 1:
|
||||||
|
return True
|
||||||
|
uniq = {letter for letter in word}
|
||||||
|
if len(uniq) > 1:
|
||||||
|
return True
|
||||||
|
logger.debug(f"{word} consists of only one repeated letter.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def must_have_a_vowel(language: Language, word: str) -> bool:
|
||||||
|
for vowel in language.vowels.members:
|
||||||
|
if vowel in word:
|
||||||
|
return True
|
||||||
|
logger.debug(f"{word} does not contain a vowel.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
default_rules = {must_have_a_vowel, too_many_vowels, too_many_consonants, cannot_have_just_repeated_vowels}
|
461
language/types.py
Normal file
461
language/types.py
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
import inspect
|
||||||
|
import random
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageError(Exception):
|
||||||
|
"""
|
||||||
|
Thrown when an error is encountered in language construction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ImprobableTemplateError(Exception):
|
||||||
|
"""
|
||||||
|
Thrown when too many successive attempts to create a word which passes all
|
||||||
|
language rules fails.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class WeightedSet:
|
||||||
|
"""
|
||||||
|
A set in which members each have a weight, used for selecting at random.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
>>> ws = WeightedSet(('foo', 1.0), ('bar', 0.5))
|
||||||
|
>>> ws.random()
|
||||||
|
('foo', 1.0)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *weighted_members: tuple):
|
||||||
|
self.members = []
|
||||||
|
self.weights = []
|
||||||
|
if weighted_members:
|
||||||
|
self.members, self.weights = list(zip(*weighted_members))
|
||||||
|
|
||||||
|
def random(self) -> str:
|
||||||
|
return random.choices(self.members, self.weights)[0]
|
||||||
|
|
||||||
|
def __add__(self, obj):
|
||||||
|
ws = WeightedSet()
|
||||||
|
ws.members = self.members + obj.members
|
||||||
|
ws.weights = self.weights + obj.weights
|
||||||
|
return ws
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.members}\n{self.weights}"
|
||||||
|
|
||||||
|
|
||||||
|
class Syllable:
|
||||||
|
"""
|
||||||
|
One syllable of a word. Used to populate a SyllableSet.
|
||||||
|
|
||||||
|
A syllable template is a string consisting of one or more grapheme types
|
||||||
|
separated by a vertical pipe (|). Multiple template strings can be
|
||||||
|
concatenated together with commas. When words are constructed, each
|
||||||
|
syllable is populated with a random sequence chosen by
|
||||||
|
Language.add_grapheme().
|
||||||
|
|
||||||
|
A syllable template must contain at least one 'vowel'.
|
||||||
|
|
||||||
|
Syllables can be multiplied by integers to produce repeated templates.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
# A syllable consisting of either a vowel or a consonant, followed by
|
||||||
|
# a vowel, followed by either a vowel or consonant.
|
||||||
|
>>> foo = Syllable(template='vowel|consonant,vowel,consonant|vowel')
|
||||||
|
|
||||||
|
# Example multiplication
|
||||||
|
>>> print(Syllable(template='vowel|consonant') * 3)
|
||||||
|
vowel|consonant vowel|consonant vowel|consonant
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, template: str = "vowel|consonant"):
|
||||||
|
self.template = template
|
||||||
|
self.validate()
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
if "vowel" not in self.template:
|
||||||
|
raise LanguageError(
|
||||||
|
f"Invalid syllable template {self.template}!\n"
|
||||||
|
"Syllables must have at least one vowel in the template."
|
||||||
|
)
|
||||||
|
|
||||||
|
def __mul__(self, count: int):
|
||||||
|
return Syllable(template=",".join([self.template] * count))
|
||||||
|
|
||||||
|
__rmul__ = __mul__
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.template
|
||||||
|
|
||||||
|
|
||||||
|
class SyllableSet(WeightedSet):
|
||||||
|
"""
|
||||||
|
A WeightedSet that selects random syllables.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
>>> word = SyllableSet(
|
||||||
|
(Syllable('vowel'), 1.0),
|
||||||
|
(Syllable('vowel|consonant') * 2, 1.0),
|
||||||
|
(syllable('vowel|consonant,vowel|consonant') * 3, 0.75)
|
||||||
|
)
|
||||||
|
>>> word.random()
|
||||||
|
vowel consonant consonant vowel
|
||||||
|
"""
|
||||||
|
|
||||||
|
def random(self) -> iter:
|
||||||
|
for syllable in random.choices(self.members, self.weights)[0].template.split(","):
|
||||||
|
grapheme_template = random.choice(syllable.split("|"))
|
||||||
|
yield grapheme_template.lower()
|
||||||
|
|
||||||
|
|
||||||
|
class Language:
|
||||||
|
"""
|
||||||
|
A class representing a language.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
>>> Common = Language(
|
||||||
|
name="common",
|
||||||
|
vowels=WeightedSet(("a", 1.0), ("e", 1.0), ("i", 1.0), ...),
|
||||||
|
consonants=WeightedSet(("b", 0.5), ("c", 0.5), ("d", 0.5), ...),
|
||||||
|
prefixes=WeightedSet(("re", 0.5), ("de", 0.5), ("", 1.0), ...),
|
||||||
|
suffixes=WeightedSet(("ed", 0.5), ("ing", 0.5), ("", 1.0), ...),
|
||||||
|
syllables=SyllableSet(
|
||||||
|
(Syllable('consonant|vowel'), 1.0),
|
||||||
|
(Syllable('consonant|vowel') * 2, 0.75),
|
||||||
|
...
|
||||||
|
),
|
||||||
|
rules=set(callable1, callable2, ...),
|
||||||
|
minimum_grapheme_count=2,
|
||||||
|
)
|
||||||
|
>>> Common.word()
|
||||||
|
reibing
|
||||||
|
|
||||||
|
How Words Are Constructed:
|
||||||
|
|
||||||
|
The main interface for callers is word(), which returns a
|
||||||
|
randomly-generated word in the language according to the following
|
||||||
|
algorithm:
|
||||||
|
|
||||||
|
1. Choose a random syllable from the syllable set
|
||||||
|
2. For each grapheme in the syllable
|
||||||
|
3. Choose a random grapheme template
|
||||||
|
4. Choose a random sequence from the language for that grapheme
|
||||||
|
5. Validate the word against the language rules
|
||||||
|
6. Repeat 1-5 until a valid word is generated
|
||||||
|
7. Add a prefix and suffix, if they are defined
|
||||||
|
|
||||||
|
The following graphemes are supported by default:
|
||||||
|
- vowel
|
||||||
|
- consonant
|
||||||
|
- prefix
|
||||||
|
- suffix
|
||||||
|
|
||||||
|
When graphemes are chosen, the following rules are applied:
|
||||||
|
- Every syllable must have at least one vowel
|
||||||
|
- A syllable may never have three consecutive consonants
|
||||||
|
|
||||||
|
How Words Are Validated:
|
||||||
|
|
||||||
|
Once a word has been constructed by populating syllable templates, it is
|
||||||
|
tested against one or more language rules.
|
||||||
|
|
||||||
|
The default rules are defined in language.rules.default_rules; they are:
|
||||||
|
- the word must contain at least one vowel
|
||||||
|
- the word must not contain 3 or more contiguous english vowels
|
||||||
|
- the word must not contain 3 or more contiguous english consonants
|
||||||
|
- the word must not consist of just one vowel, repeated
|
||||||
|
|
||||||
|
Since it is possible to craft Syllables resulting in grapheme
|
||||||
|
selections that rarely or never yield valid words, or rules that
|
||||||
|
reject every word, an ImprobableTemplateError will be thrown if
|
||||||
|
10 successive attempts to create a valid word fail.
|
||||||
|
|
||||||
|
Extending Languages:
|
||||||
|
|
||||||
|
Graphemes are populated by means of callbacks which select a member
|
||||||
|
of the associated weighted set at random. Graphemes can be any string,
|
||||||
|
so long as the Language class has a matching callback.
|
||||||
|
|
||||||
|
To add support for a new grapheme type, define a method on your
|
||||||
|
Language class called get_grapheme_TYPE, where TYPE is the string
|
||||||
|
used in your Syllable templates. Examine test cases in test_types.py
|
||||||
|
for examples.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
vowels: WeightedSet,
|
||||||
|
consonants: WeightedSet,
|
||||||
|
prefixes: WeightedSet,
|
||||||
|
suffixes: WeightedSet,
|
||||||
|
syllables: SyllableSet,
|
||||||
|
rules: set = set(),
|
||||||
|
minimum_grapheme_count: int = 1,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
name - friendly name for the language
|
||||||
|
vowels - the weighted set of vowel graphemes
|
||||||
|
consonants - the weighted set of consonant graphemes
|
||||||
|
prefixes - the weighted set of prefix graphemes
|
||||||
|
suffixes - the weighted set of suffix graphemes
|
||||||
|
rules - a set of rules callbacks; see above.
|
||||||
|
minimum_grapheme_count - the minimum number of graphemes in each word
|
||||||
|
"""
|
||||||
|
self.name = name
|
||||||
|
self.vowels = vowels
|
||||||
|
self.consonants = consonants
|
||||||
|
self.prefixes = prefixes
|
||||||
|
self.suffixes = suffixes
|
||||||
|
self.rules = rules
|
||||||
|
self.syllables = syllables
|
||||||
|
self.minimum_grapheme_count = minimum_grapheme_count
|
||||||
|
self.validate_syllable_set()
|
||||||
|
|
||||||
|
self.handlers = dict([(n, v) for (n, v) in inspect.getmembers(self, inspect.ismethod) if n.startswith("get_")])
|
||||||
|
|
||||||
|
def validate(self, word: str) -> bool:
|
||||||
|
"""
|
||||||
|
Returns true if the given word is possible in the current language.
|
||||||
|
"""
|
||||||
|
if not word:
|
||||||
|
return False
|
||||||
|
for rule in self.rules:
|
||||||
|
if not rule(self, word):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_syllable_set(self):
|
||||||
|
for syllable in self.syllables.members:
|
||||||
|
if len(syllable.template.split(",")) < self.minimum_grapheme_count:
|
||||||
|
raise ImprobableTemplateError(
|
||||||
|
f"Syllable {syllable} does not define enough graphemes ({self.minimum_grapheme_count} required)."
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate_graphemes(self, graphemes: list) -> bool:
|
||||||
|
if len(graphemes) < self.minimum_grapheme_count:
|
||||||
|
return False
|
||||||
|
|
||||||
|
last = ""
|
||||||
|
count = 0
|
||||||
|
for g in graphemes:
|
||||||
|
if g == last:
|
||||||
|
count += 1
|
||||||
|
if count == 3:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
count = 1
|
||||||
|
last = g
|
||||||
|
return True
|
||||||
|
|
||||||
|
def word(self, count: int = 1) -> list:
|
||||||
|
"""
|
||||||
|
Yields words composed of randomized phonemes built from a random word template.
|
||||||
|
"""
|
||||||
|
words = []
|
||||||
|
for _ in range(count):
|
||||||
|
random_word = ""
|
||||||
|
attempts = 0
|
||||||
|
while not self.validate(random_word):
|
||||||
|
if attempts == 10:
|
||||||
|
raise ImprobableTemplateError(
|
||||||
|
f"Exhausted all attempts to create a valid word. Last attempt: {random_word}. "
|
||||||
|
"If you're getting this a lot, try enabling debugging to see what rules are failing."
|
||||||
|
)
|
||||||
|
graphemes = []
|
||||||
|
random_word = ""
|
||||||
|
while not self.validate_graphemes(graphemes):
|
||||||
|
graphemes = list(self.syllables.random())
|
||||||
|
for grapheme in graphemes:
|
||||||
|
random_word = self.add_grapheme(random_word, grapheme)
|
||||||
|
attempts += 1
|
||||||
|
if self.prefixes:
|
||||||
|
random_word = self.get_grapheme_prefix() + random_word
|
||||||
|
if self.suffixes:
|
||||||
|
random_word = random_word + self.get_grapheme_suffix()
|
||||||
|
words.append(random_word)
|
||||||
|
return words
|
||||||
|
|
||||||
|
def add_grapheme(self, word: str, template: str) -> str:
|
||||||
|
"""
|
||||||
|
Returns a random grapheme of a supported type. The class must support a method of the name:
|
||||||
|
get_grapheme_{template}
|
||||||
|
"""
|
||||||
|
template = template.lower()
|
||||||
|
try:
|
||||||
|
return word + self.handlers[f"get_grapheme_{template}"]()
|
||||||
|
except KeyError:
|
||||||
|
raise NotImplementedError(
|
||||||
|
f"No handler found for grapheme template '{template}'. "
|
||||||
|
f"Do you need to define get_grapheme_{template}()?\n"
|
||||||
|
"Supported handlers: " + self.handlers.keys
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_grapheme_consonant(self) -> str:
|
||||||
|
return self.consonants.random()
|
||||||
|
|
||||||
|
def get_grapheme_vowel(self) -> str:
|
||||||
|
return self.vowels.random()
|
||||||
|
|
||||||
|
def get_grapheme_prefix(self) -> str:
|
||||||
|
return self.prefixes.random()
|
||||||
|
|
||||||
|
def get_grapheme_suffix(self) -> str:
|
||||||
|
return self.suffixes.random()
|
||||||
|
|
||||||
|
def text(self, count: int = 25) -> str:
|
||||||
|
phrases = []
|
||||||
|
phrase = []
|
||||||
|
for word in self.word(count):
|
||||||
|
phrase.append(str(word))
|
||||||
|
if len(phrase) >= random.randint(1, 12):
|
||||||
|
phrases.append(" ".join(phrase))
|
||||||
|
phrase = []
|
||||||
|
if phrase:
|
||||||
|
phrases.append(" ".join(phrase))
|
||||||
|
|
||||||
|
paragraph = phrases[0].capitalize()
|
||||||
|
for phrase in phrases[1:]:
|
||||||
|
if random.choice([0, 0, 1]):
|
||||||
|
paragraph = paragraph + random.choice("?!.") + " " + phrase.capitalize()
|
||||||
|
else:
|
||||||
|
paragraph = paragraph + ", " + phrase
|
||||||
|
paragraph = paragraph + random.choice("?!.")
|
||||||
|
return paragraph
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return self.__class__(
|
||||||
|
name=self.name,
|
||||||
|
vowels=self.vowels,
|
||||||
|
consonants=self.consonants,
|
||||||
|
prefixes=self.prefixes,
|
||||||
|
suffixes=self.suffixes,
|
||||||
|
rules=self.rules,
|
||||||
|
syllables=self.syllables,
|
||||||
|
minimum_grapheme_count=self.minimum_grapheme_count,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.word()[0]
|
||||||
|
|
||||||
|
|
||||||
|
NameSet = SyllableSet
|
||||||
|
|
||||||
|
|
||||||
|
class Name(defaultdict):
|
||||||
|
def __str__(self):
|
||||||
|
return self["fullname"][0]
|
||||||
|
|
||||||
|
|
||||||
|
class NameTemplate(Syllable):
|
||||||
|
def validate(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NameGenerator:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
language: Language,
|
||||||
|
templates: NameSet,
|
||||||
|
syllables: Union[SyllableSet, None] = None,
|
||||||
|
names: Union[WeightedSet, None] = None,
|
||||||
|
surnames: Union[WeightedSet, None] = None,
|
||||||
|
nicknames: Union[WeightedSet, None] = None,
|
||||||
|
adjectives: Union[WeightedSet, None] = None,
|
||||||
|
titles: Union[WeightedSet, None] = None,
|
||||||
|
counts: Union[WeightedSet, None] = None,
|
||||||
|
affixes: Union[WeightedSet, None] = None,
|
||||||
|
suffixes: Union[WeightedSet, None] = None,
|
||||||
|
):
|
||||||
|
self.language = language.copy()
|
||||||
|
if syllables:
|
||||||
|
self.language.syllables = syllables
|
||||||
|
self.templates = templates
|
||||||
|
self._names = names
|
||||||
|
self._surnames = surnames
|
||||||
|
self._nicknames = nicknames
|
||||||
|
self._adjectives = adjectives
|
||||||
|
self._titles = titles
|
||||||
|
self._counts = counts
|
||||||
|
self._suffixes = suffixes
|
||||||
|
self._affixes = affixes
|
||||||
|
|
||||||
|
self.handlers = dict([(n, v) for (n, v) in inspect.getmembers(self, inspect.ismethod) if n.startswith("get_")])
|
||||||
|
|
||||||
|
def name(self, count: int = 1) -> list:
|
||||||
|
"""
|
||||||
|
Generate Name instances.
|
||||||
|
"""
|
||||||
|
names = []
|
||||||
|
for _ in range(count):
|
||||||
|
name = Name(list)
|
||||||
|
fullname = []
|
||||||
|
for part in self.templates.random():
|
||||||
|
thisname = self.add_part(part).strip()
|
||||||
|
if not thisname:
|
||||||
|
continue
|
||||||
|
name[part].append(thisname)
|
||||||
|
fullname.append(thisname)
|
||||||
|
name["fullname"] = " ".join(fullname)
|
||||||
|
names.append(name)
|
||||||
|
return names
|
||||||
|
|
||||||
|
def add_part(self, template: str) -> str:
|
||||||
|
template = template.lower()
|
||||||
|
try:
|
||||||
|
return self.handlers[f"get_{template}"]()
|
||||||
|
except KeyError:
|
||||||
|
raise NotImplementedError(
|
||||||
|
f"No handler found for name template '{template}' on class {self.__class__.__name__}. "
|
||||||
|
f"Do you need to define get_{template}()?\nSupported Handlers: "
|
||||||
|
+ ",".join(n for n in dir(self) if n.startswith("get_"))
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
name = (self._names.random() if self._names else self.language.word())[0]
|
||||||
|
return name.title()
|
||||||
|
|
||||||
|
def get_surname(self) -> str:
|
||||||
|
name = (self._surnames.random() if self._surnames else self.language.word())[0]
|
||||||
|
if self._suffixes:
|
||||||
|
name = name + self._suffixes.random()
|
||||||
|
if len(name) == 1:
|
||||||
|
name = f"{name}."
|
||||||
|
return name.title()
|
||||||
|
|
||||||
|
def get_adjective(self) -> str:
|
||||||
|
return (self._adjectives.random() if self._adjectives else "").title()
|
||||||
|
|
||||||
|
def get_affix(self) -> str:
|
||||||
|
return self._affixes.random() if self._affixes else ""
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
return (self._titles.random() if self._titles else "").title()
|
||||||
|
|
||||||
|
def get_the(self) -> str:
|
||||||
|
return "the"
|
||||||
|
|
||||||
|
def get_count(self) -> str:
|
||||||
|
return self._counts.random() if self._counts else ""
|
||||||
|
|
||||||
|
def get_nickname(self) -> str:
|
||||||
|
name = (self._nicknames.random() if self._nicknames else "").title()
|
||||||
|
if name:
|
||||||
|
return '"' + name + '"'
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def get_initial(self) -> str:
|
||||||
|
return
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name()[0]["fullname"]
|
||||||
|
|
||||||
|
|
||||||
|
def equal_weights(terms: list, weight: float = 1.0, blank: bool = True) -> WeightedSet:
|
||||||
|
ws = WeightedSet(*[(term, weight) for term in terms])
|
||||||
|
if blank:
|
||||||
|
ws = WeightedSet(("", 1.0)) + ws
|
||||||
|
return ws
|
47
pyproject.toml
Normal file
47
pyproject.toml
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
[tool.poetry]
|
||||||
|
name = "dnd-name-generator"
|
||||||
|
version = "1.0"
|
||||||
|
description = "Fantasy language generators for D&D"
|
||||||
|
authors = ["evilchili <evilchili@gmail.com>"]
|
||||||
|
license = "The Unlicense"
|
||||||
|
packages = [
|
||||||
|
{ include = 'language' },
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.10"
|
||||||
|
typer = "latest"
|
||||||
|
rich = "latest"
|
||||||
|
dice = "latest"
|
||||||
|
|
||||||
|
[tool.poetry.dev-dependencies]
|
||||||
|
pytest = "latest"
|
||||||
|
black = "^23.3.0"
|
||||||
|
isort = "^5.12.0"
|
||||||
|
pyproject-autoflake = "^1.0.2"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 120
|
||||||
|
target-version = ['py310']
|
||||||
|
|
||||||
|
[tool.isort]
|
||||||
|
multi_line_output = 3
|
||||||
|
line_length = 120
|
||||||
|
include_trailing_comma = true
|
||||||
|
|
||||||
|
[tool.autoflake]
|
||||||
|
check = false # return error code if changes are needed
|
||||||
|
in-place = true # make changes to files instead of printing diffs
|
||||||
|
recursive = true # drill down directories recursively
|
||||||
|
remove-all-unused-imports = true # remove all unused imports (not just those from the standard library)
|
||||||
|
ignore-init-module-imports = true # exclude __init__.py when removing unused imports
|
||||||
|
remove-duplicate-keys = true # remove all duplicate keys in objects
|
||||||
|
remove-unused-variables = true # remove unused variables
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
fanlang = "language.cli:app"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user