Of Course!
Hello, loves!
As soon as I figure the quest giver is done, I get a request for a new feature. Great! Tout le monde déteste l’IA.
I improved the flower drawing a bit and reinstalled it. I had also made a pink flower, and I stuck that into the dungeon to see how it looked, and that gave me an idea. The bee would be more interesting if he only wanted the yellow flower, and if you bring him the pink one, only then would he mention that his flower was yellow. You might get it right the first time, or maybe not.
(This, of course, makes me think the bee should decide randomly what color he wants. We’ll belay that idea for now.)
Let’s enhance the bee, Content, and QuestGiverDenizen to include a list of items that are not valid but that trigger better clues.
Let’s start with the QGDenizen, adding a state and events to it. Here’s the machine as it stands now:
class QuestGiverDenizen:
def __init__(self, *, knowledge):
self.knowledge = knowledge
self.machine=Machine(
initial_state='seeking',
seeking=State(
has_item=Event(to='satisfied', hook=self._giving_hook),
no_item=Event(to='seeking', hook=self._seeking_hook),
),
satisfied=State(
has_item=Event(to='satisfied', hook=self._satisfied_hook),
no_item=Event(to='satisfied', hook=self._satisfied_hook),
),
)
We’ll have a wrong_item event and state. There are some options here to think about but we’ll just pick a way for it to be and then see how it feels.
class QuestGiverDenizen:
def __init__(self, *, knowledge):
self.knowledge = knowledge
self.machine=Machine(
initial_state='seeking',
seeking=State(
has_item=Event(to='satisfied', hook=self._giving_hook),
wrong_item=Event(to="wrong", hook=self._wrong_item_hook),
no_item=Event(to='seeking', hook=self._seeking_hook),
),
wrong=State(
has_item=Event(to='satisfied', hook=self._giving_hook),
wrong_item=Event(to="wrong", hook=self._wrong_item_hook),
no_item=Event(to='seeking', hook=self._seeking_hook),
),
satisfied=State(
has_item=Event(to='satisfied', hook=self._satisfied_hook),
wrong_item=Event(to="satisfied", hook=self._satisfied_hook),
no_item=Event(to='satisfied', hook=self._satisfied_hook),
),
)
def _wrong_item_hook(self, interactor):
interactor.announce(next(self.knowledge.wrong_item_sayings))
I note that the machine setup we have grows rapidly. We may need to do something about that someday.
Now we need the wrong-item sayings for the bee.
def add_bee(factory):
seeking_sentences = ['Where is it?',
'Where can it be?',
'I lost my nice flower, maybe you can find it.',
'Please help me find my pretty yellow flower!']
giving_sentences = [
'Oh, thank you!',
'I am most grateful!',
'Here\'s something you may need.',
]
wrong_item_sentences = [
'wrong!'
]
satisfied_sentences = [
'I\'m just a happy little bee!',
'Hmmm, hmmm, just buzzin along.',
'Nothing to see here, just a bee'
]
gift = ContentFactory().receivable(name='delicious honeycomb',
resource='honeycomb.png',
scale=1)
quest_item = 'a yellow flower'
resources = ['bee.png']
scale = 0.5
bee = factory.quest_giver(name="Buzz", quest_item=quest_item, seeking_sentences=seeking_sentences,
giving_sentences=giving_sentences, satisfied_sentences=satisfied_sentences,
wrong_item_sentences=wrong_item_sentences, gift=gift,
resources=resources, scale=scale)
That’ll do for testing. Now add in a wrong item, of which we want to allow a list, I think.
quest_item = 'a yellow flower'
wrong_items= ['a pink flower', 'a blue flower']
resources = ['bee.png']
scale = 0.5
bee = factory.quest_giver(name="Buzz", quest_item=quest_item, seeking_sentences=seeking_sentences,
giving_sentences=giving_sentences, satisfied_sentences=satisfied_sentences,
wrong_items=wrong_items, wrong_item_sentences=wrong_item_sentences, gift=gift,
resources=resources, scale=scale)
That should do, although we do not have a blue flower at present. Now in the content, we start here:
class ContentFactory:
def quest_giver(self, *, name, quest_item, seeking_sentences, giving_sentences, satisfied_sentences, gift,
resources, scale):
def interaction(self, interactor):
event = 'has_item' if interactor.has(quest_item) else 'no_item'
self.info.denizen.event(event, interactor)
return False
Need to enhance the assignment of event.
Bah!
I only had to start this to realize that the items need not go into the knowledge, just into the quest_giver constructor. We’ll fix that.
A Bit of Thrashing Ensues.
I have no excuse, it just happened. I think these things are a bit tricky to wire up. Here’s the whole thing:
main.py
def add_bee(factory):
seeking_sentences = ['Where is it?',
'Where can it be?',
'I lost my nice flower, maybe you can find it.',
'Please help me find my pretty yellow flower!']
giving_sentences = [
'Oh, thank you!',
'I am most grateful!',
'Here\'s something you may need.',
]
wrong_item_sentences = [
'That is a pretty flower but not the one I wanted.',
'Thanks, but mine was a different color.',
'You are kind, but I had a nice yellow one.'
]
satisfied_sentences = [
'I\'m just a happy little bee!',
'Hmmm, hmmm, just buzzin along.',
'Nothing to see here, just a bee'
]
gift = ContentFactory().receivable(name='delicious honeycomb',
resource='honeycomb.png',
scale=1)
quest_item = 'a yellow flower'
wrong_items= ['a pink flower', 'a blue flower']
resources = ['bee.png']
scale = 0.5
bee = factory.quest_giver(name="Buzz", quest_item=quest_item, wrong_items=wrong_items,
seeking_sentences=seeking_sentences, wrong_sentences=wrong_item_sentences,
giving_sentences=giving_sentences, satisfied_sentences=satisfied_sentences, gift=gift,
resources=resources, scale=scale)
cell = Cell(33, 25)
cell.add_content(bee)
Here we set up all the data for the quest_giver configuration. There’s a lot, but we want a lot of behavior.
I think we should rename thewrong_sentences parameter to wrong_item_sentences. Done. Moving right along:
class ContentFactory:
def quest_giver(self, *, name, quest_item, wrong_items, seeking_sentences, wrong_item_sentences, giving_sentences,
satisfied_sentences, gift, resources, scale):
def interaction(self, interactor):
if interactor.has(quest_item):
event = 'has_item'
elif interactor.has_any(wrong_items):
event = 'wrong_item'
else:
event = 'no_item`'
self.info.denizen.event(event, interactor)
return False
knowledge = self.create_knowledge(name, seeking_sentences, wrong_item_sentences, giving_sentences,
satisfied_sentences, gift, quest_item)
denizen = QuestGiverDenizen(knowledge=knowledge)
info = SimpleNamespace(denizen=denizen)
return Content(name=name, resources=resources,
scale=scale, interaction=interaction,
info=info)
Here we have the enhanced interaction method that determines whether the event is ‘has_item’, ‘wrong_item’, or
no_item’. I am kind of assuming that we’ll enhance the QGDenizen to accept the wrong items, so that they are no longer in Dot’s inventory. That is not yet done.
In create_knowledge, which should be private, change that … we set up the knowledge NameSpace for the QGDenizen:
def _create_knowledge(self, name, seeking_sentences, wrong_sentences, giving_sentences, satisfied_sentences, gift,
quest_item):
seeking_sayer = NameSayer.cycle(name, seeking_sentences)
wrong_sayer = NameSayer.cycle(name, wrong_sentences)
giving_sayer = NameSayer.once(name, giving_sentences)
satisfied_sayer = NameSayer.random(name, satisfied_sentences)
return SimpleNamespace(
quest_item=quest_item,
seeking_sayings=seeking_sayer,
wrong_item_sayings=wrong_sayer,
giving_sayings=giving_sayer,
satisfied_sayings=satisfied_sayer,
gift=gift,
)
And in the Denizen, we have the machine, just as shown above, of course with all the hooks. For completeness, here it is in its entirety:
class QuestGiverDenizen:
def __init__(self, *, knowledge):
self.knowledge = knowledge
self.machine=Machine(
initial_state='seeking',
seeking=State(
has_item=Event(to='satisfied', hook=self._giving_hook),
wrong_item=Event(to="wrong", hook=self._wrong_item_hook),
no_item=Event(to='seeking', hook=self._seeking_hook),
),
wrong=State(
has_item=Event(to='satisfied', hook=self._giving_hook),
wrong_item=Event(to="wrong", hook=self._wrong_item_hook),
no_item=Event(to='seeking', hook=self._seeking_hook),
),
satisfied=State(
has_item=Event(to='satisfied', hook=self._satisfied_hook),
wrong_item=Event(to="satisfied", hook=self._satisfied_hook),
no_item=Event(to='satisfied', hook=self._satisfied_hook),
),
)
def event(self, event, interactor):
self.machine.event(event, interactor)
def _seeking_hook(self, interactor):
interactor.announce(next(self.knowledge.seeking_sayings))
def _giving_hook(self, interactor):
for saying in self.knowledge.giving_sayings:
interactor.announce(saying)
interactor.receive_content(self.knowledge.gift)
def _wrong_item_hook(self, interactor):
interactor.announce(next(self.knowledge.wrong_item_sayings))
def _satisfied_hook(self, interactor):
interactor.announce(next(self.knowledge.satisfied_sayings))
One thing that I wonder about is what will happen if there are no strings in the wrong_items collection. Eek. I get an error saying no_item is not found:
File "/Users/ron/PycharmProjects/dungeon/src/state_machiine.py", line 30, in event
current_transition = current_state[event]
~~~~~~~~~~~~~^^^^^^^
File "/Users/ron/PycharmProjects/dungeon/src/state_machiine.py", line 14, in __getitem__
return self.info[item]
~~~~~~~~~^^^^^^
KeyError: 'no_item`'
Ah. That tick mark. I have an error in the string. That’s an issue with strings for states. We should improve that aspect of the design. Also we need better tests.
And I’ll add those to my list of things to work on. So much for being done with the quest giving denizen.
And, of course, what we’d probably really like to have is a different set of responses for each kind of wrong_item. Perhaps in due time. As things stand, we just inspect Dot’s inventory to see if she has what we’re looking for. We would likely want something better, including but no limited to Dot offering the item rather than the denizen just seeing she has it and accepting it.
But we’re here to learn things and have fun, so we may never get to that. Anyway, we have a better denizen, and some opportunities for improvement.
Once again I am surprised at what we work on. I wonder who’s running this show. See you next time!
