# HG changeset patch # User Jeremy Thurgood # Date 1378044459 -7200 # Node ID c03982fe3c70367a25a664718a4350dcaa7f4abc # Parent 3e4d8091268ca862ee8bf660591ffbd1eea0cdbc Protagonist and environment. diff -r 3e4d8091268c -r c03982fe3c70 nagslang/environment.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nagslang/environment.py Sun Sep 01 16:07:39 2013 +0200 @@ -0,0 +1,85 @@ +class ProtagonistCondition(object): + """A condition on the protagonist that can be checked. + """ + + def check(self, protagonist): + """Check if this condition applies. + """ + raise NotImplementedError() + + +class AllConditions(ProtagonistCondition): + def __init__(self, *conditions): + self.conditions = conditions + + def check(self, protagonist): + for condition in self.conditions: + if not condition.check(protagonist): + return False + return True + + +class AnyCondition(ProtagonistCondition): + def __init__(self, *conditions): + self.conditions = conditions + + def check(self, protagonist): + for condition in self.conditions: + if condition.check(protagonist): + return True + return False + + +class WolfFormCondition(ProtagonistCondition): + def check(self, protagonist): + return protagonist.in_wolf_form() + + +class HumanFormCondition(ProtagonistCondition): + def check(self, protagonist): + return protagonist.in_human_form() + + +class ItemRequiredCondition(ProtagonistCondition): + def __init__(self, required_item): + self.required_item = required_item + + def check(self, protagonist): + return protagonist.has_item(self.required_item) + + +class Action(object): + """Representation of an action that can be performed. + + If the (optional) condition is met, the provided function will be called + with the protagonist and the target as parameters. + """ + def __init__(self, func, condition=None): + self.func = func + self.condition = condition + + def check(self, protagonist): + if self.condition is None: + return True + return self.condition.check(protagonist) + + def perform(self, protagonist, target): + if not self.check(protagonist): + raise ValueError("Attempt to perform invalid action.") + return self.func(protagonist, target) + + +class Interactible(object): + """The property of interactibility on a thing. + """ + + def __init__(self, *actions): + self.actions = actions + + def select_action(self, protagonist): + """Select a possible action given the protagonist's state. + """ + for action in self.actions: + if action.check(protagonist): + return action + return None diff -r 3e4d8091268c -r c03982fe3c70 nagslang/protagonist.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nagslang/protagonist.py Sun Sep 01 16:07:39 2013 +0200 @@ -0,0 +1,54 @@ + + +class Protagonist(object): + """Representation of our fearless protagonist. + + TODO: Factor out a bunch of this stuff when we need it for other objects. + """ + + HUMAN_FORM = 'human' + WOLF_FORM = 'wolf' + + def __init__(self): + self.inventory = {} + self.form = self.HUMAN_FORM + + @classmethod + def from_saved_state(cls, saved_state): + """Create an instance from the provided serialised state. + """ + obj = cls() + # TODO: Update from saved state. + return obj + + def act_on(self, target): + """Perform an action on the target. + """ + # TODO: Decide how best to do this. + pass + + def change_to_form(self, form): + """Change to a particular form. + + This will be a no-op if we're already in this form. + """ + pass + + def swap_form(self): + """Swap to your other form. + """ + pass + + def attack(self): + """Attempt to hurt something. + """ + pass + + def in_wolf_form(self): + return self.form == self.WOLF_FORM + + def in_human_form(self): + return self.form == self.HUMAN_FORM + + def has_item(self, item): + return item in self.inventory diff -r 3e4d8091268c -r c03982fe3c70 nagslang/tests/__init__.py diff -r 3e4d8091268c -r c03982fe3c70 nagslang/tests/test_environment.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nagslang/tests/test_environment.py Sun Sep 01 16:07:39 2013 +0200 @@ -0,0 +1,161 @@ +from unittest import TestCase + +from nagslang import environment + + +class YesCondition(environment.ProtagonistCondition): + def check(self, protagonist): + return True + + +class NoCondition(environment.ProtagonistCondition): + def check(self, protagonist): + return False + + +class ErrorCondition(environment.ProtagonistCondition): + def check(self, protagonist): + raise Exception() + + +class FakeProtagonist(object): + def __init__(self, wolf=False, human=False, item=False): + self._wolf = wolf + self._human = human + self._item = item + + def in_wolf_form(self): + return self._wolf + + def in_human_form(self): + return self._human + + def has_item(self, item): + return self._item + + +class TestConditions(TestCase): + def assert_met(self, condition, protagonist=None): + self.assertTrue(condition.check(protagonist)) + + def assert_not_met(self, condition, protagonist=None): + self.assertFalse(condition.check(protagonist)) + + def assert_error(self, condition, protagonist=None): + self.assertRaises(Exception, condition.check, protagonist) + + def test_test_conditions(self): + self.assert_met(YesCondition()) + self.assert_not_met(NoCondition()) + self.assert_error(ErrorCondition()) + + def test_all_conditions(self): + yes = YesCondition() + no = NoCondition() + err = ErrorCondition() + self.assert_met(environment.AllConditions(yes, yes)) + self.assert_not_met(environment.AllConditions(yes, no)) + self.assert_not_met(environment.AllConditions(no, err)) + self.assert_error(environment.AllConditions(err, yes)) + self.assert_error(environment.AllConditions(yes, err)) + + def test_any_condition(self): + yes = YesCondition() + no = NoCondition() + err = ErrorCondition() + self.assert_met(environment.AnyCondition(no, yes)) + self.assert_not_met(environment.AnyCondition(no, no)) + self.assert_met(environment.AnyCondition(yes, err)) + self.assert_error(environment.AnyCondition(err, yes)) + self.assert_error(environment.AnyCondition(no, err)) + + def test_wolf_form_condition(self): + wolf = environment.WolfFormCondition() + self.assert_met(wolf, FakeProtagonist(wolf=True, human=True)) + self.assert_met(wolf, FakeProtagonist(wolf=True, human=False)) + self.assert_not_met(wolf, FakeProtagonist(wolf=False, human=True)) + self.assert_not_met(wolf, FakeProtagonist(wolf=False, human=False)) + + def test_human_form_condition(self): + human = environment.HumanFormCondition() + self.assert_met(human, FakeProtagonist(human=True, wolf=True)) + self.assert_met(human, FakeProtagonist(human=True, wolf=False)) + self.assert_not_met(human, FakeProtagonist(human=False, wolf=True)) + self.assert_not_met(human, FakeProtagonist(human=False, wolf=False)) + + def test_item_required_condition(self): + item = environment.ItemRequiredCondition('item') + self.assert_met(item, FakeProtagonist(item=True)) + self.assert_not_met(item, FakeProtagonist(item=False)) + + +class TestActions(TestCase): + def setUp(self): + self.state = {} + + def make_action_func(self, name): + self.state.setdefault(name, []) + + def action_func(protagonist, target): + self.state[name].append((protagonist, target)) + return len(self.state[name]) + + return action_func + + def make_action(self, name, condition=None): + return environment.Action(self.make_action_func(name), condition) + + def assert_state(self, **kw): + self.assertEqual(self.state, kw) + + def assert_action_selected(self, action, interactible, protagonist): + self.assertEqual(action, interactible.select_action(protagonist)) + + def test_unconditional_action(self): + action = self.make_action('action') + self.assert_state(action=[]) + self.assertTrue(action.check(None)) + self.assert_state(action=[]) + self.assertEqual(1, action.perform('p', 't')) + self.assert_state(action=[('p', 't')]) + self.assertEqual(2, action.perform('p2', 't2')) + self.assert_state(action=[('p', 't'), ('p2', 't2')]) + + def test_conditional_action(self): + yes_action = self.make_action('yes_action', YesCondition()) + no_action = self.make_action('no_action', NoCondition()) + self.assert_state(yes_action=[], no_action=[]) + self.assertTrue(yes_action.check(None)) + self.assert_state(yes_action=[], no_action=[]) + self.assertFalse(no_action.check(None)) + self.assert_state(yes_action=[], no_action=[]) + self.assertEqual(1, yes_action.perform('p', 't')) + self.assert_state(yes_action=[('p', 't')], no_action=[]) + + def test_perform_bad_action(self): + action = self.make_action('action', NoCondition()) + self.assert_state(action=[]) + self.assertFalse(action.check(None)) + self.assert_state(action=[]) + self.assertRaises(ValueError, action.perform, 'p', 't') + + def test_interactible_no_actions(self): + interactible = environment.Interactible() + self.assert_action_selected(None, interactible, None) + + def test_interactible_unconditional_action(self): + action = self.make_action('action') + interactible = environment.Interactible(action) + self.assert_action_selected(action, interactible, None) + + def test_interactible_conditional_actions(self): + wolf_action = self.make_action('wolf', environment.WolfFormCondition()) + item_action = self.make_action( + 'item', environment.ItemRequiredCondition('item')) + interactible = environment.Interactible(wolf_action, item_action) + self.assert_action_selected( + wolf_action, interactible, FakeProtagonist(wolf=True)) + self.assert_action_selected( + item_action, interactible, FakeProtagonist(item=True)) + self.assert_action_selected( + wolf_action, interactible, FakeProtagonist(wolf=True, item=True))