PuzzleRecipe as DefaultScript; not as DefaultObject. Misc tests
This commit is contained in:
parent
ae3f171225
commit
e767d77db8
2 changed files with 100 additions and 47 deletions
|
|
@ -44,7 +44,7 @@ Details:
|
||||||
Puzzles are created from existing objects. The given
|
Puzzles are created from existing objects. The given
|
||||||
objects are introspected to create prototypes for the
|
objects are introspected to create prototypes for the
|
||||||
puzzle parts and results. These prototypes become the
|
puzzle parts and results. These prototypes become the
|
||||||
puzzle recipe. (See PuzzleRecipeObject and @puzzle
|
puzzle recipe. (See PuzzleRecipe and @puzzle
|
||||||
command). Once the recipe is created, all parts and result
|
command). Once the recipe is created, all parts and result
|
||||||
can be disposed (i.e. destroyed).
|
can be disposed (i.e. destroyed).
|
||||||
|
|
||||||
|
|
@ -69,9 +69,10 @@ Alternatively:
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
from random import choice
|
from random import choice
|
||||||
from evennia import create_object
|
from evennia import create_object, create_script
|
||||||
from evennia import CmdSet
|
from evennia import CmdSet
|
||||||
from evennia import DefaultObject
|
from evennia import DefaultObject
|
||||||
|
from evennia import DefaultScript
|
||||||
from evennia import DefaultCharacter
|
from evennia import DefaultCharacter
|
||||||
from evennia import DefaultRoom
|
from evennia import DefaultRoom
|
||||||
from evennia.commands.default.muxcommand import MuxCommand
|
from evennia.commands.default.muxcommand import MuxCommand
|
||||||
|
|
@ -127,7 +128,7 @@ class PuzzlePartObject(DefaultObject):
|
||||||
self.db.puzzle_name = puzzle_name
|
self.db.puzzle_name = puzzle_name
|
||||||
|
|
||||||
|
|
||||||
class PuzzleRecipeObject(DefaultObject):
|
class PuzzleRecipe(DefaultScript):
|
||||||
"""
|
"""
|
||||||
Definition of a Puzzle Recipe
|
Definition of a Puzzle Recipe
|
||||||
"""
|
"""
|
||||||
|
|
@ -217,7 +218,7 @@ class CmdCreatePuzzleRecipe(MuxCommand):
|
||||||
proto_parts = [proto_def(obj) for obj in parts]
|
proto_parts = [proto_def(obj) for obj in parts]
|
||||||
proto_results = [proto_def(obj) for obj in results]
|
proto_results = [proto_def(obj) for obj in results]
|
||||||
|
|
||||||
puzzle = create_object(PuzzleRecipeObject, key=puzzle_name)
|
puzzle = create_script(PuzzleRecipe, key=puzzle_name)
|
||||||
puzzle.save_recipe(puzzle_name, proto_parts, proto_results)
|
puzzle.save_recipe(puzzle_name, proto_parts, proto_results)
|
||||||
|
|
||||||
caller.msg(
|
caller.msg(
|
||||||
|
|
@ -253,10 +254,12 @@ class CmdArmPuzzle(MuxCommand):
|
||||||
caller.msg("A puzzle recipe's #dbref must be specified")
|
caller.msg("A puzzle recipe's #dbref must be specified")
|
||||||
return
|
return
|
||||||
|
|
||||||
puzzle = caller.search(self.args, global_search=True)
|
puzzle = search.search_script(self.args)
|
||||||
if not puzzle or not inherits_from(puzzle, PuzzleRecipeObject):
|
if not puzzle or not inherits_from(puzzle[0], PuzzleRecipe):
|
||||||
|
caller.msg('Invalid puzzle %r' % (self.args))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
puzzle = puzzle[0]
|
||||||
caller.msg(
|
caller.msg(
|
||||||
"Puzzle Recipe %s(%s) '%s' found.\nSpawning %d parts ..." % (
|
"Puzzle Recipe %s(%s) '%s' found.\nSpawning %d parts ..." % (
|
||||||
puzzle.name, puzzle.dbref, puzzle.db.puzzle_name, len(puzzle.db.parts)))
|
puzzle.name, puzzle.dbref, puzzle.db.puzzle_name, len(puzzle.db.parts)))
|
||||||
|
|
@ -336,17 +339,16 @@ class CmdUsePuzzleParts(MuxCommand):
|
||||||
(part.dbref, proto_def(part, with_tags=False))
|
(part.dbref, proto_def(part, with_tags=False))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Find all puzzles by puzzle name
|
# Find all puzzles by puzzle name
|
||||||
# FIXME: we rely on obj.db.puzzle_name which is visible and may be cnaged afterwards. Can we lock it and hide it?
|
# FIXME: we rely on obj.db.puzzle_name which is visible and may be cnaged afterwards. Can we lock it and hide it?
|
||||||
puzzles = []
|
puzzles = []
|
||||||
for puzzle_name, parts in puzzle_ingredients.items():
|
for puzzle_name, parts in puzzle_ingredients.items():
|
||||||
_puzzles = caller.search(
|
_puzzles = search.search_script_attribute(
|
||||||
puzzle_name,
|
key='puzzle_name',
|
||||||
typeclass=[PuzzleRecipeObject],
|
value=puzzle_name
|
||||||
attribute_name='puzzle_name',
|
)
|
||||||
quiet=True,
|
_puzzles = list(filter(lambda p: isinstance(p, PuzzleRecipe), _puzzles))
|
||||||
exact=True,
|
|
||||||
global_search=True)
|
|
||||||
if not _puzzles:
|
if not _puzzles:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
|
|
@ -356,12 +358,11 @@ class CmdUsePuzzleParts(MuxCommand):
|
||||||
|
|
||||||
# Create lookup dict
|
# Create lookup dict
|
||||||
puzzles_dict = dict((puzzle.dbref, puzzle) for puzzle in puzzles)
|
puzzles_dict = dict((puzzle.dbref, puzzle) for puzzle in puzzles)
|
||||||
|
|
||||||
# Check if parts can be combined to solve a puzzle
|
# Check if parts can be combined to solve a puzzle
|
||||||
matched_puzzles = dict()
|
matched_puzzles = dict()
|
||||||
for puzzle in puzzles:
|
for puzzle in puzzles:
|
||||||
puzzleparts = puzzle.db.parts[:]
|
puzzleparts = list(sorted(puzzle.db.parts[:], key=lambda p: p['key']))
|
||||||
parts = puzzle_ingredients[puzzle.db.puzzle_name][:]
|
parts = list(sorted(puzzle_ingredients[puzzle.db.puzzle_name][:], key=lambda p: p[1]['key']))
|
||||||
pz = 0
|
pz = 0
|
||||||
p = 0
|
p = 0
|
||||||
matched_dbrefparts = set()
|
matched_dbrefparts = set()
|
||||||
|
|
@ -454,7 +455,7 @@ class CmdListPuzzleRecipes(MuxCommand):
|
||||||
def func(self):
|
def func(self):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
recipes = search.search_tag(
|
recipes = search.search_script_tag(
|
||||||
_PUZZLES_TAG_RECIPE, category=_PUZZLES_TAG_CATEGORY)
|
_PUZZLES_TAG_RECIPE, category=_PUZZLES_TAG_CATEGORY)
|
||||||
|
|
||||||
div = "-" * 60
|
div = "-" * 60
|
||||||
|
|
|
||||||
|
|
@ -1198,7 +1198,7 @@ class TestPuzzles(CommandTest):
|
||||||
def _keys(items):
|
def _keys(items):
|
||||||
return [item['key'] for item in items]
|
return [item['key'] for item in items]
|
||||||
|
|
||||||
recipes = search.search_tag('', category=puzzles._PUZZLES_TAG_CATEGORY)
|
recipes = search.search_script_tag('', category=puzzles._PUZZLES_TAG_CATEGORY)
|
||||||
self.assertEqual(1, len(recipes))
|
self.assertEqual(1, len(recipes))
|
||||||
self.assertEqual(name, recipes[0].db.puzzle_name)
|
self.assertEqual(name, recipes[0].db.puzzle_name)
|
||||||
self.assertEqual(parts, _keys(recipes[0].db.parts))
|
self.assertEqual(parts, _keys(recipes[0].db.parts))
|
||||||
|
|
@ -1207,8 +1207,10 @@ class TestPuzzles(CommandTest):
|
||||||
puzzles._PUZZLES_TAG_RECIPE,
|
puzzles._PUZZLES_TAG_RECIPE,
|
||||||
recipes[0].tags.get(category=puzzles._PUZZLES_TAG_CATEGORY)
|
recipes[0].tags.get(category=puzzles._PUZZLES_TAG_CATEGORY)
|
||||||
)
|
)
|
||||||
|
recipe_dbref = recipes[0].dbref
|
||||||
if and_destroy_it:
|
if and_destroy_it:
|
||||||
recipes[0].delete()
|
recipes[0].delete()
|
||||||
|
return recipe_dbref if not and_destroy_it else None
|
||||||
|
|
||||||
def _assert_no_recipes(self):
|
def _assert_no_recipes(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
@ -1216,14 +1218,41 @@ class TestPuzzles(CommandTest):
|
||||||
len(search.search_tag('', category=puzzles._PUZZLES_TAG_CATEGORY))
|
len(search.search_tag('', category=puzzles._PUZZLES_TAG_CATEGORY))
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_cmd_use(self):
|
# good recipes
|
||||||
def _use(cmdstr, msg):
|
def _good_recipe(self, name, parts, results, and_destroy_it=True):
|
||||||
self.call(puzzles.CmdUsePuzzleParts(), cmdstr, msg, caller=self.char1)
|
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
|
||||||
|
)
|
||||||
|
matches = self._assert_msg_matched(msg, regexs, re_flags=re.MULTILINE | re.DOTALL)
|
||||||
|
recipe_dbref = self._assert_recipe(name, parts, results, and_destroy_it)
|
||||||
|
return recipe_dbref
|
||||||
|
|
||||||
_use('', 'Use what?')
|
def _arm(self, recipe_dbref):
|
||||||
_use('stone', 'You have no idea how this can be used')
|
msg = self.call(
|
||||||
_use('stone flint', 'There is no stone flint around.')
|
puzzles.CmdArmPuzzle(),
|
||||||
_use('stone, flint', 'You have no idea how these can be used')
|
recipe_dbref,
|
||||||
|
caller=self.char1
|
||||||
|
)
|
||||||
|
print(msg)
|
||||||
|
# TODO: add regex for parts and whatnot
|
||||||
|
# similar to _good_recipe
|
||||||
|
'''
|
||||||
|
Puzzle Recipe makefire(#2) 'makefire' found.
|
||||||
|
Spawning 2 parts ...
|
||||||
|
Part stone(#11) spawned and placed at Room(#1)
|
||||||
|
Part flint(#12) spawned and placed at Room(#1)
|
||||||
|
Puzzle armed successfully.'''
|
||||||
|
self.assertIsNotNone(re.match(r"Puzzle Recipe .* found.*Puzzle armed successfully.", msg, re.MULTILINE | re.DOTALL))
|
||||||
|
|
||||||
def test_cmd_puzzle(self):
|
def test_cmd_puzzle(self):
|
||||||
self._assert_no_recipes()
|
self._assert_no_recipes()
|
||||||
|
|
@ -1249,32 +1278,14 @@ class TestPuzzles(CommandTest):
|
||||||
|
|
||||||
self._assert_no_recipes()
|
self._assert_no_recipes()
|
||||||
|
|
||||||
# good recipes
|
self._good_recipe('makefire', ['stone', 'flint'], ['fire', 'stone', 'flint'])
|
||||||
def _good_recipe(name, parts, results):
|
self._good_recipe('hot stones', ['stone', 'fire'], ['stone', 'fire'])
|
||||||
regexs = []
|
self._good_recipe('furnace', ['stone', 'stone', 'fire'], ['stone', 'stone', 'fire', 'fire', 'fire', 'fire'])
|
||||||
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
|
|
||||||
)
|
|
||||||
matches = self._assert_msg_matched(msg, regexs, re_flags=re.MULTILINE | re.DOTALL)
|
|
||||||
self._assert_recipe(name, parts, results)
|
|
||||||
|
|
||||||
_good_recipe('makefire', ['stone', 'flint'], ['fire', 'stone', 'flint'])
|
|
||||||
_good_recipe('hot stones', ['stone', 'fire'], ['stone', 'fire'])
|
|
||||||
_good_recipe('hot stones', ['stone', 'fire'], ['stone', 'fire'])
|
|
||||||
|
|
||||||
# bad recipes
|
# bad recipes
|
||||||
def _bad_recipe(name, parts, results, fail_regex):
|
def _bad_recipe(name, parts, results, fail_regex):
|
||||||
with self.assertRaisesRegexp(AssertionError, fail_regex):
|
with self.assertRaisesRegexp(AssertionError, fail_regex):
|
||||||
_good_recipe(name, parts, results)
|
self._good_recipe(name, parts, results)
|
||||||
self.assert_no_recipes()
|
self.assert_no_recipes()
|
||||||
|
|
||||||
_bad_recipe('name', ['nothing'], ['neither'], r"Could not find 'nothing'.")
|
_bad_recipe('name', ['nothing'], ['neither'], r"Could not find 'nothing'.")
|
||||||
|
|
@ -1282,3 +1293,44 @@ class TestPuzzles(CommandTest):
|
||||||
# _bad_recipe('', ['stone', 'fire'], ['stone', 'fire'], '') # FIXME: no name becomes '' #N(#N)
|
# _bad_recipe('', ['stone', 'fire'], ['stone', 'fire'], '') # FIXME: no name becomes '' #N(#N)
|
||||||
|
|
||||||
self._assert_no_recipes()
|
self._assert_no_recipes()
|
||||||
|
|
||||||
|
def test_cmd_armpuzzle(self):
|
||||||
|
recipe_dbref = self._good_recipe('makefile', ['stone', 'flint'], ['fire', 'stone', 'flint'], and_destroy_it=False)
|
||||||
|
self._arm(recipe_dbref)
|
||||||
|
|
||||||
|
def test_cmd_use(self):
|
||||||
|
def _use(cmdstr, msg):
|
||||||
|
msg = self.call(puzzles.CmdUsePuzzleParts(), cmdstr, msg, caller=self.char1)
|
||||||
|
return msg
|
||||||
|
|
||||||
|
_use('', 'Use what?')
|
||||||
|
_use('something', 'There is no something around.')
|
||||||
|
_use('stone', 'You have no idea how this can be used')
|
||||||
|
_use('stone flint', 'There is no stone flint around.')
|
||||||
|
_use('stone, flint', 'You have no idea how these can be used')
|
||||||
|
|
||||||
|
recipe_dbref = self._good_recipe('makefire', ['stone', 'flint'], ['fire'] , and_destroy_it=False)
|
||||||
|
|
||||||
|
# although there is stone and flint
|
||||||
|
# those aren't valid puzzle parts because
|
||||||
|
# the puzzle hasn't been armed
|
||||||
|
_use('stone', 'You have no idea how this can be used')
|
||||||
|
_use('stone, flint', 'You have no idea how these can be used')
|
||||||
|
self._arm(recipe_dbref)
|
||||||
|
|
||||||
|
# there are duplicated objects now
|
||||||
|
msg = _use('stone', None)
|
||||||
|
self.assertIsNotNone(re.match(r'^Which stone. There are many.*', msg))
|
||||||
|
msg = _use('flint', None)
|
||||||
|
self.assertIsNotNone(re.match(r'^Which flint. There are many.*', msg))
|
||||||
|
# delete them
|
||||||
|
self.stone.delete()
|
||||||
|
self.flint.delete()
|
||||||
|
|
||||||
|
msg = _use('stone, flint', None)
|
||||||
|
self.assertIsNotNone(re.match(r"^You are a Genius.*", msg))
|
||||||
|
|
||||||
|
# trying again will fail as it was resolved already
|
||||||
|
# and the parts were destroyed
|
||||||
|
_use('stone, flint', 'There is no stone around')
|
||||||
|
_use('flint, stone', 'There is no flint around')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue