#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
This module lists the policies that the environment uses to provide feedback to the player.
"""
import logging
from abc import abstractmethod
from ..policies import base_policies as bpolicies
from ..language import components as lc
from ..environment import entities as em
from ..environment import actions
from ..environment import helpers as env_helpers
from ..language import sentences as tsentences
from ..language import desc_mappers
logger = logging.getLogger(__name__)
[docs]
class EnvPolicy(bpolicies.Policy):
""" The environment self provides a response to every player's action.
The player is None, since the environment is not governed by any entity.
"""
def __init__(self, dialogue=None):
super().__init__(None, dialogue)
self.dialogue = dialogue
[docs]
def execute(self, include_goal=False, last_utter=None):
"""
Returns the environment response as feedback to the last utterance of the agent.
Parameters
----------
include_goal : bool, optional
Whether to include the goal of the environment.
last_utter : Sentence, optional
If the last utterance is not provided, the last dialogue utterance is taken.
Returns
-------
list
Returns multiple valid environmental responses.
None, optional
The goal of the environment is None, since the goal is currently generated in the
agent's self.
"""
if last_utter is None:
dia_utterances = self.dialogue.get_utterances()
if len(dia_utterances) > 0:
last_utter = dia_utterances[-1]
else:
last_utter = None
if last_utter is None or len(last_utter.describers) == 0 or last_utter.speaker is None:
responses = None
else:
try:
responses = self.parse(last_utter=last_utter)
except Exception as err:
logger.error(err, exc_info=True)
responses = None
if include_goal:
return responses, None
return responses
[docs]
@abstractmethod
def parse(self, last_utter):
"""
Parses the utterance into an actions.<action> method call that provides the environment feedback.
Parameters
----------
last_utter : Sentence
The utterance to be parsed.
Returns
-------
res : Sentence
Returns multiple valid environmental responses.
"""
[docs]
def get_steps(self, **params):
""" Returns the valid responses of the environment """
steps = self.execute(include_goal=False, **params)
return steps
[docs]
def get_goal(self, **params):
""" Returns the goal of the environment. The environment goal is None since
the agents have goals.
"""
return None
[docs]
def save_state(self):
"""
Returns the state from the parent class and additionally the world state.
The world state is returned since the environment's policies make changes
in the world.
"""
if self.dialogue is not None:
state = self.dialogue.dia_generator.world.save_state()
else:
state = None
return super().save_state(), state
[docs]
def recover_state(self, state):
""" Recovers the parent state and the environment state. """
super().recover_state(state[0])
if self.dialogue is not None:
self.dialogue.dia_generator.world.recover_state(state[1])
[docs]
class EmptyPolicy(EnvPolicy):
""" This self is used when a player issues an empty sentence (that does not contain any verbs or
describers). """
[docs]
def parse(self, last_utter):
""" Returns a sentence: <speaker> issued an empty response if the last speaker's utterance
has no describers or no verb.
"""
describers = last_utter.describers
result = None
if len(describers) == 0 or describers[0].get_arg('Rel') is None:
result = tsentences.issue(last_utter.speaker,
None,
None,
"issued",
["an", "empty", "response"]
)
return result
[docs]
class GoPolicy(EnvPolicy):
""" This self is used for providing feedback when a player tries moving in the world."""
[docs]
def parse(self, last_utter):
"""
Parses the player's utterance into an actions.go method call that provides the environmental feedback.
The utterance has to be in the form:
<player> tries going <direction> (from_location)
"""
world = self.dialogue.dia_generator.world
inner_utter, player = env_helpers.extract_tries_sent(last_utter, world)
if inner_utter is None or player is None:
return None
describer = inner_utter.describers[0]
direction = describer.get_arg('AM-DIR')
from_location = describer.get_arg('Arg-DIR')
if inner_utter == tsentences.go(rel=('going', None),
direction=(direction, None),
source_location=(from_location, None)):
if from_location is not None:
if (isinstance(from_location, list) and len(from_location) == 2
and from_location[0] == "from" and isinstance(from_location[1], em.Entity)):
from_location = getattr(world, from_location[1].properties.get("var_name"), from_location)
else:
return None
if direction is not None and isinstance(direction, str):
res = actions.go(player, direction, from_location)
return res
return None
[docs]
class GetPolicy(EnvPolicy):
""" This self is used for providing feedback when a player tries getting an entity in the world."""
[docs]
def parse(self, last_utter):
"""
Parses the player's utterance into an actions.get method call that provides the environmental feedback.
The utterance has to be in the form:
<player> tries getting <entity> <location_preposition> <location>
The location refers to the entity's location.
For example, Hannah tries getting the plastic container under the table.
Here the plastic container's location is under the table.
"""
world = self.dialogue.dia_generator.world
inner_utter, player = env_helpers.extract_tries_sent(last_utter, world)
if inner_utter is None or player is None:
return None
describer = inner_utter.describers[0]
entity = describer.get_arg('Arg-PPT')
if not isinstance(entity, em.Entity):
return None
prep_location = describer.get_arg('Arg-DIR')
res = None
if (inner_utter == tsentences.get(rel=('getting', None),
entity=(entity, None),
prepos_location=(prep_location, None))):
if (isinstance(prep_location, list) and len(prep_location) == 2
and isinstance(prep_location[0], str) and isinstance(prep_location[1], em.Entity)):
location_position = prep_location[0]
location = getattr(world, prep_location[1].properties.get("var_name"))
res = actions.get(entity, player, location, location_position)
elif prep_location is None:
res = tsentences.be([entity, "'s", "location"],
"is",
None,
["absent", "in", "the", "sentence", last_utter])
return res
[docs]
class DropPolicy(EnvPolicy):
""" This self is used for providing a response when a player tries dropping an entity in the world."""
[docs]
def parse(self, last_utter):
"""
Parses the player's utterance into an actions.drop method call that provides the environmental feedback.
The utterance has to be in the following form:
<player> tries dropping <entity> <location_position> <location>
The location refers to the one where the entity should be dropped.
For example, The big person tries dropping the small ball in the toy's container.
"""
world = self.dialogue.dia_generator.world
inner_utter, player = env_helpers.extract_tries_sent(last_utter, world)
if inner_utter is None or player is None:
return None
inner_desc = inner_utter.describers[0]
entity = inner_desc.get_arg('Arg-PPT')
if not isinstance(entity, em.Entity):
return None
prep_location = inner_desc.get_arg('Arg-GOL')
res = None
if (inner_utter == tsentences.drop(rel=('dropping', None),
entity=(entity, None),
prepos_location=(prep_location, None)
)):
if (isinstance(prep_location, list)
and len(prep_location) == 2 and isinstance(prep_location[0], str)
and isinstance(prep_location[1], em.Entity)):
location_position = prep_location[0]
location = getattr(world, prep_location[1].properties.get("var_name"))
entity = getattr(world, entity.properties.get("var_name"))
res = actions.drop(entity, player, location, location_position)
elif prep_location is None:
res = tsentences.be([entity, "'s", "location"],
"is",
None,
["absent", "in", "the", "sentence", last_utter])
return res
[docs]
class LookPolicy(EnvPolicy):
""" This self provides a response when a player tries looking at an entity in the world."""
[docs]
def parse(self, last_utter):
"""
Parses the player's utterance into an actions.look method call that provides the environmental feedback.
The utterance has to be in the form:
<player> tries looking <location_preposition1> <entity> <location_preposition2> <location>
The location refers to the entity's location.
For example, John tries looking in the toys container in the bedroom.
"""
world = self.dialogue.dia_generator.world
inner_utter, player = env_helpers.extract_tries_sent(last_utter, world)
if inner_utter is None or player is None:
return None
inner_desc = inner_utter.describers[0]
prep_thing_looked = inner_desc.get_arg('Arg-PPT')
#item_location = inner_desc.get_arg('AM-LOC')
if (inner_utter == tsentences.look(rel=('looking', None),
thing_looked=(prep_thing_looked, None))):
if len(prep_thing_looked) == 4:
item_location = prep_thing_looked[2:4]
prep_thing_looked = prep_thing_looked[0:2]
else:
item_location = None
if (isinstance(prep_thing_looked, list) and len(prep_thing_looked) == 2
and isinstance(prep_thing_looked[0], str) and isinstance(prep_thing_looked[1], em.Entity)):
look_position = prep_thing_looked[0]
thing_looked = getattr(world, prep_thing_looked[1].properties.get("var_name"))
if item_location is None:
res = tsentences.be([thing_looked, "'s", "location"],
"is",
None,
["absent", "in", "the", "sentence", last_utter])
else:
res = actions.look(thing_looked, player, look_position,
[item_location[0], getattr(world, item_location[1].properties.get("var_name"))])
return res
return None
[docs]
class SayPolicy(EnvPolicy):
""" This self provides a response when a player says a sentence """
[docs]
def parse(self, last_utter):
""" Checks whether a sentence is in the format <user> says to <agent>: <inner_sentence>
and if so, it returns an empty sentence with speaker=None.
speaker=None indicates that the sentence is uttered by the environment.
The empty sentence is returned because there is no need to implement actions.say.
The agent can say anything, and the agent's utterance is anyway added to the context.
"""
describer = last_utter.describers[0]
user = describer.get_arg('Arg-PAG')
if user != last_utter.speaker:
return None
mapped_sent = desc_mappers.say(last_utter.describers)
if describer.get_arg("AM-NEG") is None and mapped_sent == last_utter:
empty_sent = lc.Sentence()
empty_sent.describers.append(lc.Describer())
empty_sent.speaker = None
return empty_sent
return None
[docs]
class EnvAutoPolicy(bpolicies.AutoPolicy):
""" A self that automatically selects the right self out of a list of environmental policies. """
[docs]
def execute(self, include_goal=False, **params):
"""
Runs the self and returns a list of valid environment responses.
Additionally, the goal is returned if include_goal is set to True.
If all the policies in self.list_policies return None, then the environment returns:
<player> issued an unrecognizable sentence.
"""
if "last_utter" in params:
last_utter = params["last_utter"]
else:
dia_utterances = self.dialogue.get_utterances()
if len(dia_utterances) > 0:
last_utter = dia_utterances[-1]
else:
last_utter = None
params['last_utter'] = last_utter
if last_utter is not None and last_utter.speaker is not None:
result = super().execute(include_goal, **params)
if result is None:
if len(last_utter.describers) > 0:
"""
result = tsentences.issue(last_utter.speaker,
None,
None,
"issued",
["a", "sentence", "with", "unexpected", "describer/s"]
)
"""
result = tsentences.be(None, "was", None, ["an", "error", "in", "the", "serialization"])
result.parts.insert(0, lc.Word("There"))
else:
# the sent has no describers
"""
result = tsentences.issue(last_utter.speaker,
None,
None,
"issued",
["an", "sentence", "without", "describers"])
"""
result = tsentences.be(None, "was", None, ["an", "error", "in", "the", "serialization"])
result.parts.insert(0, lc.Word("There"))
not_inacc = tsentences.be(["The", "sentence"],
"is",
"not",
"inaccurate")
not_inacc.parts.insert(4, lc.Word("necessarily"))
not_inacc.describers[0].args["AM-ADV"] = "necessarily"
result = tsentences.cont([result, not_inacc])
else:
result = None
if result is not None and last_utter.speaker is not None and 'player' not in last_utter.speaker.attr_seen:
if isinstance(result, list):
result[0].meta_sent.append(tsentences.be(last_utter.speaker, 'is', None, 'player'))
else:
result.meta_sent.append(tsentences.be(last_utter.speaker, 'is', None, 'player'))
if include_goal is True and not isinstance(result, tuple):
result = (result, None)
return result