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:
```python
from evennia.contrib import rplanguages
from evennia.contrib import rplanguage
# need to be done once, here we create the "default" lang
rplanguages.add_language()
rplanguage.add_language()
say = "This is me talking."
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."
print rplanguages.obfuscate_language(say, level=0.5)
print rplanguage.obfuscate_language(say, level=0.5)
<<< "This is me byngyry."
print rplanguages.obfuscate_language(say, level=1.0)
print rplanguage.obfuscate_language(say, level=1.0)
<<< "Daly ly sy byngyry."
result = rplanguages.obfuscate_whisper(whisper, level=0.0)
result = rplanguage.obfuscate_whisper(whisper, level=0.0)
<<< "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"
result = rplanguages.obfuscate_whisper(whisper, level=0.5)
result = rplanguage.obfuscate_whisper(whisper, level=0.5)
<<< "---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",
"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,
noun_postfix=noun_postfix, vowels=vowels,
manual_translations=manual_translations
@ -96,6 +96,7 @@ import re
from random import choice, randint
from collections import defaultdict
from evennia import DefaultScript
from evennia.utils import logger
#------------------------------------------------------------
@ -105,21 +106,26 @@ from evennia import DefaultScript
#------------------------------------------------------------
# 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"
# 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)
_GRAMMAR = "v cv vc cvv vcc vcv cvcc vccv cvccv cvcvcc cvccvcv vccvccvc cvcvccvv cvcvcvcvv"
_RE_FLAGS = re.MULTILINE + re.IGNORECASE + re.UNICODE
_RE_GRAMMAR = re.compile(r"vv|cc|v|c", _RE_FLAGS)
_RE_WORD = re.compile(r'\w+', _RE_FLAGS)
_RE_EXTRA_CHARS = re.compile(r'\s+(?=\W)|[,.?;](?=[,.?;]|\s+[,.?;])', _RE_FLAGS)
class LanguageExistsError(Exception):
message = "Language is already created. Re-adding it will re-build" \
" its dictionary map. Use 'force=True' keyword if you are sure."
class LanguageError(RuntimeError):
pass
class LanguageExistsError(LanguageError):
pass
class LanguageHandler(DefaultScript):
@ -156,8 +162,11 @@ class LanguageHandler(DefaultScript):
self.db.language_storage = {}
def add(self, key="default", phonemes=_PHONEMES,
grammar=_GRAMMAR, word_length_variance=0, noun_prefix="",
noun_postfix="", vowels=_VOWELS, manual_translations=None,
grammar=_GRAMMAR, word_length_variance=0,
noun_translate=False,
noun_prefix="",
noun_postfix="",
vowels=_VOWELS, manual_translations=None,
auto_translations=None, force=False):
"""
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
should be short and unique.
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
allowed to build up words. For example cvv would be a consonant
followed by two vowels (would allow for a word like 'die').
allowed to build up words. Grammars are broken into the base phonemes
(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.
0 means a minimal variance, higher variance may mean words
have wildly varying length; this strongly affects how the
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
in this language (if any).
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:
raise LanguageExistsError
# allowed grammar are grouped by length
gramdict = defaultdict(list)
for gram in grammar.split():
gramdict[len(gram)].append(gram)
grammar = dict(gramdict)
raise LanguageExistsError(
"Language is already created. Re-adding it will re-build"
" its dictionary map. Use 'force=True' keyword if you are sure.")
# create grammar_component->phoneme mapping
# {"vv": ["ea", "oh", ...], ...}
grammar2phonemes = defaultdict(list)
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])
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
translation = {}
@ -261,6 +284,7 @@ class LanguageHandler(DefaultScript):
"grammar": grammar,
"grammar2phonemes": dict(grammar2phonemes),
"word_length_variance": word_length_variance,
"noun_translate": noun_translate,
"noun_prefix": noun_prefix,
"noun_postfix": noun_postfix}
self.db.language_storage[key] = storage
@ -282,34 +306,63 @@ class LanguageHandler(DefaultScript):
"""
word = match.group()
lword = len(word)
if len(word) <= self.level:
# below level. Don't translate
new_word = word
else:
# translate the word
# try to translate the word from dictionary
new_word = self.language["translation"].get(word.lower(), "")
if not new_word:
if word.istitle():
# capitalized word we don't have a translation for -
# treat as a name (don't translate)
new_word = "%s%s%s" % (self.language["noun_prefix"], word, self.language["noun_postfix"])
else:
# make up translation on the fly. Length can
# vary from un-translated word.
wlen = max(0, lword + sum(randint(-1, 1) for i
in range(self.language["word_length_variance"])))
grammar = self.language["grammar"]
if wlen not in grammar:
# no dictionary translation. Generate one
# find out what preceeded this word
wpos = match.start()
preceeding = match.string[:wpos].strip()
start_sentence = preceeding.endswith(".") or not preceeding
# make up translation on the fly. Length can
# vary from un-translated word.
wlen = max(0, lword + sum(randint(-1, 1) for i
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!
return ""
wlen = 0
new_word = ''
else:
# use random word length
wlen = choice(grammar.keys())
if wlen:
structure = choice(grammar[wlen])
grammar2phonemes = self.language["grammar2phonemes"]
for match in _RE_GRAMMAR.finditer(structure):
# there are only four combinations: vv,cc,c,v
new_word += choice(grammar2phonemes[match.group()])
if word.istitle():
# capitalize words the same way
new_word = new_word.capitalize()
try:
new_word += choice(grammar2phonemes[match.group()])
except KeyError:
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():
# keep LOUD words loud also when translated
new_word = new_word.upper()
@ -341,7 +394,9 @@ class LanguageHandler(DefaultScript):
# configuring the translation
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

View file

@ -18,7 +18,7 @@ from evennia.contrib import rplanguage
mtrans = {"testing": "1", "is": "2", "a": "3", "human": "4"}
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 " \
"intervention, They are easily repeatable, and often faster."
@ -33,6 +33,11 @@ class TestLanguage(EvenniaTest):
manual_translations=mtrans,
auto_translations=atrans,
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):
super(TestLanguage, self).tearDown()
@ -44,22 +49,36 @@ class TestLanguage(EvenniaTest):
self.assertEqual(result0, text)
result1 = 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(result3, text)
result1, result2 = result1.split(), result2.split()
self.assertEqual(result1[:4], result2[:4])
self.assertEqual(result1[1], "1")
self.assertEqual(result1[2], "2")
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):
self.assertEqual(rplanguage.available_languages(), ["testlang"])
self.assertEqual(rplanguage.available_languages(), ["testlang", "binary"])
def test_obfuscate_whisper(self):
self.assertEqual(rplanguage.obfuscate_whisper(text, level=0.0), text)
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(
'--------- --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), "...")
# Testing of emoting / sdesc / recog system

View file

@ -1685,6 +1685,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
msg_type = 'whisper'
msg_self = '{self} whisper to {all_receivers}, "{speech}"' if msg_self is True else msg_self
msg_receivers = '{object} whispers: "{speech}"'
msg_receivers = msg_receivers or '{object} whispers: "{speech}"'
msg_location = None
else:
msg_self = '{self} say, "{speech}"' if msg_self is True else msg_self
msg_location = msg_location or '{object} says, "{speech}"'
@ -1727,7 +1729,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
location_mapping = {"self": "You",
"object": self,
"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,
"speech": message}
location_mapping.update(custom_mapping)