Resolve merge conflicts

This commit is contained in:
Griatch 2017-12-08 19:09:01 +01:00
commit 87918e4ce0
3 changed files with 127 additions and 51 deletions

View file

@ -21,30 +21,30 @@ in the game in various ways:
Usage: Usage:
```python ```python
from evennia.contrib import rplanguages from evennia.contrib import rplanguage
# need to be done once, here we create the "default" lang # need to be done once, here we create the "default" lang
rplanguages.add_language() rplanguage.add_language()
say = "This is me talking." say = "This is me talking."
whisper = "This is me whispering. whisper = "This is me whispering.
print rplanguages.obfuscate_language(say, level=0.0) print rplanguage.obfuscate_language(say, level=0.0)
<<< "This is me talking." <<< "This is me talking."
print rplanguages.obfuscate_language(say, level=0.5) print rplanguage.obfuscate_language(say, level=0.5)
<<< "This is me byngyry." <<< "This is me byngyry."
print rplanguages.obfuscate_language(say, level=1.0) print rplanguage.obfuscate_language(say, level=1.0)
<<< "Daly ly sy byngyry." <<< "Daly ly sy byngyry."
result = rplanguages.obfuscate_whisper(whisper, level=0.0) result = rplanguage.obfuscate_whisper(whisper, level=0.0)
<<< "This is me whispering" <<< "This is me whispering"
result = rplanguages.obfuscate_whisper(whisper, level=0.2) result = rplanguage.obfuscate_whisper(whisper, level=0.2)
<<< "This is m- whisp-ring" <<< "This is m- whisp-ring"
result = rplanguages.obfuscate_whisper(whisper, level=0.5) result = rplanguage.obfuscate_whisper(whisper, level=0.5)
<<< "---s -s -- ---s------" <<< "---s -s -- ---s------"
result = rplanguages.obfuscate_whisper(whisper, level=0.7) result = rplanguage.obfuscate_whisper(whisper, level=0.7)
<<< "---- -- -- ----------" <<< "---- -- -- ----------"
result = rplanguages.obfuscate_whisper(whisper, level=1.0) result = rplanguage.obfuscate_whisper(whisper, level=1.0)
<<< "..." <<< "..."
``` ```
@ -71,7 +71,7 @@ Usage:
manual_translations = {"the":"y'e", "we":"uyi", "she":"semi", "he":"emi", manual_translations = {"the":"y'e", "we":"uyi", "she":"semi", "he":"emi",
"you": "do", 'me':'mi','i':'me', 'be':"hy'e", 'and':'y'} "you": "do", 'me':'mi','i':'me', 'be':"hy'e", 'and':'y'}
rplanguages.add_language(key="elvish", phonemes=phonemes, grammar=grammar, rplanguage.add_language(key="elvish", phonemes=phonemes, grammar=grammar,
word_length_variance=word_length_variance, word_length_variance=word_length_variance,
noun_postfix=noun_postfix, vowels=vowels, noun_postfix=noun_postfix, vowels=vowels,
manual_translations=manual_translations manual_translations=manual_translations
@ -96,6 +96,7 @@ import re
from random import choice, randint from random import choice, randint
from collections import defaultdict from collections import defaultdict
from evennia import DefaultScript from evennia import DefaultScript
from evennia.utils import logger
#------------------------------------------------------------ #------------------------------------------------------------
@ -105,21 +106,26 @@ from evennia import DefaultScript
#------------------------------------------------------------ #------------------------------------------------------------
# default language grammar # default language grammar
_PHONEMES = "ea oh ae aa eh ah ao aw ai er ey ow ia ih iy oy ua uh uw a e i u y p b t d f v t dh s z sh zh ch jh k ng g m n l r w" _PHONEMES = "ea oh ae aa eh ah ao aw ai er ey ow ia ih iy oy ua uh uw a e i u y p b t d f v t dh " \
"s z sh zh ch jh k ng g m n l r w"
_VOWELS = "eaoiuy" _VOWELS = "eaoiuy"
# these must be able to be constructed from phonemes (so for example, # these must be able to be constructed from phonemes (so for example,
# if you have v here, there must exixt at least one single-character # if you have v here, there must exist at least one single-character
# vowel phoneme defined above) # vowel phoneme defined above)
_GRAMMAR = "v cv vc cvv vcc vcv cvcc vccv cvccv cvcvcc cvccvcv vccvccvc cvcvccvv cvcvcvcvv" _GRAMMAR = "v cv vc cvv vcc vcv cvcc vccv cvccv cvcvcc cvccvcv vccvccvc cvcvccvv cvcvcvcvv"
_RE_FLAGS = re.MULTILINE + re.IGNORECASE + re.UNICODE _RE_FLAGS = re.MULTILINE + re.IGNORECASE + re.UNICODE
_RE_GRAMMAR = re.compile(r"vv|cc|v|c", _RE_FLAGS) _RE_GRAMMAR = re.compile(r"vv|cc|v|c", _RE_FLAGS)
_RE_WORD = re.compile(r'\w+', _RE_FLAGS) _RE_WORD = re.compile(r'\w+', _RE_FLAGS)
_RE_EXTRA_CHARS = re.compile(r'\s+(?=\W)|[,.?;](?=[,.?;]|\s+[,.?;])', _RE_FLAGS)
class LanguageExistsError(Exception): class LanguageError(RuntimeError):
message = "Language is already created. Re-adding it will re-build" \ pass
" its dictionary map. Use 'force=True' keyword if you are sure."
class LanguageExistsError(LanguageError):
pass
class LanguageHandler(DefaultScript): class LanguageHandler(DefaultScript):
@ -156,8 +162,11 @@ class LanguageHandler(DefaultScript):
self.db.language_storage = {} self.db.language_storage = {}
def add(self, key="default", phonemes=_PHONEMES, def add(self, key="default", phonemes=_PHONEMES,
grammar=_GRAMMAR, word_length_variance=0, noun_prefix="", grammar=_GRAMMAR, word_length_variance=0,
noun_postfix="", vowels=_VOWELS, manual_translations=None, noun_translate=False,
noun_prefix="",
noun_postfix="",
vowels=_VOWELS, manual_translations=None,
auto_translations=None, force=False): auto_translations=None, force=False):
""" """
Add a new language. Note that you generally only need to do Add a new language. Note that you generally only need to do
@ -170,14 +179,21 @@ class LanguageHandler(DefaultScript):
will be used as an identifier for the language so it will be used as an identifier for the language so it
should be short and unique. should be short and unique.
phonemes (str, optional): Space-separated string of all allowed phonemes (str, optional): Space-separated string of all allowed
phonemes in this language. phonemes in this language. If either of the base phonemes
(c, v, cc, vv) are present in the grammar, the phoneme list must
at least include one example of each.
grammar (str): All allowed consonant (c) and vowel (v) combinations grammar (str): All allowed consonant (c) and vowel (v) combinations
allowed to build up words. For example cvv would be a consonant allowed to build up words. Grammars are broken into the base phonemes
followed by two vowels (would allow for a word like 'die'). (c, v, cc, vv) prioritizing the longer bases. So cvv would be a
the c + vv (would allow for a word like 'die' whereas
cvcvccc would be c+v+c+v+cc+c (a word like 'galosch').
word_length_variance (real): The variation of length of words. word_length_variance (real): The variation of length of words.
0 means a minimal variance, higher variance may mean words 0 means a minimal variance, higher variance may mean words
have wildly varying length; this strongly affects how the have wildly varying length; this strongly affects how the
language "looks". language "looks".
noun_translate (bool, optional): If a proper noun, identified as a
capitalized word, should be translated or not. By default they
will not, allowing for e.g. the names of characters to be understandable.
noun_prefix (str, optional): A prefix to go before every noun noun_prefix (str, optional): A prefix to go before every noun
in this language (if any). in this language (if any).
noun_postfix (str, optuonal): A postfix to go after every noun noun_postfix (str, optuonal): A postfix to go after every noun
@ -213,21 +229,28 @@ class LanguageHandler(DefaultScript):
""" """
if key in self.db.language_storage and not force: if key in self.db.language_storage and not force:
raise LanguageExistsError raise LanguageExistsError(
"Language is already created. Re-adding it will re-build"
# allowed grammar are grouped by length " its dictionary map. Use 'force=True' keyword if you are sure.")
gramdict = defaultdict(list)
for gram in grammar.split():
gramdict[len(gram)].append(gram)
grammar = dict(gramdict)
# create grammar_component->phoneme mapping # create grammar_component->phoneme mapping
# {"vv": ["ea", "oh", ...], ...} # {"vv": ["ea", "oh", ...], ...}
grammar2phonemes = defaultdict(list) grammar2phonemes = defaultdict(list)
for phoneme in phonemes.split(): for phoneme in phonemes.split():
if re.search("\W", phoneme):
raise LanguageError("The phoneme '%s' contains an invalid character" % phoneme)
gram = "".join(["v" if char in vowels else "c" for char in phoneme]) gram = "".join(["v" if char in vowels else "c" for char in phoneme])
grammar2phonemes[gram].append(phoneme) grammar2phonemes[gram].append(phoneme)
# allowed grammar are grouped by length
gramdict = defaultdict(list)
for gram in grammar.split():
if re.search("\W|(!=[cv])", gram):
raise LanguageError("The grammar '%s' is invalid (only 'c' and 'v' are allowed)" % gram)
gramdict[len(gram)].append(gram)
grammar = dict(gramdict)
# create automatic translation # create automatic translation
translation = {} translation = {}
@ -261,6 +284,7 @@ class LanguageHandler(DefaultScript):
"grammar": grammar, "grammar": grammar,
"grammar2phonemes": dict(grammar2phonemes), "grammar2phonemes": dict(grammar2phonemes),
"word_length_variance": word_length_variance, "word_length_variance": word_length_variance,
"noun_translate": noun_translate,
"noun_prefix": noun_prefix, "noun_prefix": noun_prefix,
"noun_postfix": noun_postfix} "noun_postfix": noun_postfix}
self.db.language_storage[key] = storage self.db.language_storage[key] = storage
@ -282,34 +306,63 @@ class LanguageHandler(DefaultScript):
""" """
word = match.group() word = match.group()
lword = len(word) lword = len(word)
if len(word) <= self.level: if len(word) <= self.level:
# below level. Don't translate # below level. Don't translate
new_word = word new_word = word
else: else:
# translate the word # try to translate the word from dictionary
new_word = self.language["translation"].get(word.lower(), "") new_word = self.language["translation"].get(word.lower(), "")
if not new_word: if not new_word:
if word.istitle(): # no dictionary translation. Generate one
# capitalized word we don't have a translation for -
# treat as a name (don't translate) # find out what preceeded this word
new_word = "%s%s%s" % (self.language["noun_prefix"], word, self.language["noun_postfix"]) wpos = match.start()
else: preceeding = match.string[:wpos].strip()
# make up translation on the fly. Length can start_sentence = preceeding.endswith(".") or not preceeding
# vary from un-translated word.
wlen = max(0, lword + sum(randint(-1, 1) for i # make up translation on the fly. Length can
in range(self.language["word_length_variance"]))) # vary from un-translated word.
grammar = self.language["grammar"] wlen = max(0, lword + sum(randint(-1, 1) for i
if wlen not in grammar: in range(self.language["word_length_variance"])))
grammar = self.language["grammar"]
if wlen not in grammar:
if randint(0, 1) == 0:
# this word has no direct translation! # this word has no direct translation!
return "" wlen = 0
new_word = ''
else:
# use random word length
wlen = choice(grammar.keys())
if wlen:
structure = choice(grammar[wlen]) structure = choice(grammar[wlen])
grammar2phonemes = self.language["grammar2phonemes"] grammar2phonemes = self.language["grammar2phonemes"]
for match in _RE_GRAMMAR.finditer(structure): for match in _RE_GRAMMAR.finditer(structure):
# there are only four combinations: vv,cc,c,v # there are only four combinations: vv,cc,c,v
new_word += choice(grammar2phonemes[match.group()]) try:
if word.istitle(): new_word += choice(grammar2phonemes[match.group()])
# capitalize words the same way except KeyError:
new_word = new_word.capitalize() logger.log_trace("You need to supply at least one example of each of "
"the four base phonemes (c, v, cc, vv)")
# abort translation here
new_word = ''
break
if word.istitle():
title_word = ''
if not start_sentence and not self.language.get("noun_translate", False):
# don't translate what we identify as proper nouns (names)
title_word = word
elif new_word:
title_word = new_word
if title_word:
# Regardless of if we translate or not, we will add the custom prefix/postfixes
new_word = "%s%s%s" % (self.language["noun_prefix"],
title_word.capitalize(),
self.language["noun_postfix"])
if len(word) > 1 and word.isupper(): if len(word) > 1 and word.isupper():
# keep LOUD words loud also when translated # keep LOUD words loud also when translated
new_word = new_word.upper() new_word = new_word.upper()
@ -341,7 +394,9 @@ class LanguageHandler(DefaultScript):
# configuring the translation # configuring the translation
self.level = int(10 * (1.0 - max(0, min(level, 1.0)))) self.level = int(10 * (1.0 - max(0, min(level, 1.0))))
return _RE_WORD.sub(self._translate_sub, text) translation = _RE_WORD.sub(self._translate_sub, text)
# the substitution may create too long empty spaces, remove those
return _RE_EXTRA_CHARS.sub("", translation)
# Language access functions # Language access functions

View file

@ -18,7 +18,7 @@ from evennia.contrib import rplanguage
mtrans = {"testing": "1", "is": "2", "a": "3", "human": "4"} mtrans = {"testing": "1", "is": "2", "a": "3", "human": "4"}
atrans = ["An", "automated", "advantageous", "repeatable", "faster"] atrans = ["An", "automated", "advantageous", "repeatable", "faster"]
text = "Automated testing is advantageous for a number of reasons:" \ text = "Automated testing is advantageous for a number of reasons: " \
"tests may be executed Continuously without the need for human " \ "tests may be executed Continuously without the need for human " \
"intervention, They are easily repeatable, and often faster." "intervention, They are easily repeatable, and often faster."
@ -33,6 +33,11 @@ class TestLanguage(EvenniaTest):
manual_translations=mtrans, manual_translations=mtrans,
auto_translations=atrans, auto_translations=atrans,
force=True) force=True)
rplanguage.add_language(key="binary",
phonemes="oo ii a ck w b d t",
grammar="cvvv cvv cvvcv cvvcvv cvvvc cvvvcvv cvvc",
noun_prefix='beep-',
word_length_variance=4)
def tearDown(self): def tearDown(self):
super(TestLanguage, self).tearDown() super(TestLanguage, self).tearDown()
@ -44,22 +49,36 @@ class TestLanguage(EvenniaTest):
self.assertEqual(result0, text) self.assertEqual(result0, text)
result1 = rplanguage.obfuscate_language(text, level=1.0, language="testlang") result1 = rplanguage.obfuscate_language(text, level=1.0, language="testlang")
result2 = rplanguage.obfuscate_language(text, level=1.0, language="testlang") result2 = rplanguage.obfuscate_language(text, level=1.0, language="testlang")
result3 = rplanguage.obfuscate_language(text, level=1.0, language='binary')
self.assertNotEqual(result1, text) self.assertNotEqual(result1, text)
self.assertNotEqual(result3, text)
result1, result2 = result1.split(), result2.split() result1, result2 = result1.split(), result2.split()
self.assertEqual(result1[:4], result2[:4]) self.assertEqual(result1[:4], result2[:4])
self.assertEqual(result1[1], "1") self.assertEqual(result1[1], "1")
self.assertEqual(result1[2], "2") self.assertEqual(result1[2], "2")
self.assertEqual(result2[-1], result2[-1]) self.assertEqual(result2[-1], result2[-1])
def test_faulty_language(self):
self.assertRaises(
rplanguage.LanguageError,
rplanguage.add_language,
key='binary2',
phonemes="w b d t oe ee, oo e o a wh dw bw", # erroneous comma
grammar="cvvv cvv cvvcv cvvcvvo cvvvc cvvvcvv cvvc c v cc vv ccvvc ccvvccvv ",
vowels="oea",
word_length_variance=4)
def test_available_languages(self): def test_available_languages(self):
self.assertEqual(rplanguage.available_languages(), ["testlang"]) self.assertEqual(rplanguage.available_languages(), ["testlang", "binary"])
def test_obfuscate_whisper(self): def test_obfuscate_whisper(self):
self.assertEqual(rplanguage.obfuscate_whisper(text, level=0.0), text) self.assertEqual(rplanguage.obfuscate_whisper(text, level=0.0), text)
assert (rplanguage.obfuscate_whisper(text, level=0.1).startswith( assert (rplanguage.obfuscate_whisper(text, level=0.1).startswith(
'-utom-t-d t-sting is -dv-nt-g-ous for - numb-r of r--sons:t-sts m-y b- -x-cut-d Continuously')) '-utom-t-d t-sting is -dv-nt-g-ous for - numb-r of r--sons: t-sts m-y b- -x-cut-d Continuously'))
assert(rplanguage.obfuscate_whisper(text, level=0.5).startswith( assert(rplanguage.obfuscate_whisper(text, level=0.5).startswith(
'--------- --s---- -s -----------s f-- - ------ -f ---s--s:--s-s ')) '--------- --s---- -s -----------s f-- - ------ -f ---s--s: --s-s '))
self.assertEqual(rplanguage.obfuscate_whisper(text, level=1.0), "...") self.assertEqual(rplanguage.obfuscate_whisper(text, level=1.0), "...")
# Testing of emoting / sdesc / recog system # Testing of emoting / sdesc / recog system

View file

@ -1685,6 +1685,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
msg_type = 'whisper' msg_type = 'whisper'
msg_self = '{self} whisper to {all_receivers}, "{speech}"' if msg_self is True else msg_self msg_self = '{self} whisper to {all_receivers}, "{speech}"' if msg_self is True else msg_self
msg_receivers = '{object} whispers: "{speech}"' msg_receivers = '{object} whispers: "{speech}"'
msg_receivers = msg_receivers or '{object} whispers: "{speech}"'
msg_location = None
else: else:
msg_self = '{self} say, "{speech}"' if msg_self is True else msg_self msg_self = '{self} say, "{speech}"' if msg_self is True else msg_self
msg_location = msg_location or '{object} says, "{speech}"' msg_location = msg_location or '{object} says, "{speech}"'
@ -1727,7 +1729,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
location_mapping = {"self": "You", location_mapping = {"self": "You",
"object": self, "object": self,
"location": location, "location": location,
"all_receivers": ", ".join(recv for recv in receivers) if receivers else None, "all_receivers": ", ".join(str(recv) for recv in receivers) if receivers else None,
"receiver": None, "receiver": None,
"speech": message} "speech": message}
location_mapping.update(custom_mapping) location_mapping.update(custom_mapping)