//update various history when the system choose the next topic public void updateHistory(Feature nextTopic) { //update spatial constraint information bool spatialExist = false; if (topicHistory.Count() > 0) { Feature prevTopic = graph.getFeature(topicHistory[topicHistory.Count() - 1]); if (prevTopic.getNeighbor(nextTopic.Data) != null) { foreach(string str in Directional_Words) { if (str == prevTopic.getRelationshipNeighbor(nextTopic.Data)) { prevSpatial = str; spatialExist = true; break; } } } } if (!spatialExist) { prevSpatial = ""; } //update temporal constraint information FeatureSpeaker temp = new FeatureSpeaker(this.graph, temporalConstraintList); List<int> temporalIndex = temp.temporalConstraint(nextTopic,turn,topicHistory); for (int x = 0; x < temporalIndex.Count(); x++) { temporalConstraintList[temporalIndex[x]].Satisfied = true; } //update topic's history topicHistory.Add(nextTopic.Data); }
//Form2 calls this function //input is the input to be parsed. //messageToServer indicates whether or not we are preparing a response to the front-end. //forLog indicates whether or not we are preparing a response for a log output. //outOfTopic indicates whether or not we are continuing out-of-topic handling. //projectAsTopic true means we use forward projection to choose the next node to traverse to based on // how well the nodes in the n-length path from the current node relate to the current node. public string ParseInput(string input, bool messageToServer = false, bool forLog = false, bool outOfTopic = false, bool projectAsTopic = false) { string answer = IDK; string noveltyInfo = ""; double currentTopicNovelty = -1; // Pre-processing //Console.WriteLine("parse input " + input); //The input may be delimited by colons. Try to split it. String[] split_input = input.Trim().Split(':'); //Console.WriteLine("split input " + split_input[0]); // Lowercase for comparisons input = input.Trim().ToLower(); //Console.WriteLine("trimmed lowered input " + input); if (!string.IsNullOrEmpty(input)) { // Check to see if the AIML Bot has anything to say Request request = new Request(input, this.user, this.bot); Result result = bot.Chat(request); string output = result.Output; if (output.Length > 0) { if (!output.StartsWith(FORMAT)) return output; //MessageBox.Show("Converted output reads: " + output); input = output.Replace(FORMAT, "").ToLower(); } } // Remove punctuation input = RemovePunctuation(input); // Check if (this.topic == null) this.topic = this.graph.Root; //Console.WriteLine("Before new feature speaker in parse input"); FeatureSpeaker speaker = new FeatureSpeaker(this.graph, temporalConstraintList, prevSpatial, topicHistory); //Console.WriteLine("after new speaker in parse input"); if (split_input.Length != 0 || messageToServer) { //Step-through command from Query window. if (split_input[0].Equals("STEP")) { //Step through the program with blank inputs a certain number of times, //specified by the second argument in the command //Console.WriteLine("step_count " + split_input[1]); int step_count = int.Parse(split_input[1]); //TESTING JOINT MENTIONS //If there are two more colon-separated integers in the command, they are two node IDs that should be mentioned together. if (split_input.Length > 2) { //Since this is just a test, first, clear joint_mention_sets joint_mention_sets.Clear(); //Get the two indices from the command int index_1 = int.Parse(split_input[2]); int index_2 = int.Parse(split_input[3]); //Add the pair as a list of features to joint_mention_sets. List<Feature> joint_set = new List<Feature>(); joint_set.Add(this.graph.getFeature(index_1)); joint_set.Add(this.graph.getFeature(index_2)); joint_mention_sets.Add(joint_set); }//end if //Create an answer by calling the ParseInput function step_count times. answer = ""; for (int s = 0; s < step_count; s++) { //Get forServer and forLog responses. //Treat every 5th node as topic if (s % 5 == 1) { //Last parameter true means the current node is the topic node answer += ParseInput("", true, true, false, false); }//end if else answer += ParseInput("", true, true, false, false); answer += "\n"; } //Console.WriteLine("answer " + answer); //Just return this answer by itself return answer; }//end if // GET_NODE_VALUES command from Unity front-end if (split_input[0].Equals("GET_NODE_VALUES")) { Console.WriteLine("In get node values"); //Get the node we wish to get a set of values for, by data. //"data" is represented by each node's data field in the XML. //In the split input string, index 1 is the data of the node we want //to get values for. //Index 2 is the data of the node we are getting values relative to. string current_node_data = split_input[1]; string old_node_data = split_input[2]; //Get the features for these two nodes Feature current_feature = this.graph.getFeature(current_node_data); Feature old_feature = this.graph.getFeature(old_node_data); //If EITHER feature is null, return an error message. if (current_feature == null || old_feature == null) return "no feature found"; double[] return_node_values = speaker.calculateScoreComponents(current_feature, old_feature); //Turn them into a colon-separated string, headed by //the key-phrase "RETURN_NODE_VALUES" string return_string = return_node_values[Constant.ScoreArrayScoreIndex] + ":" + return_node_values[Constant.ScoreArrayNoveltyIndex] + ":" + return_node_values[Constant.ScoreArrayDiscussedAmountIndex] + ":" + return_node_values[Constant.ScoreArrayExpectedDramaticIndex] + ":" + return_node_values[Constant.ScoreArraySpatialIndex] + ":" + return_node_values[Constant.ScoreArrayHierarchyIndex] + ":"; return return_string; }//end if // GET_WEIGHT command from Unity front-end else if (split_input[0].Equals("GET_WEIGHT")) { //Return a colon-separated string of every weight value string return_string = "Weights: "; double[] weight_array = this.graph.getWeightArray(); for (int i = 0; i < weight_array.Length; i++) { if (i != 0) return_string += ":"; return_string += weight_array[i]; }//end for return return_string; }//end else if // SET_WEIGHT command from Unity front-end else if (split_input[0].Equals("SET_WEIGHT")) { //For each pair, //Index 1 is the index of the weight we wish to adjust. //Index 2 is the new weight value. for (int m = 1; m < split_input.Length; m += 2) { this.graph.setWeight(int.Parse(split_input[m]), double.Parse(split_input[m + 1])); }//end for //Return the new weight values right away. string return_string = "Weights: "; double[] weight_array = this.graph.getWeightArray(); for (int i = 0; i < weight_array.Length; i++) { if (i != 0) return_string += ":"; return_string += weight_array[i]; }//end for return return_string; }//end else if //GET_RELATED command from Unity front-end. //Returns a message containing a list of most novel and most proximal nodes else if (split_input[0].Equals("GET_RELATED")) { //GET_RELATED only gets related nodes for the current topic. noveltyInfo = speaker.getNovelty(this.topic, this.turn, noveltyAmount); return "Novelty:" + noveltyInfo + ":Proximal:" + speaker.getProximal(this.topic, noveltyAmount); }//end else if //SET_LANGUAGE command from Unity front-end. else if (split_input[0].Equals("SET_LANGUAGE")) { //Index 1 is the new language mode. language_mode_display = int.Parse(split_input[1]); language_mode_tts = int.Parse(split_input[2]); return "Language to display set to " + language_mode_display + ": Language of TTS set to " + language_mode_tts; }//end else if //BEGIN_TTS command from Unity front-end. else if (split_input[0].Equals("BEGIN_TTS")) { if (buffered_tts.Equals("")) { return "-1"; }//end if else { string to_return = "TTS_COMPLETE##" + buffered_tts; buffered_tts = ""; return to_return; } }//end else if //GET_TTS command from Unity front-end. else if (split_input[0].Equals("GET_TTS")) { if (buffered_tts.Equals("")) { return "-1"; }//end if else { return buffered_tts; }//end else }//end else if }//end else if // CASE: Nothing / Move on to next topic if (string.IsNullOrEmpty(input)) { Feature nextTopic = this.topic; string[] newBuffer; // == testing forward projection if (false) { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); int forwardTurn = 20; List<Feature> testingForwardP = speaker.forwardProjection(nextTopic, forwardTurn); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; // Format and display the TimeSpan value. string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("RunTime of forward projection" + elapsedTime); //print out all the topics for (int i = 0; i < forwardTurn; i++) { Console.WriteLine(testingForwardP[i].Data); } } // Can't guarantee it'll actually move on to anything... //If we are not projecting the current node as a topic, pick the next node normally if (!projectAsTopic) { nextTopic = speaker.getNextTopic(nextTopic, "", this.turn); //Console.WriteLine("Next Topic from " + this.topic.Data + " is " + nextTopic.Data); }//end if //If we are projecting the current node as a topic, pick the next node whose projected //path of nodes relate most to the current node (has the highest score). else { Console.WriteLine("Current Topic: " + this.topic.Data); //Go this many steps in the forward projection. int forward_turn = 5; //Get a list of all the neighbors to the current node List<Tuple<Feature, double, string>> all_neighbors = this.topic.Neighbors; //print out all neighbors Console.WriteLine("Neighbors: "); for (int i = 0; i < all_neighbors.Count; i++) { Console.WriteLine(all_neighbors[i].Item1.Data); }//end for //For each neighbor, find its projected path and sum the score of each node in the path relative to the current node. double highest_score = -10000; foreach (Tuple<Feature, double, string> neighbor_tuple in all_neighbors) { //First, check if the neighbor is a filtered node. //If so, do not consider it. if (filter_nodes.Contains(neighbor_tuple.Item1.Data)) continue; List<Feature> projected_path = speaker.forwardProjection(neighbor_tuple.Item1, forward_turn); //print out all the topics /*Console.WriteLine("Projected Path: "); for (int i = 0; i < forward_turn; i++) { Console.WriteLine(projected_path[i].Data); }//end for*/ double total_score = 0; //Total score calculation for topic //Sum score of each path node relative to the current node /* foreach (Feature path_node in projected_path) { total_score += speaker.calculateScore(path_node, this.topic); }//end foreach //Console.WriteLine("Score for path: " + total_score); */ //Total score calculation for joint mentions //If a joint mention appears in the path, add an amount (currently just the joint mention weight) //to the score of the neighbor (first path node) relative to the current node. bool joint_mention_exists = true; //For testing purposes, only check the first list in joint_mention_sets foreach (Feature temp_node in joint_mention_sets[0]) { if (!projected_path.Contains(temp_node)) joint_mention_exists = false; }//end foreach if (joint_mention_exists) { Console.WriteLine("Joint mention exists"); total_score = speaker.calculateScore(neighbor_tuple.Item1, this.topic) + this.graph.getSingleWeight(Constant.JointWeightIndex); }//end if if (total_score > highest_score) { highest_score = total_score; nextTopic = neighbor_tuple.Item1; }//end if }//end foreach //At the end of this foreach, nextTopic is set to the next node whose projected path had the highest sum score //relative to the current node. Console.WriteLine("Next Topic from " + this.topic.Data + " is " + nextTopic.Data + " with score " + highest_score); Console.WriteLine("Path: "); List<Feature> test_path = speaker.forwardProjection(nextTopic, forward_turn); //print out all the topics for (int i = 0; i < forward_turn; i++) { Console.WriteLine(test_path[i].Data); }//end for }//end else /* //Check for filter nodes. if (filter_nodes.Contains(nextTopic.Data)) { //If it is a filter node, take another step. Console.WriteLine("Filtering out " + nextTopic.Data); ParseInput("", false, false); }//end if */ noveltyInfo = speaker.getNovelty(nextTopic, this.turn, noveltyAmount); currentTopicNovelty = speaker.getCurrentTopicNovelty(); noveltyValue = speaker.getCurrentTopicNovelty(); newBuffer = FindStuffToSay(nextTopic); //MessageBox.Show("Explored " + nextTopic.Data + " with " + newBuffer.Length + " speaks."); nextTopic.DiscussedAmount += 1; this.graph.setFeatureDiscussedAmount(nextTopic.Data, nextTopic.DiscussedAmount); this.topic = nextTopic; // talk about this.buffer = newBuffer; answer = this.buffer[b++]; if (projectAsTopic) answer = "*****" + answer; } // CASE: Tell me more / Continue speaking else if (input.Contains("more") && input.Contains("tell")) { this.topic.DiscussedAmount += 1; this.graph.setFeatureDiscussedAmount(this.topic.Data, this.topic.DiscussedAmount); // talk about if (b < this.buffer.Length) answer = this.buffer[b++]; else { answer = "I've said all I can about that topic!" + "##" + "我已经把我知道的都说完了。" + "##"; } noveltyInfo = speaker.getNovelty(this.topic, this.turn, noveltyAmount); } // CASE: New topic/question else { Query query = BuildQuery(input); if (query == null) { return ParseInput("", messageToServer, false, true); //answer = "I'm sorry, I'm afraid I don't understand what you are asking. But here's something I do know about. "; //answer = answer + ParseInput("", false, false); //out_of_topic = true; } else { Feature feature = query.MainTopic; feature.DiscussedAmount += 1; this.graph.setFeatureDiscussedAmount(feature.Data, feature.DiscussedAmount); this.topic = feature; this.buffer = ParseQuery(query); answer = this.buffer[b++]; noveltyInfo = speaker.getNovelty(this.topic, this.turn, noveltyAmount); } } //Update updateHistory(this.topic); this.turn++; if (answer.Length == 0) { return IDK; } else { if (messageToServer) { //Return message to Unity front-end with both novel and proximal nodes return MessageToServer(this.topic, answer, noveltyInfo, speaker.getProximal(this.topic, noveltyAmount), forLog, outOfTopic); } if (outOfTopic) answer += ParseInput("", false, false); if (forLog) return answer; else return answer;// +" <Novelty Info: " + noveltyInfo + " > <Proximal Info: " + speaker.getProximal(this.topic, noveltyAmount) + ">"; } }