getRequiredParameterNames() {
+ return newSet(); // empty set
+ }
+
+ @Override
+ public boolean isFallbackRequired() {
+ return false;
+ }
+
}
\ No newline at end of file
diff --git a/dialog/src/main/java/roboy/dialog/states/definitions/MonologState.java b/dialog/src/main/java/roboy/dialog/states/definitions/MonologState.java
new file mode 100644
index 00000000..72c481a4
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/definitions/MonologState.java
@@ -0,0 +1,39 @@
+package roboy.dialog.states.definitions;
+
+
+import roboy.linguistics.sentenceanalysis.Interpretation;
+
+public abstract class MonologState extends State {
+
+ /**
+ * Create a state object with given identifier (state name) and parameters.
+ *
+ * The parameters should contain a reference to a state machine for later use.
+ * The state will not automatically add itself to the state machine.
+ *
+ * @param stateIdentifier identifier (name) of this state
+ * @param params parameters for this state, should contain a reference to a state machine
+ */
+ public MonologState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ }
+
+
+ /**
+ * Defines how to react to an input. This is usually the answer to the incoming question or some other statement.
+ * If this state can't react, it can return 'null' to trigger the fallback state for the answer.
+ *
+ * Note: In the new architecture, react() does not define the next state anymore! Reaction and state
+ * transitions are now decoupled. State transitions are defined in getNextState()
+ *
+ * @param input input from the person we talk to
+ * @return reaction to the input (should not be null)
+ */
+ @Override
+ public Output react(Interpretation input){
+
+ return Output.skipInput();
+ }
+
+
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/definitions/State.java b/dialog/src/main/java/roboy/dialog/states/definitions/State.java
index 8dbea581..d2101166 100644
--- a/dialog/src/main/java/roboy/dialog/states/definitions/State.java
+++ b/dialog/src/main/java/roboy/dialog/states/definitions/State.java
@@ -64,13 +64,14 @@ public static class Output {
private final Logger logger = LogManager.getLogger();
public enum OutputType {
- INTERPRETATION, SAY_NOTHING, USE_FALLBACK, END_CONVERSATION
+ INTERPRETATION, SAY_NOTHING, USE_FALLBACK, END_CONVERSATION, SKIP_INPUT
}
private final OutputType type;
private final Interpretation interpretation;
private Segue segue;
private RoboyEmotion emotion;
+ private String sound;
/**
* Private constructor, used only inside static methods.
@@ -81,7 +82,6 @@ private Output(OutputType type, Interpretation interpretation) {
this.type = type;
this.interpretation = interpretation;
this.segue = null;
- this.emotion = null;
if(interpretation != null){
this.emotion = interpretation.getEmotion();
}
@@ -151,6 +151,14 @@ public static Output endConversation(String lastWords) {
return new Output(OutputType.END_CONVERSATION, new Interpretation(lastWords));
}
+ /**
+ * Skip the users input
+ * @return State.Output object with appropriate settings
+ */
+ public static Output skipInput() {
+ return new Output(OutputType.SKIP_INPUT, null);
+ }
+
// Non-static methods
@@ -170,6 +178,10 @@ public boolean isEndOfConversation() {
return type == OutputType.END_CONVERSATION;
}
+ public boolean isSkippingUser() {
+ return type == OutputType.SKIP_INPUT;
+ }
+
public Interpretation getInterpretation() {
return interpretation;
}
@@ -215,6 +227,18 @@ public Output setEmotion(RoboyEmotion emotion) {
public boolean hasEmotion() { return emotion != null; }
public RoboyEmotion getEmotion() { return emotion; }
+ public Output addSound(String filename) {
+ if (type == OutputType.USE_FALLBACK) {
+ logger.warn("Adding a sound to an answer that requires fallback is not allowed! " +
+ "Sound behaviour is defined in the fallback state.");
+ }
+ this.sound = filename;
+ return this;
+ }
+
+ public boolean hasSound() {return sound != null; }
+ public String getSoundFilename() { return sound; }
+
}
//endregion
diff --git a/dialog/src/main/java/roboy/dialog/states/devoStates/IntroductionState.java b/dialog/src/main/java/roboy/dialog/states/devoStates/IntroductionState.java
new file mode 100644
index 00000000..72e7bae8
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/devoStates/IntroductionState.java
@@ -0,0 +1,213 @@
+package roboy.dialog.states.devoStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.Segue;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.linguistics.sentenceanalysis.Interpretation;
+import roboy.memory.Neo4jProperty;
+import roboy.memory.Neo4jRelationship;
+import roboy.memory.nodes.Interlocutor;
+import roboy.memory.nodes.Interlocutor.RelationshipAvailability;
+import roboy.memory.nodes.MemoryNodeModel;
+import roboy.memory.nodes.Roboy;
+import roboy.util.Agedater;
+import roboy.util.QAJsonParser;
+import roboy.util.RandomList;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Set;
+
+import static roboy.memory.Neo4jProperty.*;
+import static roboy.memory.Neo4jRelationship.*;
+import static roboy.memory.nodes.Interlocutor.RelationshipAvailability.NONE_AVAILABLE;
+import static roboy.memory.nodes.Interlocutor.RelationshipAvailability.SOME_AVAILABLE;
+
+/**
+ * This state will:
+ * - ask the interlocutor for his name
+ * - query memory if the person is already known
+ * - create and update the interlocutor in the context
+ * - take one of two transitions: knownPerson or newPerson
+ *
+ * IntroductionState interface:
+ * 1) Fallback is not required.
+ * 2) Outgoing transitions that have to be defined:
+ * - knownPerson: following state if the person is already known
+ * - newPerson: following state if the person is NOT known
+ * 3) No parameters are used.
+ */
+public class IntroductionState extends State {
+ private QAJsonParser infoValues;
+ private final String UPDATE_KNOWN_PERSON = "knownPerson";
+ private final String LEARN_ABOUT_PERSON = "newPerson";
+ private final Logger LOGGER = LogManager.getLogger();
+ private final String INFO_FILE_PARAMETER_ID = "infoFile";
+ private final RandomList introPhrases = new RandomList<>("What's your name?");
+ private final RandomList successResponsePhrases = new RandomList<>("Hey, I know you, %s!");
+ private final RandomList failureResponsePhrases = new RandomList<>("Nice to meet you, %s!");
+
+ private Neo4jRelationship[] personPredicates = { FROM, HAS_HOBBY, WORK_FOR, STUDY_AT };
+ private RandomList roboyRelatioshipPredicates = new RandomList<>(FROM, MEMBER_OF, LIVE_IN, HAS_HOBBY, FRIEND_OF, CHILD_OF, SIBLING_OF);
+ private RandomList roboyPropertiesPredicates = new RandomList<>(skills, abilities, future);
+ private State nextState;
+
+ public IntroductionState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ String infoListPath = params.getParameter(INFO_FILE_PARAMETER_ID);
+ LOGGER.info(" -> The infoList path: " + infoListPath);
+ infoValues = new QAJsonParser(infoListPath);
+ }
+
+ @Override
+ public Output act() {
+ return Output.say(getIntroPhrase());
+ }
+
+ @Override
+ public Output react(Interpretation input) {
+ // expecting something like "My name is NAME"
+
+ // 1. get name
+ String name = getNameFromInput(input);
+
+ if (name == null) {
+ // input couldn't be parsed properly
+ // TODO: do something intelligent if the parser fails
+ nextState = this;
+ LOGGER.warn("IntroductionState couldn't get name! Staying in the same state.");
+ return Output.say("Sorry, my parser is out of service.");
+ // alternatively: Output.useFallback() or Output.sayNothing()
+ }
+
+
+ // 2. get interlocutor object from context
+ // this also should query memory and do other magic
+ Interlocutor person = getContext().ACTIVE_INTERLOCUTOR.getValue();
+ person.addName(name);
+ // Roboy roboy = new Roboy(getMemory());
+
+
+ // 3. update interlocutor in context
+ updateInterlocutorInContext(person);
+ String retrievedPersonalFact = "";
+ Double segueProbability = 0.0;
+
+ // 4. check if person is known/familiar
+ if (person.FAMILIAR) {
+ // 4a. person known/familiar
+ // TODO: get some hobbies or occupation of the person to make answer more interesting
+ RandomList nodes = getMemNodesByIds(person.getRelationships(FRIEND_OF));
+ if (!nodes.isEmpty()) {
+ retrievedPersonalFact = " You are friends with " +
+ nodes.getRandomElement().getProperties().get(Neo4jProperty.name).toString();
+ }
+
+ RelationshipAvailability availability = person.checkRelationshipAvailability(personPredicates);
+ if (availability == SOME_AVAILABLE) {
+ nextState = (Math.random() < 0.3) ? getTransition(UPDATE_KNOWN_PERSON) : getTransition(LEARN_ABOUT_PERSON);
+ } else if (availability == NONE_AVAILABLE) {
+ nextState = getTransition(LEARN_ABOUT_PERSON);
+ } else {
+ nextState = getTransition(UPDATE_KNOWN_PERSON);
+ }
+ } else {
+ // 4b. person is not known
+ nextState = getTransition(LEARN_ABOUT_PERSON);
+ segueProbability = 0.6;
+ }
+ String retrievedRoboyFacts = getRoboyFactsPhrase(new Roboy(getMemory()));
+ Segue s = new Segue(Segue.SegueType.DISTRACT, segueProbability);
+ return Output.say(getResponsePhrase(person.getName(), person.FAMILIAR) +
+ retrievedPersonalFact + retrievedRoboyFacts).setSegue(s);
+ }
+
+ @Override
+ public State getNextState() {
+ return nextState;
+ }
+
+ private String getNameFromInput(Interpretation input) {
+ return getInference().inferProperty(name, input);
+ }
+
+ private void updateInterlocutorInContext(Interlocutor interlocutor) {
+ getContext().ACTIVE_INTERLOCUTOR_UPDATER.updateValue(interlocutor);
+ }
+
+ private String getIntroPhrase() {
+ return introPhrases.getRandomElement();
+ }
+
+ private String getResponsePhrase(String name, boolean familiar) {
+ if (familiar) {
+ return String.format(successResponsePhrases.getRandomElement(), name);
+ } else {
+ return String.format(failureResponsePhrases.getRandomElement(), name);
+ }
+ }
+
+ private String getRoboyFactsPhrase(Roboy roboy) {
+ String result = "";
+
+ // Get some random properties facts
+ if (roboy.getProperties() != null && !roboy.getProperties().isEmpty()) {
+ HashMap properties = roboy.getProperties();
+ if (properties.containsKey(full_name)) {
+ result += " " + String.format(infoValues.getSuccessAnswers(full_name).getRandomElement(), properties.get(full_name));
+ }
+ if (properties.containsKey(birthdate)) {
+ HashMap ages = new Agedater().determineAge(properties.get(birthdate).toString());
+ String retrievedAge = "0 days";
+ if (ages.get("years") > 0) {
+ retrievedAge = ages.get("years") + " years";
+ } else if (ages.get("months") > 0) {
+ retrievedAge = ages.get("months") + " months";
+ } else {
+ retrievedAge = ages.get("days") + " days";
+ }
+ result += " " + String.format(infoValues.getSuccessAnswers(age).getRandomElement(), retrievedAge);
+ } else if (properties.containsKey(age)) {
+ result += " " + String.format(infoValues.getSuccessAnswers(age).getRandomElement(), properties.get(age) + " years!");
+ }
+ if (properties.containsKey(skills)) {
+ RandomList retrievedResult = new RandomList<>(Arrays.asList(properties.get(skills).toString().split(",")));
+ result += " " + String.format(infoValues.getSuccessAnswers(skills).getRandomElement(), retrievedResult.getRandomElement());
+ }
+ if (properties.containsKey(abilities)) {
+ RandomList retrievedResult = new RandomList<>(Arrays.asList(properties.get(abilities).toString().split(",")));
+ result += " " + String.format(infoValues.getSuccessAnswers(abilities).getRandomElement(), retrievedResult.getRandomElement());
+ }
+ if (properties.containsKey(future)) {
+ RandomList retrievedResult = new RandomList<>(Arrays.asList(properties.get(future).toString().split(",")));
+ result += " " + String.format(infoValues.getSuccessAnswers(future).getRandomElement(), retrievedResult.getRandomElement());
+ }
+ }
+
+ if (result.equals("")) {
+ result = "I am Roboy 2.0! ";
+ }
+
+ // Get a random relationship fact
+ Neo4jRelationship predicate = roboyRelatioshipPredicates.getRandomElement();
+ MemoryNodeModel node = getMemNodesByIds(roboy.getRelationships(predicate)).getRandomElement();
+ if (node != null) {
+ String nodeName = "";
+ if (node.getProperties().containsKey(full_name) && !node.getProperties().get(full_name).equals("")) {
+ nodeName = node.getProperties().get(full_name).toString();
+ } else {
+ nodeName = node.getProperties().get(name).toString();
+ }
+ result += " " + String.format(infoValues.getSuccessAnswers(predicate).getRandomElement(), nodeName);
+ }
+
+ return result;
+ }
+
+ @Override
+ protected Set getRequiredTransitionNames() {
+ return newSet(UPDATE_KNOWN_PERSON, LEARN_ABOUT_PERSON);
+ }
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/devoStates/PersonalInformationAskingState.java b/dialog/src/main/java/roboy/dialog/states/devoStates/PersonalInformationAskingState.java
new file mode 100644
index 00000000..1b7cf5f9
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/devoStates/PersonalInformationAskingState.java
@@ -0,0 +1,92 @@
+package roboy.dialog.states.devoStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.context.contextObjects.IntentValue;
+import roboy.dialog.Segue;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.linguistics.sentenceanalysis.Interpretation;
+import roboy.memory.Neo4jRelationship;
+import roboy.memory.nodes.Interlocutor;
+import roboy.util.QAJsonParser;
+import roboy.util.RandomList;
+
+import java.util.Set;
+
+import static roboy.memory.Neo4jRelationship.*;
+
+/**
+ * Personal Information Asking State
+ *
+ * The state tries to interact with the Interlocutor to learn new information about the person.
+ * This information is sent to the Roboy Memory Module through Neo4jMemoryInterface for storing.
+ * Afterwards, Roboy can use this acquired data for the future interactions with the same person.
+ *
+ * - if there is no existing Interlocutor or the data is missing, ask a question
+ * - the question topic (intent) is selected from the Neo4jRelationship predicates
+ * - retrieve the questions stored in the QAList json file
+ * - update the Context IntentsHistory
+ * - try to extract the result from the Interpretation
+ * - retrieve the answers stored in the QAList json file
+ * - send the result to Memory
+ *
+ * PersonalInformationAskingState interface:
+ * 1) Fallback is not required.
+ * 2) Outgoing transitions that have to be defined:
+ * - TRANSITION_INFO_OBTAINED: following state if the question was asked
+ * 3) Required parameters: path to the QAList.json file.
+ */
+public class PersonalInformationAskingState extends State {
+ private QAJsonParser qaValues;
+ private Neo4jRelationship[] predicates = { FROM, HAS_HOBBY, WORK_FOR, STUDY_AT };
+ private Neo4jRelationship selectedPredicate;
+ private State nextState;
+
+ private final String TRANSITION_INFO_OBTAINED = "questionAnswering";
+ private final String QA_FILE_PARAMETER_ID = "qaFile";
+ final Logger LOGGER = LogManager.getLogger();
+
+ public final static String INTENTS_HISTORY_ID = "PIA";
+
+ public PersonalInformationAskingState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ String qaListPath = params.getParameter(QA_FILE_PARAMETER_ID);
+ LOGGER.info(" -> The QAList path: " + qaListPath);
+ qaValues = new QAJsonParser(qaListPath);
+ }
+
+ @Override
+ public Output act() {
+ Interlocutor person = getContext().ACTIVE_INTERLOCUTOR.getValue();
+ LOGGER.info(" -> Retrieved Interlocutor: " + person.getName());
+
+ return Output.say("What is my purpose?");
+ }
+
+ @Override
+ public Output react(Interpretation input) {
+ nextState = getTransition(TRANSITION_INFO_OBTAINED);
+ return Output.say("Oh. My. God.");
+ }
+
+ @Override
+ public State getNextState() {
+ return nextState;
+ }
+
+ @Override
+ protected Set getRequiredTransitionNames() {
+ // optional: define all required transitions here:
+ return newSet(TRANSITION_INFO_OBTAINED);
+ }
+
+ @Override
+ protected Set getRequiredParameterNames() {
+ return newSet(QA_FILE_PARAMETER_ID);
+ }
+
+ private String InferResult(Interpretation input) {
+ return getInference().inferRelationship(selectedPredicate, input);
+ }
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/devoStates/QuestionRoboyQAState.java b/dialog/src/main/java/roboy/dialog/states/devoStates/QuestionRoboyQAState.java
new file mode 100644
index 00000000..5de2cfd4
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/devoStates/QuestionRoboyQAState.java
@@ -0,0 +1,439 @@
+package roboy.dialog.states.devoStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.context.contextObjects.IntentValue;
+import roboy.dialog.Segue;
+import roboy.dialog.states.definitions.ExpoState;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.dialog.states.ordinaryStates.PersonalInformationFollowUpState;
+import roboy.emotions.RoboyEmotion;
+import roboy.linguistics.Linguistics;
+import roboy.linguistics.Linguistics.SemanticRole;
+import roboy.linguistics.Triple;
+import roboy.linguistics.sentenceanalysis.Interpretation;
+import roboy.logic.StatementInterpreter;
+import roboy.memory.Neo4jRelationship;
+import roboy.memory.Neo4jProperty;
+import roboy.memory.nodes.Interlocutor;
+import roboy.memory.nodes.MemoryNodeModel;
+import roboy.memory.nodes.Roboy;
+import roboy.talk.PhraseCollection;
+import roboy.talk.Verbalizer;
+import roboy.util.Agedater;
+import roboy.util.Pair;
+import roboy.util.QAJsonParser;
+import roboy.util.RandomList;
+import roboy.util.api.*;
+
+import java.util.*;
+
+import static roboy.memory.Neo4jProperty.full_name;
+import static roboy.memory.Neo4jProperty.name;
+import static roboy.memory.Neo4jRelationship.*;
+import static roboy.memory.nodes.Interlocutor.RelationshipAvailability.NONE_AVAILABLE;
+import static roboy.memory.nodes.Interlocutor.RelationshipAvailability.SOME_AVAILABLE;
+
+/**
+ * QuestionAnsweringState
+ * Roboy Question Answering State
+ *
+ * The parser:
+ * - provides triples generated from the question
+ * - adds the answer to the question if there is an answer in DBpedia
+ * - tells a specifying followup question if the interlocutor's question was ambiguous
+ *
+ * This state:
+ * - checks if interlocutor wants to play a game
+ * - returns the answer if provided by the parser
+ * - asks the specifying followup question if provided by the parser
+ * - - if answered with yes --> will use the parser again to get the answer to the original question
+ * - if answered with no --> will use a segue to avoid answer
+ * - tries to query memory if there is no answer to the question
+ * - queries the fallback if memory fails to answer as well
+ *
+ *
+ * QuestionAnsweringState interface:
+ * 1) Fallback is required.
+ * 2) Outgoing transitions that have to be defined:
+ * - finishedQuestionAnswering: following state if this state if finished with answering questions
+ * 3) No parameters are used.
+ */
+public class QuestionRoboyQAState extends ExpoState {
+ private final Logger LOGGER = LogManager.getLogger();
+
+ private final static String TRANSITION_FINISHED_ANSWERING = "finishedQuestionAnswering";
+ private final static String TRANSITION_LOOP_TO_NEW_PERSON = "loopToNewPerson";
+ private final static String TRANSITION_LOOP_TO_KNOWN_PERSON = "loopToKnownPerson";
+ private final static String TRANSITION_SWITCH_TO_GAMING = "switchToGaming";
+ private final static String TRANSITION_STORY = "tellStory";
+ private final static int MAX_NUM_OF_QUESTIONS = 3;
+ private int questionsAnswered = 0;
+
+ private final static RandomList reenteringPhrases = PhraseCollection.QUESTION_ANSWERING_REENTERING;
+ private final static RandomList answerStartingPhrases = PhraseCollection.QUESTION_ANSWERING_START;
+ private final String INFO_FILE_PARAMETER_ID = "infoFile";
+
+ private boolean offeredGame = false;
+ private boolean userWantsGame = false;
+ private boolean offeredStory = false;
+ private boolean userWantsStory = false;
+
+ private QAJsonParser infoValues;
+
+ public QuestionRoboyQAState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ String infoListPath = params.getParameter(INFO_FILE_PARAMETER_ID);
+ LOGGER.info("The infoList path: " + infoListPath);
+ infoValues = new QAJsonParser(infoListPath);
+ }
+
+ @Override
+ public Output act() {
+ if (questionsAnswered >= MAX_NUM_OF_QUESTIONS && !offeredGame) {
+ offeredGame = true;
+ return Output.say(PhraseCollection.OFFER_GAME_PHRASES.getRandomElement());
+ }
+ else
+ offeredGame = false;
+ // if(Math.random() < 0.05) {
+ // offeredStory = true;
+ // return Output.say(Verbalizer.confirmStory.getRandomElement());
+ // }
+ return Output.sayNothing();
+ }
+
+ @Override
+ public Output react(Interpretation input) {
+ return reactToQuestion(input);
+ }
+ private Output reactToQuestion(Interpretation input)
+ {
+ // -- Decide whether to go forward on proposed transitions
+
+ if (offeredGame) {
+ if (input.getSentiment() == Linguistics.UtteranceSentiment.POSITIVE ||
+ input.getSentiment() == Linguistics.UtteranceSentiment.NEUTRAL ||
+ input.getSentiment() == Linguistics.UtteranceSentiment.UNCERTAIN_POS ||
+ input.getSentiment() == Linguistics.UtteranceSentiment.MAYBE
+ ) {
+ userWantsGame = true;
+ questionsAnswered++;
+ return Output.sayNothing().setSegue(new Segue(Segue.SegueType.PICKUP,0.5)).setEmotion(RoboyEmotion.HAPPY);
+ }
+ else {
+ return Output.sayNothing().setEmotion(RoboyEmotion.SADNESS);
+ }
+ }
+
+ if (offeredStory || input.getTokens().contains("story") || input.getTokens().contains("interesting")) {
+ offeredGame = false;
+ if (StatementInterpreter.isFromList(input.getSentence(), Verbalizer.consent)) {
+ questionsAnswered++;
+ userWantsStory = true;
+ return Output.say(Verbalizer.startSomething.getRandomElement());
+ }
+ }
+
+ // -- Reset transition proposals
+
+ userWantsGame = false;
+ userWantsStory = false;
+ questionsAnswered++;
+
+ // -- Try to interpret input as API request
+
+ String answer = inferApiAnswer(input);
+ if (!answer.isEmpty()) {
+ return Output.say(answer);
+ }
+
+ // -- Try to interpret input as Joke request
+
+ if (input.getTokens().contains("joke") || input.getTokens().contains("funny")) {
+ return Output.say(PhraseCollection.JOKES.getRandomElement()).setEmotion(RoboyEmotion.positive.getRandomElement());
+ }
+
+ // -- Try to interpret input as Memory request
+
+ try {
+ if ( input.getPas() != null || input.getTriples() != null) {
+ // try to use memory to answer
+ Roboy roboy = new Roboy(getMemory());
+ answer = inferMemoryAnswer(input, roboy);
+ //String answer = String.format(infoValues.getSuccessAnswers(predicate).getRandomElement(), inferMemoryAnswer(input, roboy));
+ if (!answer.isEmpty()) {
+ return Output.say(answer);
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.warn(e.getMessage());
+ }
+
+ // -- Try to interpret input as parsable question
+
+ Linguistics.ParsingOutcome parseOutcome = input.getParsingOutcome();
+ if (parseOutcome == Linguistics.ParsingOutcome.SUCCESS) {
+ if (input.getAnswer() != null) {
+ // tell the answer, that was provided by the parser
+ return Output.say(answerStartingPhrases.getRandomElement()+" "+input.getAnswer());
+ }
+ }
+
+ return Output.useFallback();
+ }
+
+ @Override
+ public State getNextState() {
+ State nextState = null;
+ if (userWantsGame) {
+ nextState = getTransition(TRANSITION_SWITCH_TO_GAMING);
+ }
+ else if (userWantsStory) {
+ nextState = getTransition(TRANSITION_STORY);
+ }
+ else if (questionsAnswered > MAX_NUM_OF_QUESTIONS) { // enough questions answered --> finish asking
+ nextState = getTransition(TRANSITION_FINISHED_ANSWERING);
+ }
+ if (nextState != null) {
+ userWantsGame = false;
+ userWantsStory = false;
+ questionsAnswered = 0;
+ return nextState;
+ }
+ return this;
+ }
+
+ private String inferApiAnswer(Interpretation input) {
+ Map pas = input.getPas();
+ String answer = "";
+ if (matchPas(pas, new Pair(SemanticRole.PATIENT, ".*\\bweather\\b.*"))) {
+ try {
+ answer = String.format("It seems like it is %s out there!", Weather.getData("munich"));
+ }
+ catch (Exception e) {
+ answer = "It seems a bit moody...";
+ LOGGER.error(e.getMessage());
+ }
+ } else if (matchPas(pas, new Pair(SemanticRole.AGENT, ".*\\bmovie.*"))) {
+ try {
+ answer = String.format("I have heard that %s is playing!", Movie.getData("title"));
+ }
+ catch (Exception e) {
+ answer = "Wall e is a great movie!";
+ LOGGER.error(e.getMessage());
+ }
+ } else if (matchPas(pas, new Pair(SemanticRole.PATIENT, ".+ in .+"), new Pair(SemanticRole.MANNER, "how"), new Pair(SemanticRole.PREDICATE, "say"), new Pair(SemanticRole.AGENT, "you")) ||
+ matchPas(pas, new Pair(SemanticRole.PATIENT, ".+ in .+"), new Pair(SemanticRole.PREDICATE, "is"), new Pair(SemanticRole.AGENT, "what"))) {
+ String[] parts = pas.get(SemanticRole.PATIENT).split(" in ");
+ assert(parts.length == 2);
+ try {
+ answer = answerStartingPhrases.getRandomElement() + " " + Translate.getData(parts[0], parts[1]);
+ }
+ catch (Exception e) {
+ answer = String.format("I am not sure whether I know %s", parts[1]);
+ LOGGER.error(e.getMessage());
+ }
+ }
+ return answer;
+ }
+
+ private String inferMemoryAnswer(Interpretation input, Roboy roboy) {
+
+ String answer = "";
+ Map pas = input.getPas();
+ List triples = input.getTriples();
+
+ if (pas != null) {
+ answer = inferPasAnswer(pas, roboy);
+ if (!answer.isEmpty()) {
+ return answer;
+ }
+ }
+
+ if (triples != null) {
+ return inferTripleAnswer(triples, roboy);
+ }
+
+ if (input.getObjAnswer() != null) {
+ String objAnswer = input.getObjAnswer().toLowerCase();
+ if (!objAnswer.equals("")) {
+ LOGGER.info("OBJ_ANSWER: " + objAnswer);
+ Neo4jRelationship predicate = inferPredicateFromObjectAnswer(objAnswer);
+ if (predicate != null) {
+ answer = extractNodeNameForPredicate(predicate, roboy);
+ }
+ } else {
+ LOGGER.warn("OBJ_ANSWER is empty");
+ }
+ }
+
+ return answer;
+ }
+
+ private String inferPasAnswer(Map pas, Roboy roboy) {
+ String answer = "";
+ if (matchPas(pas, new Pair(SemanticRole.AGENT, "old")) || matchPas(pas, new Pair(SemanticRole.PATIENT, ".*\\bage\\b.*"))) {
+ answer = extractAge(roboy);
+ } else if (matchPas(pas, new Pair(SemanticRole.PREDICATE, "from"))) {
+ answer = extractNodeNameForPredicate(Neo4jRelationship.FROM, roboy);
+ }
+ else if (matchPas(pas, new Pair(SemanticRole.LOCATION, "where"), new Pair(SemanticRole.PREDICATE, "live"))) {
+ answer = extractNodeNameForPredicate(Neo4jRelationship.LIVE_IN, roboy);
+ }
+ else if (matchPas(pas, new Pair(SemanticRole.AGENT, "you"), new Pair(SemanticRole.MANNER, "how"))) {
+ answer = "I am really happy!";
+ }
+ else if (matchPas(pas, new Pair(SemanticRole.AGENT, "who"))) {
+ if (matchPas(pas, new Pair(SemanticRole.PATIENT, "you"), new Pair(SemanticRole.PREDICATE, "are"))) {
+ answer = extractNodeNameForPredicate(Neo4jProperty.name, roboy) + " "
+ + extractNodeNameForPredicate(Neo4jProperty.identitiy, roboy);
+ } else if (matchPas(pas, new Pair(SemanticRole.PATIENT, ".*\\b(father|dad)\\b.*"))) {
+ answer = extractNodeNameForPredicate(Neo4jRelationship.CHILD_OF, roboy);
+ } else if (matchPas(pas, new Pair(SemanticRole.PATIENT, ".*\\b(sibling|brother)\\b.*"))){
+ answer = extractNodeNameForPredicate(Neo4jRelationship.SIBLING_OF, roboy);
+ } else if (matchPas(pas, new Pair(SemanticRole.PREDICATE, "created|made|built"), new Pair(SemanticRole.PATIENT, "you"))) {
+ answer = extractNodeNameForPredicate(Neo4jRelationship.CREATED_BY, roboy);
+ }
+ } else if (matchPas(pas, new Pair(SemanticRole.PREDICATE, "do|like"), new Pair(SemanticRole.AGENT, "you"))){
+// double prob = Math.random();
+// if (prob < .3) {
+// answer = extractNodeNameForPredicate(Neo4jProperty.abilities, roboy);
+// } else if(prob < .7) {
+// answer = extractNodeNameForPredicate(Neo4jRelationship.HAS_HOBBY, roboy);
+// } else {
+ answer = extractNodeNameForPredicate(Neo4jProperty.skills, roboy);
+// }
+ } else if (matchPas(pas, new Pair(SemanticRole.PREDICATE, "dream|wish"), new Pair(SemanticRole.AGENT, "you"))) {
+ answer = extractNodeNameForPredicate(Neo4jProperty.dreams, roboy);
+ }
+ else if (matchPas(pas, new Pair(SemanticRole.PATIENT, "you"), new Pair(SemanticRole.PREDICATE, "are"),
+ new Pair(SemanticRole.AGENT, "what"))) {
+ answer = extractNodeNameForPredicate(Neo4jProperty.name, roboy) + " "
+ + extractNodeNameForPredicate(Neo4jProperty.identitiy, roboy);
+ }
+
+ return answer;
+ }
+
+ private String inferTripleAnswer(List triples, Roboy roboy) {
+ String answer = "";
+ for (Triple t: triples) {
+ if (t.subject.matches(".*\\b(meet|see|know)\\b.*")) {
+ answer = infoValues.getSuccessAnswers(Neo4jProperty.media).getRandomElement();
+ }
+ }
+ // else if {OBJ: *} -> query * -> I'm sure I know a typeof(*) called *! (Where does he live? :))
+ //
+ // if * in Neo4jRelationship.FRIEND_OF
+ return answer;
+ }
+
+ private Neo4jRelationship inferPredicateFromObjectAnswer(String objectAnswer) {
+ if (objectAnswer.contains("hobby")) {
+ return Neo4jRelationship.HAS_HOBBY;
+ } else if (objectAnswer.contains("member")) {
+ return Neo4jRelationship.MEMBER_OF;
+ } else if (objectAnswer.contains("friend")) {
+ return Neo4jRelationship.FRIEND_OF;
+ } else if (objectAnswer.contains("where") ||
+ objectAnswer.contains("city") ||
+ objectAnswer.contains("place") ||
+ objectAnswer.contains("country") ||
+ objectAnswer.contains("live") ||
+ objectAnswer.contains("life")) {
+ return Neo4jRelationship.LIVE_IN;
+ } else if (objectAnswer.contains("from") ||
+ objectAnswer.contains("born")) {
+ return Neo4jRelationship.FROM;
+ } else if (objectAnswer.contains("child") ||
+ objectAnswer.contains("father") ||
+ objectAnswer.contains("dad") ||
+ objectAnswer.contains("parent")) {
+ return Neo4jRelationship.CHILD_OF;
+ } else if (objectAnswer.contains("brother") ||
+ objectAnswer.contains("family") ||
+ objectAnswer.contains("relativ")) {
+ return Neo4jRelationship.SIBLING_OF;
+ }
+ return null;
+ }
+
+ private String extractNodeNameForPredicate(Neo4jRelationship predicate, Roboy roboy) {
+ MemoryNodeModel node = getMemNodesByIds(roboy.getRelationships(predicate)).getRandomElement();
+ if (node != null) {
+ String nodeName;
+ if (node.getProperties().containsKey(full_name) && !node.getProperties().get(full_name).equals("")) {
+ nodeName = node.getProperties().get(full_name).toString();
+ } else {
+ nodeName = node.getProperties().get(name).toString();
+ }
+ return String.format(infoValues.getSuccessAnswers(predicate).getRandomElement(), nodeName);
+ }
+ return null;
+ }
+
+ private String extractNodeNameForPredicate(Neo4jProperty predicate, Roboy roboy) {
+ String property = roboy.getProperty(predicate).toString();
+ if (property != null) {
+ // check if there are multiple things in it
+ RandomList properties = new RandomList<>(property.split(","));
+ //pick 2 random properties to talk about
+ String propertiesToShare = properties.getRandomElement();
+ properties.remove(propertiesToShare); // so we dont pick the same one again
+ if (!properties.isEmpty()) {
+ propertiesToShare += " and " + properties.getRandomElement();
+ }
+ return String.format(infoValues.getSuccessAnswers(predicate).getRandomElement(), propertiesToShare);
+ }
+ return null;
+ }
+
+ private String extractAge(Roboy roboy) {
+ HashMap ages = new Agedater().determineAge(roboy.getProperty(Neo4jProperty.birthdate).toString());
+ String retrievedAge = "0 days";
+ if (ages.get("years") > 0) {
+ retrievedAge = ages.get("years") + " years";
+ } else if (ages.get("months") > 0) {
+ retrievedAge = ages.get("months") + " months";
+ } else {
+ retrievedAge = ages.get("days") + " days";
+ }
+ return String.format(infoValues.getSuccessAnswers(Neo4jProperty.age).getRandomElement(), retrievedAge);
+ }
+
+ private boolean matchPas(Map pas, Pair... matchCriteria) {
+ if (pas == null)
+ return false;
+ boolean allCriteriaSatisfied = true;
+ for (Pair criterion : matchCriteria) {
+ if (!pas.containsKey(criterion.getKey()) ||
+ !pas.get(criterion.getKey()).matches("(?i)"+criterion.getValue())) { // (?i)-> case insesitive
+ allCriteriaSatisfied = false;
+ break;
+ }
+ }
+ return allCriteriaSatisfied;
+ }
+
+ @Override
+ protected Set getRequiredTransitionNames() {
+ return newSet(TRANSITION_FINISHED_ANSWERING, TRANSITION_LOOP_TO_NEW_PERSON, TRANSITION_LOOP_TO_KNOWN_PERSON);
+ }
+
+ @Override
+ public boolean isFallbackRequired() {
+ return true;
+ }
+
+ private boolean isIntentsHistoryComplete(Neo4jRelationship[] predicates) {
+ boolean isComplete = true;
+ for (Neo4jRelationship predicate : predicates) {
+ if (!getContext().DIALOG_INTENTS.contains(new IntentValue(PersonalInformationFollowUpState.INTENTS_HISTORY_ID, predicate))) {
+ isComplete = false;
+ }
+ }
+ return isComplete;
+ }
+}
\ No newline at end of file
diff --git a/dialog/src/main/java/roboy/dialog/states/eventStates/PartnerState.java b/dialog/src/main/java/roboy/dialog/states/eventStates/PartnerState.java
new file mode 100644
index 00000000..807c7202
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/eventStates/PartnerState.java
@@ -0,0 +1,89 @@
+package roboy.dialog.states.eventStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.linguistics.sentenceanalysis.Interpretation;
+import roboy.logic.StatementInterpreter;
+import roboy.talk.Verbalizer;
+import roboy.util.QAJsonParser;
+import roboy.util.RandomList;
+
+import java.util.Set;
+
+/**
+ * Personal Information Asking State
+ *
+ * Sugar for Wacker
+ */
+public class PartnerState extends State {
+ private QAJsonParser qaValues;
+ private RandomList intents;
+ private String selectedPredicate;
+ private State nextState;
+
+ private final String NEXT_STATE = "nextState";
+ private final String QA_FILE_PARAMETER_ID = "qaFile";
+ final Logger LOGGER = LogManager.getLogger();
+
+ public final static String INTENTS_HISTORY_ID = "WK";
+
+ // we have to track question's index of the predicate OTHER, since the answer's order matters
+ private int currentIdx = 0;
+
+ public PartnerState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ String qaListPath = params.getParameter(QA_FILE_PARAMETER_ID);
+ LOGGER.info(" -> The QAList path: " + qaListPath);
+ qaValues = new QAJsonParser(qaListPath);
+ intents = qaValues.getIntents();
+ }
+
+ @Override
+ public Output act() {
+
+ String question;
+ selectedPredicate = intents.getRandomElement();
+ RandomList questions = qaValues.getQuestions(selectedPredicate);
+
+ if (questions != null && !questions.isEmpty()) {
+ question = questions.getRandomElement();
+ currentIdx = questions.indexOf(question);
+ } else {
+ LOGGER.error(" -> The list of " + selectedPredicate + " questions is empty or null");
+ return Output.sayNothing();
+ }
+
+ return Output.say(question);
+
+ }
+
+ @Override
+ public Output react(Interpretation input) {
+ if(StatementInterpreter.isFromList(input.getSentence(),Verbalizer.consent)) {
+ return Output.say(qaValues.getSuccessAnswers(selectedPredicate).getRandomElement());
+ }
+ if(StatementInterpreter.isFromList(input.getSentence(),Verbalizer.denial)) {
+ return Output.say(qaValues.getFailureAnswers(selectedPredicate).getRandomElement());
+ }
+ return Output.useFallback();
+ }
+
+ @Override
+ public State getNextState() {
+ return nextState;
+ }
+
+ @Override
+ protected Set getRequiredTransitionNames() {
+ // optional: define all required transitions here:
+ return newSet(NEXT_STATE);
+ }
+
+ @Override
+ protected Set getRequiredParameterNames() {
+ return newSet(QA_FILE_PARAMETER_ID);
+ }
+
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/eventStates/StoryTellingState.java b/dialog/src/main/java/roboy/dialog/states/eventStates/StoryTellingState.java
new file mode 100644
index 00000000..98bbf1d0
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/eventStates/StoryTellingState.java
@@ -0,0 +1,103 @@
+package roboy.dialog.states.eventStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.yecht.ruby.Out;
+import roboy.dialog.Segue;
+import roboy.dialog.states.definitions.MonologState;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.emotions.RoboyEmotion;
+import roboy.linguistics.Linguistics;
+import roboy.linguistics.sentenceanalysis.Interpretation;
+import roboy.logic.StatementInterpreter;
+import roboy.talk.Verbalizer;
+
+import java.io.IOException;
+import java.util.*;
+
+import static roboy.util.UzupisIntents.*;
+
+
+/**
+ * A class that will replay a pre-recorded story as a sound file
+ */
+public class StoryTellingState extends State {
+
+ private final Logger logger = LogManager.getLogger();
+ private final String STORYFILES = "stories";
+
+ // key = name, value = path
+ private final HashMap stories = new HashMap<>();
+ private boolean askForFeedback = false;
+ Map.Entry storyToTell;
+
+ public StoryTellingState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ String storiesRaw = params.getParameter(STORYFILES);
+ String[] storiesArray = storiesRaw.split(";");
+ for (int i=0; i Available sounds: " + stories.keySet());
+ }
+
+ @Override
+ public Output act() {
+ if (askForFeedback)
+ return Output.say(Verbalizer.askForFeedback.getRandomElement());
+ // TODO ask what story if there're many
+ if (stories.isEmpty()) {
+ logger.error("StoryTellingState: no stories to tell");
+ return Output.useFallback();
+ }
+ storyToTell = stories.entrySet().iterator().next();
+ String phrase = "The story is called " + storyToTell.getKey() + ". Are you ready?";
+ return Output.say(Verbalizer.startSomething.getRandomElement() + phrase);
+
+ }
+
+ @Override
+ public Output react(Interpretation input) {
+ if(askForFeedback) {
+ askForFeedback = false;
+ if (input.getSentiment()!=Linguistics.UtteranceSentiment.NEGATIVE) {
+ return Output.say(Verbalizer.takePositiveFeedback.getRandomElement())
+ .setEmotion(RoboyEmotion.positive.getRandomElement());
+ }
+ else {
+ return Output.say(Verbalizer.takeNegativeFeedback.getRandomElement());
+ }
+ }
+ if(inferStoryWanted(input)) {
+ askForFeedback = true;
+ getRosMainNode().PlaySoundFile(storyToTell.getValue());
+ return Output.say("That was it, you guys!");//.addSound(storyToTell.getValue());
+ }
+
+ return Output.say("oh well, next time then.");
+ }
+
+ private boolean inferStoryWanted(Interpretation input) {
+ if (StatementInterpreter.isFromList(input.getSentence(), Verbalizer.denial)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public State getNextState() {
+
+ if (askForFeedback) return this;
+ return getTransition("next");
+
+ }
+
+ @Override
+ protected Set getRequiredParameterNames() {
+ return newSet(STORYFILES);
+ }
+
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/eventStates/UzupisState.java b/dialog/src/main/java/roboy/dialog/states/eventStates/UzupisState.java
index 1014a828..e0000683 100644
--- a/dialog/src/main/java/roboy/dialog/states/eventStates/UzupisState.java
+++ b/dialog/src/main/java/roboy/dialog/states/eventStates/UzupisState.java
@@ -8,8 +8,8 @@
import roboy.dialog.states.definitions.StateParameters;
import roboy.linguistics.sentenceanalysis.Interpretation;
import roboy.memory.nodes.Interlocutor;
-import roboy.util.QAFileParser;
import roboy.util.QAJsonParser;
+import roboy.util.RandomList;
import roboy.util.UzupisIntents;
import java.io.IOException;
@@ -32,9 +32,9 @@ public class UzupisState extends State {
private final int toAskCounter = UzupisIntents.values().length;
private UzupisIntents currentIntent;
- private Map> questions;
- private Map> successAnswers;
- private Map> failureAnswers;
+ private Map> questions;
+ private Map> successAnswers;
+ private Map> failureAnswers;
private String CertificatesGeneratorScript;
@@ -44,7 +44,7 @@ public UzupisState(String stateIdentifier, StateParameters params) {
super(stateIdentifier, params);
String qaListPath = params.getParameter(QAFILEPATH);
logger.info(" -> The qa list path: " + qaListPath);
- QAFileParser parser = new QAFileParser(qaListPath);
+ QAJsonParser parser = new QAJsonParser(qaListPath);
CertificatesGeneratorScript = params.getParameter(CERTIFICATESGENERATOR);
diff --git a/dialog/src/main/java/roboy/dialog/states/expoStates/RoboyQAState.java b/dialog/src/main/java/roboy/dialog/states/expoStates/RoboyQAState.java
index d6820cd9..f4f28d11 100644
--- a/dialog/src/main/java/roboy/dialog/states/expoStates/RoboyQAState.java
+++ b/dialog/src/main/java/roboy/dialog/states/expoStates/RoboyQAState.java
@@ -2,7 +2,6 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import roboy.context.Context;
import roboy.dialog.states.definitions.State;
import roboy.dialog.states.definitions.StateParameters;
import roboy.dialog.states.definitions.ExpoState;
diff --git a/dialog/src/main/java/roboy/dialog/states/fairShowStates/ActiveIntroState.java b/dialog/src/main/java/roboy/dialog/states/fairShowStates/ActiveIntroState.java
new file mode 100644
index 00000000..ac6abc54
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/fairShowStates/ActiveIntroState.java
@@ -0,0 +1,63 @@
+package roboy.dialog.states.fairShowStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.states.definitions.MonologState;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.talk.PhraseCollection;
+import roboy.talk.Verbalizer;
+
+
+
+/**
+ * Active state to start a conversation.
+ * Roboy is introducing himself autonomously
+ *
+ */
+public class ActiveIntroState extends MonologState {
+
+ private final static String TRANSITION_PEOPLE_AROUND = "peopleAround";
+ private final static String TRANSITION_LONELY_ROBOY = "lonelyRoboy";
+ private final int MIN_NUMBER_PEOPLE = 1;
+
+ private final Logger logger = LogManager.getLogger();
+
+ private State nextState = this;
+
+ public ActiveIntroState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ }
+
+ @Override
+ public Output act() {
+
+ return Output.say(Verbalizer.greetings.getRandomElement() + " " + Verbalizer.roboyIntro.getRandomElement() + PhraseCollection.ROBOY_PHRASES.getRandomElement());
+ }
+
+ @Override
+ public State getNextState() {
+
+ if(checkPplAround()){
+ nextState = getTransition(TRANSITION_PEOPLE_AROUND);
+ }else{
+ nextState = getTransition((TRANSITION_LONELY_ROBOY));
+ }
+
+ return nextState;
+ }
+
+
+ private boolean checkPplAround(){
+
+ try {
+ logger.info("People around: " + getContext().CROWD_DETECTION.getLastValue().getData());
+ return getContext().CROWD_DETECTION.getLastValue().getData() >= MIN_NUMBER_PEOPLE;
+
+ } catch(NullPointerException e){
+ logger.info("Make sure crowd detection publishing, receiving: " + e.getMessage());
+ return false;
+ }
+ }
+
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/fairShowStates/ChooseInteractiveTalkState.java b/dialog/src/main/java/roboy/dialog/states/fairShowStates/ChooseInteractiveTalkState.java
new file mode 100644
index 00000000..54b00103
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/fairShowStates/ChooseInteractiveTalkState.java
@@ -0,0 +1,88 @@
+package roboy.dialog.states.fairShowStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.states.definitions.MonologState;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.util.RandomList;
+
+import java.util.Arrays;
+
+/**
+ * State to randomly select the next interactive state
+ * This State will
+ * - first switch state to get to know the person if he/she is unknown
+ * - check for detected objects from vision -> switch state to talk about these
+ * - switch state to play games, calculate mathematical stuff or do question answering
+ */
+public class ChooseInteractiveTalkState extends MonologState {
+
+ private final static String TRANSITION_NEW_PERSON = "newPerson";
+ private final static String TRANSITION_MATH = "math";
+ private final static String TRANSITION_GAME = "game";
+ private final static String TRANSITION_PERSONAL_QA = "personalQA";
+ private final static String TRANSITION_OBJECT_DETECTION = "objectDetected";
+ private final static String TRANSITION_GENERAL_QA = "generalQA";
+
+ private final Logger logger = LogManager.getLogger();
+
+ private State nextState = this;
+
+ private final RandomList availableInteractions = new RandomList<>();
+ private String nextInteraction;
+
+
+ public ChooseInteractiveTalkState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ resetAvailableInteractions();
+ }
+
+ @Override
+ public Output act() {
+
+ if(getContext().ACTIVE_INTERLOCUTOR.getValue().getName() == null){
+
+ nextInteraction = TRANSITION_NEW_PERSON;
+
+ } else {
+
+ nextInteraction = selectRandomInteraction();
+
+ }
+ nextState = getTransition(nextInteraction);
+
+ return Output.sayNothing();
+ }
+
+
+ @Override
+ public State getNextState() {
+ return nextState;
+ }
+
+ /**
+ * Resets the list of available interactions so that it contains all of them.
+ */
+ private void resetAvailableInteractions() {
+ availableInteractions.clear();
+ availableInteractions.addAll(Arrays.asList(TRANSITION_PERSONAL_QA, TRANSITION_MATH, TRANSITION_GAME, TRANSITION_OBJECT_DETECTION, TRANSITION_GENERAL_QA));
+ }
+
+ /**
+ * Selects one of the interactions from the availableInteractions list at random and removes it from the list.
+ * If the list becomes empty this way, resets it to the initial state
+ * @return one of the available interactions
+ */
+ private String selectRandomInteraction() {
+ String interaction = availableInteractions.getRandomElement();
+ availableInteractions.remove(interaction);
+ if (availableInteractions.size() == 0) {
+ resetAvailableInteractions(); // reset if all infos were used
+ logger.info("all interactions were selected at least once, resetting the list");
+ }
+ return interaction;
+ }
+
+
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/fairShowStates/DemoIdleState.java b/dialog/src/main/java/roboy/dialog/states/fairShowStates/DemoIdleState.java
new file mode 100644
index 00000000..0207adf3
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/fairShowStates/DemoIdleState.java
@@ -0,0 +1,84 @@
+package roboy.dialog.states.fairShowStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.states.definitions.MonologState;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.emotions.RoboyEmotion;
+import roboy.talk.PhraseCollection;
+import roboy.talk.Verbalizer;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Idle state.
+ * Roboy is waiting until he sees some person to autonomously start a conversation.
+ *
+ */
+public class DemoIdleState extends MonologState {
+
+ private final static String TRANSITION_PERSON_DETECTED = "personDetected";
+
+ private final static String SHOW_TIME_ID = "showTimeinMins";
+ private final Logger LOGGER = LogManager.getLogger();
+
+ private State nextState = this;
+
+ private long showTime;
+ private long startTime;
+
+ public DemoIdleState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ showTime = Long.parseLong(params.getParameter(SHOW_TIME_ID));
+ LOGGER.info("--> The show takes: " + showTime + " mins.");
+ startTime = System.nanoTime();
+ }
+
+ @Override
+ public Output act() {
+
+ if(TimeUnit.MINUTES.toNanos(showTime) < System.nanoTime() - startTime){
+ LOGGER.info("Closing Conversation after " + showTime + " minutes");
+ return Output.endConversation();
+ }
+
+ while(notInVision()){
+ try {
+ TimeUnit.SECONDS.sleep(1);
+ } catch (InterruptedException e) {
+ LOGGER.error(e.getMessage());
+ }
+ }
+
+ nextState = getTransition(TRANSITION_PERSON_DETECTED);
+
+ try{
+ getRosMainNode().ShowEmotion(RoboyEmotion.HAPPY);
+ TimeUnit.SECONDS.sleep(1);
+ } catch (InterruptedException e){
+ LOGGER.error(e.getMessage());
+ }
+
+ return Output.say(Verbalizer.greetings.getRandomElement() + " " + Verbalizer.roboyIntro.getRandomElement() + PhraseCollection.ROBOY_PHRASES.getRandomElement());
+ }
+
+ @Override
+ public State getNextState() {
+ return nextState;
+ }
+
+ private boolean notInVision(){
+
+ try {
+
+ return !getContext().PERSON_DETECTION.getLastValue().getData();
+ } catch(NullPointerException e){
+ LOGGER.info("Make sure person listening is publishing, receiving: " + e.getMessage());
+ return true;
+ }
+ }
+
+}
+
+
diff --git a/dialog/src/main/java/roboy/dialog/states/fairShowStates/DemoQuestionAnsweringState.java b/dialog/src/main/java/roboy/dialog/states/fairShowStates/DemoQuestionAnsweringState.java
new file mode 100644
index 00000000..3a27dd07
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/fairShowStates/DemoQuestionAnsweringState.java
@@ -0,0 +1,231 @@
+package roboy.dialog.states.fairShowStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.Segue;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.linguistics.Linguistics;
+import roboy.linguistics.Triple;
+import roboy.linguistics.sentenceanalysis.Interpretation;
+import roboy.memory.Neo4jRelationship;
+import roboy.memory.nodes.MemoryNodeModel;
+import roboy.memory.nodes.Roboy;
+import roboy.talk.PhraseCollection;
+import roboy.talk.Verbalizer;
+import roboy.util.RandomList;
+
+import java.util.List;
+
+
+/**
+ * Simple question answering state
+ */
+public class DemoQuestionAnsweringState extends State {
+
+ private final static String TRANSITION_LONELY_ROBOY = "lonelyRoboy";
+ private final static String TRANSITION_FINISHED = "finished";
+ private final static int MAX_NUM_OF_QUESTIONS = 3;
+
+ private final Logger LOGGER = LogManager.getLogger();
+
+ private State nextState = this;
+ private int questionsAnswered = 0;
+ private boolean askingSpecifyingQuestion = false;
+ private String answerAfterUnspecifiedQuestion = "";
+
+ public DemoQuestionAnsweringState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ }
+
+ @Override
+ public Output act() {
+
+ if (questionsAnswered > 0) {
+ return Output.say(PhraseCollection.QUESTION_ANSWERING_REENTERING.getRandomElement());
+ }
+
+ return Output.say("I'm pretty good at answering questions about myself and other stuff. What would you like to know?");
+
+ }
+
+ @Override
+ public Output react(Interpretation input){
+
+ if (askingSpecifyingQuestion) {
+
+ askingSpecifyingQuestion = false;
+ return reactToSpecifyingAnswer(input);
+
+ } else{
+
+ return reactToQuestion(input);
+ }
+
+ }
+
+ @Override
+ public State getNextState() {
+
+ if(questionsAnswered > MAX_NUM_OF_QUESTIONS){
+
+ nextState = getTransition(TRANSITION_FINISHED);
+ } else {
+
+ nextState = this;
+ //if (askingSpecifyingQuestion) { // we are asking a yes/no question --> stay in this state
+
+ // nextState = this;
+ //}
+ }
+
+ return nextState;
+ }
+
+
+ /**
+ * React to answer of the specifying question asked previously.
+ *
+ * @param input something like "yes" or "no"
+ * @return answer to the answer to the original question if specifying question was answered with 'yes'
+ */
+ private Output reactToSpecifyingAnswer(Interpretation input) {
+
+ askingSpecifyingQuestion = false;
+
+ // check if answer is yes
+ if (input.getSentence() != null && input.getSentence().contains("yes")) {
+ if (answerAfterUnspecifiedQuestion == null) {
+ // parser could parse the question but did't provide an answer
+ return Output.say("Not sure about the answer, " +
+ "but at least my amazing parser could understand you! ")
+ .setSegue(new Segue(Segue.SegueType.FLATTERY, 0.4));
+ } else {
+ // tell the response previously cached in answerAfterUnspecifiedQuestion
+ return Output.say("In this case, " + PhraseCollection.QUESTION_ANSWERING_START.getRandomElement() + answerAfterUnspecifiedQuestion);
+ }
+
+ } else {
+ // the answer is no. we don't ask more specifying questions
+ // use avoid answer segue
+ return Output.sayNothing().setSegue(new Segue(Segue.SegueType.AVOID_ANSWER, 1));
+ }
+ }
+
+
+ private Output reactToQuestion(Interpretation input) {
+
+ askingSpecifyingQuestion = false;
+ questionsAnswered++;
+
+ Linguistics.ParsingOutcome parseOutcome = input.getParsingOutcome();
+ if (parseOutcome == null) {
+ LOGGER.error("Invalid parser outcome!");
+ return Output.say("Invalid parser outcome!");
+ }
+
+ if (parseOutcome == Linguistics.ParsingOutcome.UNDERSPECIFIED) {
+
+ // ambiguous question, luckily the parser has prepared a followup question
+ // and maybe even an answer if we are lucky (we will check in reactToSpecifyingAnswer later)
+
+ String question = input.getUnderspecifiedQuestion();
+ answerAfterUnspecifiedQuestion = input.getAnswer(); // could be null, but that's fine for now
+
+ askingSpecifyingQuestion = true; // next input should be a yes/no answer
+ return Output.say("Could you be more precise, please? " + question);
+ }
+
+ if (parseOutcome == Linguistics.ParsingOutcome.SUCCESS) {
+ if (input.getAnswer() != null) {
+ // tell the answer, that was provided by the parser
+ return Output.say(PhraseCollection.QUESTION_ANSWERING_START.getRandomElement() + " " + input.getAnswer());
+
+ } else {
+ // check for triple
+
+
+ // parser could parse the question but has no answer
+ return useMemoryOrFallback(input);
+ }
+ }
+
+ // from here we know that dummyParserResult.equals("FAILURE")
+ return useMemoryOrFallback(input);
+ }
+
+ private Output useMemoryOrFallback(Interpretation input) {
+ try {
+ if (input.getSemTriples() != null) {
+ Output memoryAnswer = answerFromMemory(input.getSemTriples());
+ if (memoryAnswer != null) return memoryAnswer;
+ }
+ } catch (Exception e) {
+ LOGGER.warn(e.getMessage());
+ }
+
+ return Output.useFallback();
+ }
+
+ private Output answerFromMemory(List triples) {
+
+ // try to use memory to answer
+ Roboy roboy = new Roboy(getMemory());
+
+ if (triples.size() == 0) {
+ return null;
+ }
+
+ String answer = "I like " + inferMemoryAnswer(triples, roboy) + "humans. ";
+ return Output.say(answer);
+ }
+
+ private String inferMemoryAnswer(List triples, Roboy roboy) {
+ String answer = "";
+ for (Triple result : triples) {
+
+ if (result.predicate != null) {
+ if (result.predicate.contains(Neo4jRelationship.HAS_HOBBY.type)) {
+ RandomList nodes = getMemNodesByIds(roboy.getRelationships(Neo4jRelationship.HAS_HOBBY));
+ if (!nodes.isEmpty()) {
+ for (MemoryNodeModel node : nodes) {
+ answer += node.getProperties().get("name").toString() + " and ";
+ }
+ }
+ break;
+
+ } else if (result.predicate.contains(Neo4jRelationship.FRIEND_OF.type)) {
+ answer += "my friends ";
+ RandomList nodes = getMemNodesByIds(roboy.getRelationships(Neo4jRelationship.FRIEND_OF));
+ if (!nodes.isEmpty()) {
+ for (MemoryNodeModel node : nodes) {
+ answer += node.getProperties().get("name").toString() + " and ";
+ }
+ }
+ answer += " other ";
+ break;
+ }
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public boolean isFallbackRequired() {
+ return true;
+ }
+
+ private boolean checkPersonListening(){
+
+ try {
+
+ return getContext().PERSON_DETECTION.getLastValue().getData();
+
+ } catch(NullPointerException e){
+ LOGGER.info("Make sure person listening is publishing, receiving: " + e.getMessage());
+ return false;
+ }
+ }
+
+
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/fairShowStates/IdleState.java b/dialog/src/main/java/roboy/dialog/states/fairShowStates/IdleState.java
new file mode 100644
index 00000000..a20e1066
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/fairShowStates/IdleState.java
@@ -0,0 +1,82 @@
+package roboy.dialog.states.fairShowStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.states.definitions.MonologState;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Idle state.
+ * Roboy is waiting minutes to autonomously start a conversation.
+ *
+ */
+public class IdleState extends MonologState {
+
+ private final static String TRANSITION_TIME_IS_UP = "timeIsUp";
+ private final static String DELAY_ID = "delayInMins";
+ private final static String SHOW_TIME_ID = "showTimeinMins";
+ private final static int MIN_NUMBER_PEOPLE = 1;
+ private final Logger LOGGER = LogManager.getLogger();
+
+ private State nextState = this;
+ private long delay;
+ private long showTime;
+ private long startTime;
+
+ public IdleState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ delay = Long.parseLong(params.getParameter(DELAY_ID));
+ showTime = Long.parseLong(params.getParameter(SHOW_TIME_ID));
+ LOGGER.info("--> Timer: " + delay + " mins in Idle-State.");
+ LOGGER.info("--> Show takes: " + showTime + " mins.");
+ startTime = System.nanoTime();
+ }
+
+ @Override
+ public Output act() {
+
+ if(TimeUnit.MINUTES.toNanos(showTime) < System.nanoTime() - startTime){
+ LOGGER.info("Closing Conversation");
+ return Output.endConversation();
+ }
+
+ LOGGER.info("Starting Idle State, waiting " + delay + " Minute(s) until next Interaction!");
+
+ long enteringTime = System.nanoTime();
+
+ //while(notInVision() && (TimeUnit.MINUTES.toNanos(delay) > System.nanoTime() - enteringTime))
+ while(TimeUnit.MINUTES.toNanos(delay) > System.nanoTime() - enteringTime){
+ try {
+ TimeUnit.SECONDS.sleep(1);
+ } catch (InterruptedException e) {
+ LOGGER.error(e.getMessage());
+ }
+ }
+
+ nextState = getTransition(TRANSITION_TIME_IS_UP);
+
+ return Output.sayNothing();
+ }
+
+ @Override
+ public State getNextState() {
+ return nextState;
+ }
+
+ private boolean notInVision(){
+
+ try {
+
+ return getContext().CROWD_DETECTION.getLastValue().getData() < MIN_NUMBER_PEOPLE;
+ } catch(NullPointerException e){
+ LOGGER.info("Make sure crowd detection is publishing, receiving: " + e.getMessage());
+ return true;
+ }
+ }
+
+}
+
+
diff --git a/dialog/src/main/java/roboy/dialog/states/fairShowStates/InfoTalkState.java b/dialog/src/main/java/roboy/dialog/states/fairShowStates/InfoTalkState.java
new file mode 100644
index 00000000..37c4f5a1
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/fairShowStates/InfoTalkState.java
@@ -0,0 +1,176 @@
+package roboy.dialog.states.fairShowStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.states.definitions.MonologState;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.emotions.RoboyEmotion;
+import roboy.talk.PhraseCollection;
+import roboy.util.RandomList;
+
+import java.util.*;
+
+
+
+enum InfoAbout {
+
+ ABOUT_TEAM,
+ ABOUT_BOOTH,
+ ABOUT_ROBOY,
+ ABOUT_MOVEMENT,
+ ABOUT_EMOTIONS
+}
+
+/**
+ * Roboy is talking about several topics autonomously
+ * - team
+ * - mission
+ * - movement
+ * - emotions
+ */
+public class InfoTalkState extends MonologState {
+
+ private final static String TRANSITION_PERSON_DETECTED = "personDetected";
+ private final static String TRANSITION_LONELY_ROBOY = "lonelyRoboy";
+
+ private final RandomList availableInformation = new RandomList<>();
+ private InfoAbout activeInfo;
+
+ private RandomList phrases = new RandomList<>();
+
+ private final Logger logger = LogManager.getLogger();
+
+ private State nextState = this;
+
+ private Random random = new Random();
+ private RandomList emotions = new RandomList<>();
+
+ public InfoTalkState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ resetAvailableInformation();
+ }
+
+ @Override
+ public Output act() {
+
+ activeInfo = selectRandomInfo();
+
+ switch(activeInfo){
+ case ABOUT_TEAM:
+
+ phrases = PhraseCollection.ROBOY_TEAM_PHRASES;
+ return Output.say(phrases.getRandomElement());
+
+ case ABOUT_BOOTH:
+
+ String boothSentence = "Look at this awesome booth here! Isn't it amazing what is already possible in technology? Come an talk to one of the guy and get more into the topic!";
+
+ try{
+
+ boothSentence = getContext().BOOTH_SENTENCE.getLastValue().toString();
+
+ } catch (NullPointerException e){
+
+ logger.info("No individual Booth Sentence received, make sure it is published " + e.getMessage());
+
+ }
+
+ return Output.say(boothSentence);
+
+ case ABOUT_ROBOY:
+
+ phrases = PhraseCollection.MISSION_PHRASES;
+ return Output.say(phrases.getRandomElement());
+
+ case ABOUT_MOVEMENT:
+
+
+ phrases = PhraseCollection.MOVEMENT_PHRASES;
+
+ HashMap parts = new HashMap<>();
+ parts.put("shoulder_left", "left shoulder");
+ parts.put("shoulder_right", "right shoulder");
+ parts.put("spine_right", "right leg");
+ parts.put("spine_left", "left leg");
+
+ List bodyParts = new ArrayList<>(parts.keySet());
+
+ String randomPart = bodyParts.get(random.nextInt(bodyParts.size()));
+ String randomPartLiteral = parts.get(randomPart);
+
+// try {
+ // rosNode.PerformMovement(randomPart, "random1");
+//// } catch (InterruptedException e) {
+
+ // logger.error(e.getMessage());
+ // }
+
+ return State.Output.say(String.format(phrases.getRandomElement(), randomPartLiteral));
+
+ case ABOUT_EMOTIONS:
+
+ phrases = PhraseCollection.EMOTION_PHRASES;
+ emotions.addAll(Arrays.asList(RoboyEmotion.values()));
+
+ return State.Output.say(phrases.getRandomElement()).setEmotion(emotions.getRandomElement());
+
+
+ }
+
+ return Output.sayNothing();
+ }
+
+ @Override
+ public State getNextState() {
+
+ if(checkPplListening()){
+ nextState = getTransition(TRANSITION_PERSON_DETECTED);
+ } else {
+ nextState = getTransition(TRANSITION_LONELY_ROBOY);
+ }
+
+ return nextState;
+ }
+
+
+ /**
+ * Resets the list of available information so that it contains all of them.
+ */
+ private void resetAvailableInformation() {
+ availableInformation.clear();
+ availableInformation.addAll(Arrays.asList(InfoAbout.values()));
+ }
+
+ /**
+ * Selects one of the pieces of information from the availableInformation list at random and removes it from the list.
+ * If the list becomes empty this way, resets it to the initial state
+ * @return one of the available pieces of information
+ */
+ private InfoAbout selectRandomInfo() {
+ InfoAbout infoAbout = availableInformation.getRandomElement();
+ availableInformation.remove(infoAbout);
+ if (availableInformation.size() == 0) {
+ resetAvailableInformation(); // reset if all infos were used
+ logger.info("all pieces of information were selected at least once, resetting the list");
+ }
+ return infoAbout;
+ }
+
+ /**
+ * checks if vision module detects a person that is interested
+ * @return boolean if someone is interested
+ */
+
+ private boolean checkPplListening(){
+
+ try {
+
+ return getContext().PERSON_DETECTION.getLastValue().getData();
+ }catch(NullPointerException e){
+ logger.info("Make sure person detection is publishing, receiving: " + e.getMessage());
+ return false;
+ }
+ }
+
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/fairShowStates/MathState.java b/dialog/src/main/java/roboy/dialog/states/fairShowStates/MathState.java
new file mode 100644
index 00000000..a2869ea8
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/fairShowStates/MathState.java
@@ -0,0 +1,64 @@
+package roboy.dialog.states.fairShowStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.linguistics.Linguistics;
+import roboy.linguistics.sentenceanalysis.Interpretation;
+import roboy.ros.RosMainNode;
+import roboy.talk.PhraseCollection;
+
+/**
+ * State where Roboy can calculate mathematical expressions
+ */
+public class MathState extends State {
+
+ private final static String TRANSITION_FINISHED = "finished";
+
+ private final Logger LOGGER = LogManager.getLogger();
+
+ private State nextState = this;
+
+ public MathState(String stateIdentifier, StateParameters params) {
+ super(stateIdentifier, params);
+ }
+
+ @Override
+ public Output act() {
+
+ return Output.say(PhraseCollection.OFFER_MATH_PHRASES.getRandomElement());
+ }
+
+ @Override
+ public Output react(Interpretation input){
+
+ nextState = getTransition(TRANSITION_FINISHED);
+
+ return Output.say(getAnswerFromSemanticParser(input, getContext().ACTIVE_INTERLOCUTOR.getValue().getName(), getRosMainNode()));
+ }
+
+ @Override
+ public State getNextState() {
+ return nextState;
+ }
+
+ private String getAnswerFromSemanticParser(Interpretation input, String name, RosMainNode rmn) {
+
+ Linguistics.ParsingOutcome parserOutcome = input.getParsingOutcome();
+ if (parserOutcome == Linguistics.ParsingOutcome.SUCCESS) {
+ if (input.getAnswer() != null) {
+ String result = input.getAnswer();
+ LOGGER.info("Parsing was successful! The result is " + result);
+ return String.format(PhraseCollection.CONNECTING_PHRASES.getRandomElement(), name) + result;
+ } else {
+ LOGGER.error("Parsing failed! Answer is null!");
+ }
+ }
+
+ LOGGER.error("Parsing failed! Invalid parser outcome!");
+ String generativeAnswer = rmn.GenerateAnswer(input.getSentence());
+ return generativeAnswer != null ? generativeAnswer : PhraseCollection.PARSER_ERROR.getRandomElement();
+ }
+
+}
diff --git a/dialog/src/main/java/roboy/dialog/states/fairShowStates/ObjectDetectionState.java b/dialog/src/main/java/roboy/dialog/states/fairShowStates/ObjectDetectionState.java
new file mode 100644
index 00000000..6cb6a901
--- /dev/null
+++ b/dialog/src/main/java/roboy/dialog/states/fairShowStates/ObjectDetectionState.java
@@ -0,0 +1,217 @@
+package roboy.dialog.states.fairShowStates;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import roboy.dialog.states.definitions.MonologState;
+import roboy.dialog.states.definitions.State;
+import roboy.dialog.states.definitions.StateParameters;
+import roboy.emotions.RoboyEmotion;
+import roboy.ros.RosMainNode;
+import roboy.util.RandomList;
+
+import java.util.*;
+
+
+enum Objects {
+
+ AEROPLANE {
+
+ private RandomList phrases = new RandomList<>("an aeroplane. Let's fly to Spain and spend a day at the beach.", "an aeroplane. Are you ready for take-off?", "an aeroplane. Please fasten you seatbelts, Ladies and Gentlemen!");
+
+ @Override
+ public State.Output performSpecialAction(RosMainNode rmn){
+
+ return State.Output.say(connectingPhrases.getRandomElement() + phrases.getRandomElement()).setEmotion(RoboyEmotion.HEARTS);
+
+ }
+
+ },
+ BICYCLE {
+
+ private RandomList phrases = new RandomList<>("a bycicle, I bet I can ride faster than you", "a bike, I could easily win the tour de France", "a bycicle, I learnt riding the bycicle when I was just three years old. And you?", "bike. I love bycicles. I only fell off my bike once.");
+
+ @Override
+ public State.Output performSpecialAction(RosMainNode rmn){
+
+ return State.Output.say(connectingPhrases.getRandomElement() + phrases.getRandomElement()).setEmotion(happyEmotions.getRandomElement());
+
+ }
+
+ },
+ BOTTLE {
+
+ private RandomList phrases = new RandomList<>("a bottle, it would be awesome to drink a cold beer.", "a drink. In a few years from now, I want to be a bartender" , "a beverage. In the morning, I love coffee, in the evening, I drink beer", "a drink, chin-chin", "a bottle. Cheers, enjoy your drink.");
+
+ @Override
+ public State.Output performSpecialAction(RosMainNode rmn){
+
+ return State.Output.say(connectingPhrases.getRandomElement() + phrases.getRandomElement()).setEmotion(happyEmotions.getRandomElement());
+
+ }
+ },
+ CAR {
+
+ private RandomList phrases = new RandomList<>("car, in a few years, my fellow robots will be able to ride your car", "car. In Germany, you can go as fast as you want on the highway. I was really fast once." , "car. The cars we drive say a lot about us.");
+
+ @Override
+ public State.Output performSpecialAction(RosMainNode rmn){
+
+ return State.Output.say(connectingPhrases.getRandomElement() + phrases.getRandomElement());
+
+ }
+ },
+ CHAIR {
+
+ private RandomList phrases = new RandomList<>("a chair, grab a seat if you want." , "a chair, have you ever played musical chairs? I love this game." , "a chair. Sometimes, I am really, really lazy. I just sit on a chair and do nothing but relaxing. It is so comfortable" , "a chair. For now, I would prefer sitting on a beach chair close to the sea.");
+
+ @Override
+ public State.Output performSpecialAction(RosMainNode rmn){
+
+ return State.Output.say(connectingPhrases.getRandomElement() + phrases.getRandomElement()).setEmotion(happyEmotions.getRandomElement());
+
+ }
+
+ },
+ DIGNINGTABLE {
+ private RandomList phrases = new RandomList<>("a table, I am kind of hungry. I can hardly wait for my next meal" , "a table. We need some chairs and food and we can have a great dinner.");
+
+ @Override
+ public State.Output performSpecialAction(RosMainNode rmn){
+
+ return State.Output.say(connectingPhrases.getRandomElement() + phrases.getRandomElement()).setEmotion(RoboyEmotion.TEETH);
+
+ }
+ },
+ DOG {
+
+ private RandomList phrases = new RandomList<>("a dog. Sometimes they bark at me. I think they are afraid." , "a dog. When I was a robot kid, I loved dogs, but now I am scared of them." , "a dog. I hope he doesn't bite me. I am a bit afraid of big dogs.");
+
+ @Override
+ public State.Output performSpecialAction(RosMainNode rmn){
+
+ return State.Output.say(connectingPhrases.getRandomElement() + phrases.getRandomElement()).setEmotion(RoboyEmotion.ROLLING_EYES);
+
+ }
+ },
+ SOFA {
+
+ private RandomList phrases = new RandomList<>("a sofa. We need a TV and a good movie and we could have an awesome time together." , "a sofa. I love sofas. They are so relaxing.");
+
+
+ @Override
+ public State.Output performSpecialAction(RosMainNode rmn){
+
+ return State.Output.say(connectingPhrases.getRandomElement() + phrases.getRandomElement()).setEmotion(RoboyEmotion.SHY);
+
+ }
+ },
+ TVMONITOR {
+
+ private RandomList phrases = new RandomList<>("a mobile phone. Follow me on Facebook, Instagram or LinkedIn. You will not regret it." , "a mobile phone. Come on, take a picture with me.");
+ private RandomList socialMediaEmotions = new RandomList<>(RoboyEmotion.FACEBOOK_EYES, RoboyEmotion.INSTAGRAM_EYES, RoboyEmotion.LINKED_IN_EYES);
+
+ @Override
+ public State.Output performSpecialAction(RosMainNode rmn){
+
+ return State.Output.say(connectingPhrases.getRandomElement() + phrases.getRandomElement()).setEmotion(socialMediaEmotions.getRandomElement());
+
+ }
+ };
+
+ public RandomList connectingPhrases = new RandomList<>(" and look there, ", " and over there i can see ", " oh see, ", " this is a really nice ");
+ public RandomList happyEmotions = new RandomList<>(RoboyEmotion.SMILE_BLINK, RoboyEmotion.HAPPY, RoboyEmotion.SURPRISED, RoboyEmotion.TEETH);
+
+ public abstract State.Output performSpecialAction(RosMainNode rosNode);
+
+}
+
+/**
+ * Passive state to react on detected Objects
+ */
+public class ObjectDetectionState extends MonologState {
+
+ private final static String TRANSITION_FINISHED = "finished";
+
+ private final Set detectedObjects = new HashSet<>();
+
+ private Vector