Fix merge conflicts

This commit is contained in:
Griatch 2019-01-17 23:08:19 +01:00
commit 690ed6ecd1
5 changed files with 1763 additions and 9 deletions

View file

@ -1731,6 +1731,961 @@ class TestRandomStringGenerator(EvenniaTest):
SIMPLE_GENERATOR.get()
# Test of the Puzzles module
import itertools
from evennia.contrib import puzzles
from evennia.utils import search
from evennia.utils.utils import inherits_from
class TestPuzzles(CommandTest):
def setUp(self):
super(TestPuzzles, self).setUp()
self.steel = create_object(
self.object_typeclass,
key='steel', location=self.char1.location)
self.flint = create_object(
self.object_typeclass,
key='flint', location=self.char1.location)
self.fire = create_object(
self.object_typeclass,
key='fire', location=self.char1.location)
self.steel.tags.add('tag-steel')
self.steel.tags.add('tag-steel', category='tagcat')
self.flint.tags.add('tag-flint')
self.flint.tags.add('tag-flint', category='tagcat')
self.fire.tags.add('tag-fire')
self.fire.tags.add('tag-fire', category='tagcat')
def _assert_msg_matched(self, msg, regexs, re_flags=0):
matches = []
for regex in regexs:
m = re.search(regex, msg, re_flags)
self.assertIsNotNone(m, "%r didn't match %r" % (regex, msg))
matches.append(m)
return matches
def _assert_recipe(self, name, parts, results, and_destroy_it=True, expected_count=1):
def _keys(items):
return [item['key'] for item in items]
recipes = search.search_script_tag('', category=puzzles._PUZZLES_TAG_CATEGORY)
self.assertEqual(expected_count, len(recipes))
self.assertEqual(name, recipes[expected_count-1].db.puzzle_name)
self.assertEqual(parts, _keys(recipes[expected_count-1].db.parts))
self.assertEqual(results, _keys(recipes[expected_count-1].db.results))
self.assertEqual(
puzzles._PUZZLES_TAG_RECIPE,
recipes[expected_count-1].tags.get(category=puzzles._PUZZLES_TAG_CATEGORY)
)
recipe_dbref = recipes[expected_count-1].dbref
if and_destroy_it:
recipes[expected_count-1].delete()
return recipe_dbref if not and_destroy_it else None
def _assert_no_recipes(self):
self.assertEqual(
0,
len(search.search_script_tag('', category=puzzles._PUZZLES_TAG_CATEGORY))
)
# good recipes
def _good_recipe(self, name, parts, results, and_destroy_it=True, expected_count=1):
regexs = []
for p in parts:
regexs.append(r'^Part %s\(#\d+\)$' % (p))
for r in results:
regexs.append(r'^Result %s\(#\d+\)$' % (r))
regexs.append(r"^Puzzle '%s' %s\(#\d+\) has been created successfully.$" % (name, name))
lhs = [name] + parts
cmdstr = ','.join(lhs) + '=' + ','.join(results)
msg = self.call(
puzzles.CmdCreatePuzzleRecipe(),
cmdstr,
caller=self.char1
)
recipe_dbref = self._assert_recipe(name, parts, results, and_destroy_it, expected_count)
matches = self._assert_msg_matched(msg, regexs, re_flags=re.MULTILINE | re.DOTALL)
return recipe_dbref
def _check_room_contents(self, expected, check_test_tags=False):
by_obj_key = lambda o: o.key
room1_contents = sorted(self.room1.contents, key=by_obj_key)
for key, grp in itertools.groupby(room1_contents, by_obj_key):
if key in expected:
grp = list(grp)
self.assertEqual(expected[key], len(grp),
"Expected %d but got %d for %s" % (expected[key], len(grp), key))
if check_test_tags:
for gi in grp:
tags = gi.tags.all(return_key_and_category=True)
self.assertIn(('tag-' + gi.key, None), tags)
self.assertIn(('tag-' + gi.key, 'tagcat'), tags)
def _arm(self, recipe_dbref, name, parts):
regexs = [
r"^Puzzle Recipe %s\(#\d+\) '%s' found.$" % (name, name),
r"^Spawning %d parts ...$" % (len(parts)),
]
for p in parts:
regexs.append(r'^Part %s\(#\d+\) spawned .*$' % (p))
regexs.append(r"^Puzzle armed successfully.$")
msg = self.call(
puzzles.CmdArmPuzzle(),
recipe_dbref,
caller=self.char1
)
matches = self._assert_msg_matched(msg, regexs, re_flags=re.MULTILINE | re.DOTALL)
def test_cmdset_puzzle(self):
self.char1.cmdset.add('evennia.contrib.puzzles.PuzzleSystemCmdSet')
# FIXME: testing nothing, this is just to bump up coverage
def test_cmd_puzzle(self):
self._assert_no_recipes()
# bad syntax
def _bad_syntax(cmdstr):
self.call(
puzzles.CmdCreatePuzzleRecipe(),
cmdstr,
'Usage: @puzzle name,<part1[,...]> = <result1[,...]>',
caller=self.char1
)
_bad_syntax('')
_bad_syntax('=')
_bad_syntax('nothing =')
_bad_syntax('= nothing')
_bad_syntax('nothing')
_bad_syntax(',nothing')
_bad_syntax('name, nothing')
_bad_syntax('name, nothing =')
self._assert_no_recipes()
self._good_recipe('makefire', ['steel', 'flint'], ['fire', 'steel', 'flint'])
self._good_recipe('hot steels', ['steel', 'fire'], ['steel', 'fire'])
self._good_recipe('furnace', ['steel', 'steel', 'fire'], ['steel', 'steel', 'fire', 'fire', 'fire', 'fire'])
# bad recipes
def _bad_recipe(name, parts, results, fail_regex):
cmdstr = ','.join([name] + parts) \
+ '=' + ','.join(results)
msg = self.call(
puzzles.CmdCreatePuzzleRecipe(),
cmdstr,
caller=self.char1
)
self._assert_no_recipes()
self.assertIsNotNone(re.match(fail_regex, msg), msg)
_bad_recipe('name', ['nothing'], ['neither'], r"Could not find 'nothing'.")
_bad_recipe('name', ['steel'], ['nothing'], r"Could not find 'nothing'.")
_bad_recipe('', ['steel', 'fire'], ['steel', 'fire'], r"^Invalid puzzle name ''.")
self.steel.location = self.char1
_bad_recipe('name', ['steel'], ['fire'], r"^Invalid location for steel$")
_bad_recipe('name', ['flint'], ['steel'], r"^Invalid location for steel$")
_bad_recipe('name', ['self'], ['fire'], r"^Invalid typeclass for Char$")
_bad_recipe('name', ['here'], ['fire'], r"^Invalid typeclass for Room$")
self._assert_no_recipes()
def test_cmd_armpuzzle(self):
# bad arms
self.call(
puzzles.CmdArmPuzzle(),
'1',
"A puzzle recipe's #dbref must be specified",
caller=self.char1
)
self.call(
puzzles.CmdArmPuzzle(),
'#1',
"Invalid puzzle '#1'",
caller=self.char1
)
recipe_dbref = self._good_recipe('makefire', ['steel', 'flint'], ['fire', 'steel', 'flint'], and_destroy_it=False)
# delete proto parts and proto result
self.steel.delete()
self.flint.delete()
self.fire.delete()
# good arm
self._arm(recipe_dbref, 'makefire', ['steel', 'flint'])
self._check_room_contents({'steel': 1, 'flint': 1}, check_test_tags=True)
def _use(self, cmdstr, expmsg):
msg = self.call(
puzzles.CmdUsePuzzleParts(),
cmdstr,
expmsg,
caller=self.char1
)
return msg
def test_cmd_use(self):
self._use('', 'Use what?')
self._use('something', 'There is no something around.')
self._use('steel', 'You have no idea how this can be used')
self._use('steel flint', 'There is no steel flint around.')
self._use('steel, flint', 'You have no idea how these can be used')
recipe_dbref = self._good_recipe(unicode('makefire'), ['steel', 'flint'], ['fire'] , and_destroy_it=False)
recipe2_dbref = self._good_recipe('makefire2', ['steel', 'flint'], ['fire'] , and_destroy_it=False,
expected_count=2)
# although there is steel and flint
# those aren't valid puzzle parts because
# the puzzle hasn't been armed
self._use('steel', 'You have no idea how this can be used')
self._use('steel, flint', 'You have no idea how these can be used')
self._arm(recipe_dbref, 'makefire', ['steel', 'flint'])
self._check_room_contents({'steel': 2, 'flint': 2}, check_test_tags=True)
# there are duplicated objects now
self._use('steel', 'Which steel. There are many')
self._use('flint', 'Which flint. There are many')
# delete proto parts and proto results
self.steel.delete()
self.flint.delete()
self.fire.delete()
# solve puzzle
self._use('steel, flint', 'You are a Genius')
self.assertEqual(1,
len(list(filter(
lambda o: o.key == 'fire' \
and ('makefire', puzzles._PUZZLES_TAG_CATEGORY) \
in o.tags.all(return_key_and_category=True) \
and (puzzles._PUZZLES_TAG_MEMBER, puzzles._PUZZLES_TAG_CATEGORY) \
in o.tags.all(return_key_and_category=True),
self.room1.contents))))
self._check_room_contents({'steel': 0, 'flint': 0, 'fire': 1}, check_test_tags=True)
# trying again will fail as it was resolved already
# and the parts were destroyed
self._use('steel, flint', 'There is no steel around')
self._use('flint, steel', 'There is no flint around')
# arm same puzzle twice so there are duplicated parts
self._arm(recipe_dbref, 'makefire', ['steel', 'flint'])
self._arm(recipe_dbref, 'makefire', ['steel', 'flint'])
self._check_room_contents({'steel': 2, 'flint': 2, 'fire': 1}, check_test_tags=True)
# try solving with multiple parts but incomplete set
self._use('1-steel, 2-steel', 'You try to utilize these but nothing happens ... something amiss?')
# arm the other puzzle. Their parts are identical
self._arm(recipe2_dbref, 'makefire2', ['steel', 'flint'])
self._check_room_contents({'steel': 3, 'flint': 3, 'fire': 1}, check_test_tags=True)
# solve with multiple parts for
# multiple puzzles. Both can be solved but
# only one is.
self._use(
'1-steel, 2-flint, 3-steel, 3-flint',
'Your gears start turning and 2 different ideas come to your mind ... ')
self._check_room_contents({'steel': 2, 'flint': 2, 'fire': 2}, check_test_tags=True)
self.room1.msg_contents = Mock()
# solve all
self._use('1-steel, 1-flint', 'You are a Genius')
self.room1.msg_contents.assert_called_once_with('|cChar|n performs some kind of tribal dance and |yfire|n seems to appear from thin air', exclude=(self.char1,))
self._use('steel, flint', 'You are a Genius')
self._check_room_contents({'steel': 0, 'flint': 0, 'fire': 4}, check_test_tags=True)
def test_puzzleedit(self):
recipe_dbref = self._good_recipe('makefire', ['steel', 'flint'], ['fire'] , and_destroy_it=False)
def _puzzleedit(swt, dbref, args, expmsg):
if (swt is None) and (dbref is None) and (args is None):
cmdstr = ''
else:
cmdstr = '%s %s%s' % (swt, dbref, args)
self.call(
puzzles.CmdEditPuzzle(),
cmdstr,
expmsg,
caller=self.char1
)
# delete proto parts and proto results
self.steel.delete()
self.flint.delete()
self.fire.delete()
# bad syntax
_puzzleedit(None, None, None, "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit")
_puzzleedit('', '1', '', "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit")
_puzzleedit('', '', '', "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit")
_puzzleedit('', recipe_dbref, 'dummy', "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit")
_puzzleedit('', self.script.dbref, '', 'Script(#1) is not a puzzle')
# edit use_success_message and use_success_location_message
_puzzleedit('', recipe_dbref, '/use_success_message = Yes!', 'makefire(%s) use_success_message = Yes!' % recipe_dbref)
_puzzleedit('', recipe_dbref, '/use_success_location_message = {result_names} Yeah baby! {caller}', 'makefire(%s) use_success_location_message = {result_names} Yeah baby! {caller}' % recipe_dbref)
self._arm(recipe_dbref, 'makefire', ['steel', 'flint'])
self.room1.msg_contents = Mock()
self._use('steel, flint', 'Yes!')
self.room1.msg_contents.assert_called_once_with('fire Yeah baby! Char', exclude=(self.char1,))
self.room1.msg_contents.reset_mock()
# edit mask: exclude location and desc during matching
_puzzleedit('', recipe_dbref, '/mask = location,desc',
"makefire(%s) mask = ('location', 'desc')" % recipe_dbref)
self._arm(recipe_dbref, 'makefire', ['steel', 'flint'])
# change location and desc
self.char1.search('steel').db.desc = 'A solid bar of steel'
self.char1.search('steel').location = self.char1
self.char1.search('flint').db.desc = 'A flint steel'
self.char1.search('flint').location = self.char1
self._use('steel, flint', 'Yes!')
self.room1.msg_contents.assert_called_once_with('fire Yeah baby! Char', exclude=(self.char1,))
# delete
_puzzleedit('/delete', recipe_dbref, '', 'makefire(%s) was deleted' % recipe_dbref)
self._assert_no_recipes()
def test_puzzleedit_add_remove_parts_results(self):
recipe_dbref = self._good_recipe('makefire', ['steel', 'flint'], ['fire'] , and_destroy_it=False)
def _puzzleedit(swt, dbref, rhslist, expmsg):
cmdstr = '%s %s = %s' % (swt, dbref, ', '.join(rhslist))
self.call(
puzzles.CmdEditPuzzle(),
cmdstr,
expmsg,
caller=self.char1
)
red_steel = create_object(
self.object_typeclass,
key='red steel', location=self.char1.location)
smoke = create_object(
self.object_typeclass,
key='smoke', location=self.char1.location)
_puzzleedit('/addresult', recipe_dbref, ['smoke'], 'smoke were added to results')
_puzzleedit('/addpart', recipe_dbref, ['red steel', 'steel'], 'red steel, steel were added to parts')
# create a box so we can put all objects in
# so that they can't be found during puzzle resolution
self.box = create_object(
self.object_typeclass,
key='box', location=self.char1.location)
def _box_all():
for o in self.room1.contents:
if o not in [self.char1, self.char2, self.exit,
self.obj1, self.obj2, self.box]:
o.location = self.box
_box_all()
self._arm(recipe_dbref, 'makefire', ['steel', 'flint', 'red steel', 'steel'])
self._check_room_contents({
'steel': 2,
'red steel': 1,
'flint': 1,
})
self._use('1-steel, flint', 'You try to utilize these but nothing happens ... something amiss?')
self._use('1-steel, flint, red steel, 3-steel', 'You are a Genius')
self._check_room_contents({
'smoke': 1,
'fire': 1
})
_box_all()
self.fire.location = self.room1
self.steel.location = self.room1
_puzzleedit('/delresult', recipe_dbref, ['fire'], 'fire were removed from results')
_puzzleedit('/delpart', recipe_dbref, ['steel', 'steel'], 'steel, steel were removed from parts')
_box_all()
self._arm(recipe_dbref, 'makefire', ['flint', 'red steel'])
self._check_room_contents({
'red steel': 1,
'flint': 1,
})
self._use('red steel, flint', 'You are a Genius')
self._check_room_contents({
'smoke': 1,
'fire': 0
})
def test_lspuzzlerecipes_lsarmedpuzzles(self):
msg = self.call(
puzzles.CmdListPuzzleRecipes(),
'',
caller=self.char1
)
self._assert_msg_matched(
msg,
[
r"^-+$",
r"^Found 0 puzzle\(s\)\.$",
r"-+$",
],
re.MULTILINE | re.DOTALL
)
recipe_dbref = self._good_recipe(
'makefire', ['steel', 'flint'], ['fire'],
and_destroy_it=False)
msg = self.call(
puzzles.CmdListPuzzleRecipes(),
'',
caller=self.char1
)
self._assert_msg_matched(
msg,
[
r"^-+$",
r"^Puzzle 'makefire'.*$",
r"^Success Caller message:$",
r"^Success Location message:$",
r"^Mask:$",
r"^Parts$",
r"^.*key: steel$",
r"^.*key: flint$",
r"^Results$",
r"^.*key: fire$",
r"^.*key: steel$",
r"^.*key: flint$",
r"^-+$",
r"^Found 1 puzzle\(s\)\.$",
r"^-+$",
],
re.MULTILINE | re.DOTALL
)
msg = self.call(
puzzles.CmdListArmedPuzzles(),
'',
caller=self.char1
)
self._assert_msg_matched(
msg,
[
r"^-+$",
r"^-+$",
r"^Found 0 armed puzzle\(s\)\.$",
r"^-+$"
],
re.MULTILINE | re.DOTALL
)
self._arm(recipe_dbref, 'makefire', ['steel', 'flint'])
msg = self.call(
puzzles.CmdListArmedPuzzles(),
'',
caller=self.char1
)
self._assert_msg_matched(
msg,
[
r"^-+$",
r"^Puzzle name: makefire$",
r"^.*steel.* at \s+ Room.*$",
r"^.*flint.* at \s+ Room.*$",
r"^Found 1 armed puzzle\(s\)\.$",
r"^-+$",
],
re.MULTILINE | re.DOTALL
)
def test_e2e(self):
def _destroy_objs_in_room(keys):
for obj in self.room1.contents:
if obj.key in keys:
obj.delete()
# parts don't survive resolution
# but produce a large result set
tree = create_object(
self.object_typeclass,
key='tree', location=self.char1.location)
axe = create_object(
self.object_typeclass,
key='axe', location=self.char1.location)
sweat = create_object(
self.object_typeclass,
key='sweat', location=self.char1.location)
dull_axe = create_object(
self.object_typeclass,
key='dull axe', location=self.char1.location)
timber = create_object(
self.object_typeclass,
key='timber', location=self.char1.location)
log = create_object(
self.object_typeclass,
key='log', location=self.char1.location)
parts = ['tree', 'axe']
results = (['sweat'] * 10) + ['dull axe'] + (['timber'] * 20) + (['log'] * 50)
recipe_dbref = self._good_recipe(
'lumberjack',
parts, results,
and_destroy_it=False
)
_destroy_objs_in_room(set(parts + results))
sps = sorted(parts)
expected = {key: len(list(grp)) for key, grp in itertools.groupby(sps)}
expected.update({r: 0 for r in set(results)})
self._arm(recipe_dbref, 'lumberjack', parts)
self._check_room_contents(expected)
self._use(','.join(parts), 'You are a Genius')
srs = sorted(set(results))
expected = {(key, len(list(grp))) for key, grp in itertools.groupby(srs)}
expected.update({p: 0 for p in set(parts)})
self._check_room_contents(expected)
# parts also appear in results
# causing a new puzzle to be armed 'automatically'
# i.e. the puzzle is self-sustaining
hole = create_object(
self.object_typeclass,
key='hole', location=self.char1.location)
shovel = create_object(
self.object_typeclass,
key='shovel', location=self.char1.location)
dirt = create_object(
self.object_typeclass,
key='dirt', location=self.char1.location)
parts = ['shovel', 'hole']
results = ['dirt', 'hole', 'shovel']
recipe_dbref = self._good_recipe(
'digger',
parts, results,
and_destroy_it=False,
expected_count=2
)
_destroy_objs_in_room(set(parts + results))
nresolutions = 0
sps = sorted(set(parts))
expected = {key: len(list(grp)) for key, grp in itertools.groupby(sps)}
expected.update({'dirt': nresolutions})
self._arm(recipe_dbref, 'digger', parts)
self._check_room_contents(expected)
for i in range(10):
self._use(','.join(parts), 'You are a Genius')
nresolutions += 1
expected.update({'dirt': nresolutions})
self._check_room_contents(expected)
# Uppercase puzzle name
balloon = create_object(
self.object_typeclass,
key='Balloon', location=self.char1.location)
parts = ['Balloon']
results = ['Balloon']
recipe_dbref = self._good_recipe(
'boom!!!',
parts, results,
and_destroy_it=False,
expected_count=3
)
_destroy_objs_in_room(set(parts + results))
sps = sorted(parts)
expected = {key: len(list(grp)) for key, grp in itertools.groupby(sps)}
self._arm(recipe_dbref, 'boom!!!', parts)
self._check_room_contents(expected)
self._use(','.join(parts), 'You are a Genius')
srs = sorted(set(results))
expected = {(key, len(list(grp))) for key, grp in itertools.groupby(srs)}
self._check_room_contents(expected)
def test_e2e_accumulative(self):
flashlight = create_object(
self.object_typeclass,
key='flashlight', location=self.char1.location)
flashlight_w_1 = create_object(
self.object_typeclass,
key='flashlight-w-1', location=self.char1.location)
flashlight_w_2 = create_object(
self.object_typeclass,
key='flashlight-w-2', location=self.char1.location)
flashlight_w_3 = create_object(
self.object_typeclass,
key='flashlight-w-3',
location=self.char1.location)
battery = create_object(
self.object_typeclass,
key='battery', location=self.char1.location)
battery.tags.add('flashlight-1', category=puzzles._PUZZLES_TAG_CATEGORY)
battery.tags.add('flashlight-2', category=puzzles._PUZZLES_TAG_CATEGORY)
battery.tags.add('flashlight-3', category=puzzles._PUZZLES_TAG_CATEGORY)
# TODO: instead of tagging each flashlight,
# arm and resolve each puzzle in order so they all
# are tagged correctly
# it will be necessary to add/remove parts/results because
# each battery is supposed to be consumed during resolution
# as the new flashlight has one more battery than before
flashlight_w_1.tags.add('flashlight-2', category=puzzles._PUZZLES_TAG_CATEGORY)
flashlight_w_2.tags.add('flashlight-3', category=puzzles._PUZZLES_TAG_CATEGORY)
recipe_fl1_dbref = self._good_recipe('flashlight-1', ['flashlight', 'battery'], ['flashlight-w-1'] , and_destroy_it=False,
expected_count=1)
recipe_fl2_dbref = self._good_recipe('flashlight-2', ['flashlight-w-1', 'battery'], ['flashlight-w-2'] , and_destroy_it=False,
expected_count=2)
recipe_fl3_dbref = self._good_recipe('flashlight-3', ['flashlight-w-2', 'battery'], ['flashlight-w-3'] , and_destroy_it=False,
expected_count=3)
# delete protoparts
for obj in [battery, flashlight, flashlight_w_1,
flashlight_w_2, flashlight_w_3]:
obj.delete()
def _group_parts(parts, excluding=set()):
group = dict()
dbrefs = dict()
for o in self.room1.contents:
if o.key in parts and o.dbref not in excluding:
if o.key not in group:
group[o.key] = []
group[o.key].append(o.dbref)
dbrefs[o.dbref] = o
return group, dbrefs
# arm each puzzle and group its parts
self._arm(recipe_fl1_dbref, 'flashlight-1', ['battery', 'flashlight'])
fl1_parts, fl1_dbrefs = _group_parts(['battery', 'flashlight'])
self._arm(recipe_fl2_dbref, 'flashlight-2', ['battery', 'flashlight-w-1'])
fl2_parts, fl2_dbrefs = _group_parts(['battery', 'flashlight-w-1'], excluding=fl1_dbrefs.keys())
self._arm(recipe_fl3_dbref, 'flashlight-3', ['battery', 'flashlight-w-2'])
fl3_parts, fl3_dbrefs = _group_parts(['battery', 'flashlight-w-2'], excluding=set(fl1_dbrefs.keys() + fl2_dbrefs.keys()))
self._check_room_contents({
'battery': 3,
'flashlight': 1,
'flashlight-w-1': 1,
'flashlight-w-2': 1,
'flashlight-w-3': 0
})
# all batteries have identical protodefs
battery_1 = fl1_dbrefs[fl1_parts['battery'][0]]
battery_2 = fl2_dbrefs[fl2_parts['battery'][0]]
battery_3 = fl3_dbrefs[fl3_parts['battery'][0]]
protodef_battery_1 = puzzles.proto_def(battery_1, with_tags=False)
del(protodef_battery_1['prototype_key'])
protodef_battery_2 = puzzles.proto_def(battery_2, with_tags=False)
del(protodef_battery_2['prototype_key'])
protodef_battery_3 = puzzles.proto_def(battery_3, with_tags=False)
del(protodef_battery_3['prototype_key'])
assert protodef_battery_1 == protodef_battery_2 == protodef_battery_3
# each battery can be used in every other puzzle
b1_parts_dict, b1_puzzlenames, b1_protodefs = puzzles._lookups_parts_puzzlenames_protodefs([battery_1])
_puzzles = puzzles._puzzles_by_names(b1_puzzlenames.keys())
assert set(['flashlight-1', 'flashlight-2', 'flashlight-3']) \
== set([p.db.puzzle_name for p in _puzzles])
matched_puzzles = puzzles._matching_puzzles(_puzzles, b1_puzzlenames, b1_protodefs)
assert 0 == len(matched_puzzles)
b2_parts_dict, b2_puzzlenames, b2_protodefs = puzzles._lookups_parts_puzzlenames_protodefs([battery_2])
_puzzles = puzzles._puzzles_by_names(b2_puzzlenames.keys())
assert set(['flashlight-1', 'flashlight-2', 'flashlight-3']) \
== set([p.db.puzzle_name for p in _puzzles])
matched_puzzles = puzzles._matching_puzzles(_puzzles, b2_puzzlenames, b2_protodefs)
assert 0 == len(matched_puzzles)
b3_parts_dict, b3_puzzlenames, b3_protodefs = puzzles._lookups_parts_puzzlenames_protodefs([battery_3])
_puzzles = puzzles._puzzles_by_names(b3_puzzlenames.keys())
assert set(['flashlight-1', 'flashlight-2', 'flashlight-3']) \
== set([p.db.puzzle_name for p in _puzzles])
matched_puzzles = puzzles._matching_puzzles(_puzzles, b3_puzzlenames, b3_protodefs)
assert 0 == len(matched_puzzles)
assert battery_1 == b1_parts_dict.values()[0]
assert battery_2 == b2_parts_dict.values()[0]
assert battery_3 == b3_parts_dict.values()[0]
assert b1_puzzlenames.keys() == b2_puzzlenames.keys() == b3_puzzlenames.keys()
for puzzle_name in ['flashlight-1', 'flashlight-2', 'flashlight-3']:
assert puzzle_name in b1_puzzlenames
assert puzzle_name in b2_puzzlenames
assert puzzle_name in b3_puzzlenames
assert b1_protodefs.values()[0] == b2_protodefs.values()[0] == b3_protodefs.values()[0] \
== protodef_battery_1 == protodef_battery_2 == protodef_battery_3
# all flashlights have similar protodefs except their key
flashlight_1 = fl1_dbrefs[fl1_parts['flashlight'][0]]
flashlight_2 = fl2_dbrefs[fl2_parts['flashlight-w-1'][0]]
flashlight_3 = fl3_dbrefs[fl3_parts['flashlight-w-2'][0]]
protodef_flashlight_1 = puzzles.proto_def(flashlight_1, with_tags=False)
del(protodef_flashlight_1['prototype_key'])
assert protodef_flashlight_1['key'] == 'flashlight'
del(protodef_flashlight_1['key'])
protodef_flashlight_2 = puzzles.proto_def(flashlight_2, with_tags=False)
del(protodef_flashlight_2['prototype_key'])
assert protodef_flashlight_2['key'] == 'flashlight-w-1'
del(protodef_flashlight_2['key'])
protodef_flashlight_3 = puzzles.proto_def(flashlight_3, with_tags=False)
del(protodef_flashlight_3['prototype_key'])
assert protodef_flashlight_3['key'] == 'flashlight-w-2'
del(protodef_flashlight_3['key'])
assert protodef_flashlight_1 == protodef_flashlight_2 == protodef_flashlight_3
# each flashlight can only be used in its own puzzle
f1_parts_dict, f1_puzzlenames, f1_protodefs = puzzles._lookups_parts_puzzlenames_protodefs([flashlight_1])
_puzzles = puzzles._puzzles_by_names(f1_puzzlenames.keys())
assert set(['flashlight-1']) \
== set([p.db.puzzle_name for p in _puzzles])
matched_puzzles = puzzles._matching_puzzles(_puzzles, f1_puzzlenames, f1_protodefs)
assert 0 == len(matched_puzzles)
f2_parts_dict, f2_puzzlenames, f2_protodefs = puzzles._lookups_parts_puzzlenames_protodefs([flashlight_2])
_puzzles = puzzles._puzzles_by_names(f2_puzzlenames.keys())
assert set(['flashlight-2']) \
== set([p.db.puzzle_name for p in _puzzles])
matched_puzzles = puzzles._matching_puzzles(_puzzles, f2_puzzlenames, f2_protodefs)
assert 0 == len(matched_puzzles)
f3_parts_dict, f3_puzzlenames, f3_protodefs = puzzles._lookups_parts_puzzlenames_protodefs([flashlight_3])
_puzzles = puzzles._puzzles_by_names(f3_puzzlenames.keys())
assert set(['flashlight-3']) \
== set([p.db.puzzle_name for p in _puzzles])
matched_puzzles = puzzles._matching_puzzles(_puzzles, f3_puzzlenames, f3_protodefs)
assert 0 == len(matched_puzzles)
assert flashlight_1 == f1_parts_dict.values()[0]
assert flashlight_2 == f2_parts_dict.values()[0]
assert flashlight_3 == f3_parts_dict.values()[0]
for puzzle_name in set(f1_puzzlenames.keys() + f2_puzzlenames.keys() + f3_puzzlenames.keys()):
assert puzzle_name in ['flashlight-1', 'flashlight-2', 'flashlight-3', 'puzzle_member']
protodef_flashlight_1['key'] = 'flashlight'
assert f1_protodefs.values()[0] == protodef_flashlight_1
protodef_flashlight_2['key'] = 'flashlight-w-1'
assert f2_protodefs.values()[0] == protodef_flashlight_2
protodef_flashlight_3['key'] = 'flashlight-w-2'
assert f3_protodefs.values()[0] == protodef_flashlight_3
# each battery can be matched with every other flashlight
# to potentially resolve each puzzle
for batt in [battery_1, battery_2, battery_3]:
parts_dict, puzzlenames, protodefs = puzzles._lookups_parts_puzzlenames_protodefs([batt, flashlight_1])
assert set([batt.dbref, flashlight_1.dbref]) == set(puzzlenames['flashlight-1'])
assert set([batt.dbref]) == set(puzzlenames['flashlight-2'])
assert set([batt.dbref]) == set(puzzlenames['flashlight-3'])
_puzzles = puzzles._puzzles_by_names(puzzlenames.keys())
assert set(['flashlight-1', 'flashlight-2', 'flashlight-3']) == set([p.db.puzzle_name for p in _puzzles])
matched_puzzles = puzzles._matching_puzzles(_puzzles, puzzlenames, protodefs)
assert 1 == len(matched_puzzles)
parts_dict, puzzlenames, protodefs = puzzles._lookups_parts_puzzlenames_protodefs([batt, flashlight_2])
assert set([batt.dbref]) == set(puzzlenames['flashlight-1'])
assert set([batt.dbref, flashlight_2.dbref]) == set(puzzlenames['flashlight-2'])
assert set([batt.dbref]) == set(puzzlenames['flashlight-3'])
_puzzles = puzzles._puzzles_by_names(puzzlenames.keys())
assert set(['flashlight-1','flashlight-2', 'flashlight-3']) == set([p.db.puzzle_name for p in _puzzles])
matched_puzzles = puzzles._matching_puzzles(_puzzles, puzzlenames, protodefs)
assert 1 == len(matched_puzzles)
parts_dict, puzzlenames, protodefs = puzzles._lookups_parts_puzzlenames_protodefs([batt, flashlight_3])
assert set([batt.dbref]) == set(puzzlenames['flashlight-1'])
assert set([batt.dbref]) == set(puzzlenames['flashlight-2'])
assert set([batt.dbref, flashlight_3.dbref]) == set(puzzlenames['flashlight-3'])
_puzzles = puzzles._puzzles_by_names(puzzlenames.keys())
assert set(['flashlight-1','flashlight-2', 'flashlight-3']) == set([p.db.puzzle_name for p in _puzzles])
matched_puzzles = puzzles._matching_puzzles(_puzzles, puzzlenames, protodefs)
assert 1 == len(matched_puzzles)
# delete all parts
for part in fl1_dbrefs.values() + fl2_dbrefs.values() + fl3_dbrefs.values():
part.delete()
self._check_room_contents({
'battery': 0,
'flashlight': 0,
'flashlight-w-1': 0,
'flashlight-w-2': 0,
'flashlight-w-3': 0
})
# arm first puzzle 3 times and group its parts so we can solve
# all puzzles with the parts from the 1st armed
for i in range(3):
self._arm(recipe_fl1_dbref, 'flashlight-1', ['battery', 'flashlight'])
fl1_parts, fl1_dbrefs = _group_parts(['battery', 'flashlight'])
# delete the 2 extra flashlights so we can start solving
for flashlight_dbref in fl1_parts['flashlight'][1:]:
fl1_dbrefs[flashlight_dbref].delete()
self._check_room_contents({
'battery': 3,
'flashlight': 1,
'flashlight-w-1': 0,
'flashlight-w-2': 0,
'flashlight-w-3': 0
})
self._use('1-battery, flashlight', 'You are a Genius')
self._check_room_contents({
'battery': 2,
'flashlight': 0,
'flashlight-w-1': 1,
'flashlight-w-2': 0,
'flashlight-w-3': 0
})
self._use('1-battery, flashlight-w-1', 'You are a Genius')
self._check_room_contents({
'battery': 1,
'flashlight': 0,
'flashlight-w-1': 0,
'flashlight-w-2': 1,
'flashlight-w-3': 0
})
self._use('battery, flashlight-w-2', 'You are a Genius')
self._check_room_contents({
'battery': 0,
'flashlight': 0,
'flashlight-w-1': 0,
'flashlight-w-2': 0,
'flashlight-w-3': 1
})
def test_e2e_interchangeable_parts_and_results(self):
# Parts and Results can be used in multiple puzzles
egg = create_object(
self.object_typeclass,
key='egg', location=self.char1.location)
flour = create_object(
self.object_typeclass,
key='flour', location=self.char1.location)
boiling_water = create_object(
self.object_typeclass,
key='boiling water', location=self.char1.location)
boiled_egg = create_object(
self.object_typeclass,
key='boiled egg', location=self.char1.location)
dough = create_object(
self.object_typeclass,
key='dough', location=self.char1.location)
pasta = create_object(
self.object_typeclass,
key='pasta', location=self.char1.location)
# Three recipes:
# 1. breakfast: egg + boiling water = boiled egg & boiling water
# 2. dough: egg + flour = dough
# 3. entree: dough + boiling water = pasta & boiling water
# tag interchangeable parts according to their puzzles' name
egg.tags.add('breakfast', category=puzzles._PUZZLES_TAG_CATEGORY)
egg.tags.add('dough', category=puzzles._PUZZLES_TAG_CATEGORY)
dough.tags.add('entree', category=puzzles._PUZZLES_TAG_CATEGORY)
boiling_water.tags.add('breakfast', category=puzzles._PUZZLES_TAG_CATEGORY)
boiling_water.tags.add('entree', category=puzzles._PUZZLES_TAG_CATEGORY)
# create recipes
recipe1_dbref = self._good_recipe('breakfast', ['egg', 'boiling water'], ['boiled egg', 'boiling water'] , and_destroy_it=False)
recipe2_dbref = self._good_recipe('dough', ['egg', 'flour'], ['dough'] , and_destroy_it=False, expected_count=2)
recipe3_dbref = self._good_recipe('entree', ['dough', 'boiling water'], ['pasta', 'boiling water'] , and_destroy_it=False, expected_count=3)
# delete protoparts
for obj in [egg, flour, boiling_water,
boiled_egg, dough, pasta]:
obj.delete()
# arm each puzzle and group its parts
def _group_parts(parts, excluding=set()):
group = dict()
dbrefs = dict()
for o in self.room1.contents:
if o.key in parts and o.dbref not in excluding:
if o.key not in group:
group[o.key] = []
group[o.key].append(o.dbref)
dbrefs[o.dbref] = o
return group, dbrefs
self._arm(recipe1_dbref, 'breakfast', ['egg', 'boiling water'])
breakfast_parts, breakfast_dbrefs = _group_parts(['egg', 'boiling water'])
self._arm(recipe2_dbref, 'dough', ['egg', 'flour'])
dough_parts, dough_dbrefs = _group_parts(['egg', 'flour'], excluding=breakfast_dbrefs.keys())
self._arm(recipe3_dbref, 'entree', ['dough', 'boiling water'])
entree_parts, entree_dbrefs = _group_parts(['dough', 'boiling water'], excluding=set(breakfast_dbrefs.keys() + dough_dbrefs.keys()))
# create a box so we can put all objects in
# so that they can't be found during puzzle resolution
self.box = create_object(
self.object_typeclass,
key='box', location=self.char1.location)
def _box_all():
# print "boxing all\n", "-"*20
for o in self.room1.contents:
if o not in [self.char1, self.char2, self.exit,
self.obj1, self.obj2, self.box]:
o.location = self.box
# print o.key, o.dbref, "boxed"
else:
# print "skipped", o.key, o.dbref
pass
def _unbox(dbrefs):
# print "unboxing", dbrefs, "\n", "-"*20
for o in self.box.contents:
if o.dbref in dbrefs:
o.location = self.room1
# print "unboxed", o.key, o.dbref
# solve dough puzzle using breakfast's egg
# and dough's flour. A new dough will be created
_box_all()
_unbox(breakfast_parts.pop('egg') + dough_parts.pop('flour'))
self._use('egg, flour', 'You are a Genius')
# solve entree puzzle with newly created dough
# and breakfast's boiling water. A new
# boiling water and pasta will be created
_unbox(breakfast_parts.pop('boiling water'))
self._use('boiling water, dough', 'You are a Genius')
# solve breakfast puzzle with dough's egg
# and newly created boiling water. A new
# boiling water and boiled egg will be created
_unbox(dough_parts.pop('egg'))
self._use('boiling water, egg', 'You are a Genius')
# solve entree puzzle using entree's dough
# and newly created boiling water. A new
# boiling water and pasta will be created
_unbox(entree_parts.pop('dough'))
self._use('boiling water, dough', 'You are a Genius')
self._check_room_contents({
'boiling water': 1,
'pasta': 2,
'boiled egg': 1,
})
# Tests for the building_menu contrib
from evennia.contrib.building_menu import BuildingMenu, CmdNoInput, CmdNoMatch