PuzzleRecipe as DefaultScript; not as DefaultObject. Misc tests

This commit is contained in:
Henddher Pedroza 2018-09-09 22:43:26 -05:00
parent ae3f171225
commit e767d77db8
2 changed files with 100 additions and 47 deletions

View file

@ -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

View file

@ -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')