/// <summary> /// deals with the "sentence" tag /// </summary> /// <param name="thisNode">the "sentence" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string sentence(XmlNode thisNode, cCategory thisCat) { string sSentence = ""; // lowercase it all first string sText = (string)thisNode.InnerText.ToString().ToLower().Trim(); // split into individual characters char[] cWords = sText.ToCharArray(); for (int i = 0; i < cWords.Length; i++) { if (i == 0) { char cChanged = char.ToUpper(cWords[i]); cWords[i] = cChanged; } else { if (i > 1) { if ((cWords[i - 2] == '.') & (cWords[i - 1] == ' ')) { char cChanged = char.ToUpper(cWords[i]); cWords[i] = cChanged; } } } sSentence += (string)Convert.ToString(cWords[i]); } return(sSentence); }
/// <summary> /// Overload that allows the new cCategory to be a clone of the passed cCategory /// </summary> /// <param name="SourceCat">the cCategory to be cloned</param> public cCategory(cCategory SourceCat) { //this.alInputStar.AddRange((ArrayList)SourceCat.alInputStar); //this.alThatStar.AddRange((ArrayList)SourceCat.alThatStar); //this.alTopicStar.AddRange((ArrayList)SourceCat.alTopicStar); this.sPath = (string)SourceCat.sPath.ToString(); this.sTemplate = (string)SourceCat.sTemplate.ToString(); }
/// <summary> /// deals with the "think" tag /// </summary> /// <param name="thisNode">the "think" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> private static void think(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { string sNode = thisNode.InnerXml; cCategory thinkCat = new cCategory(thisCat.sPath, "<template>" + sNode + "</template>"); thinkCat.alInputStar = thisCat.alInputStar; thinkCat.alThatStar = thinkCat.alThatStar; thinkCat.alTopicStar = thinkCat.alTopicStar; cCategory nullCat = process(thinkCat, myBot, sUserID); }
/// <summary> /// deals with the "srai" tag /// </summary> /// <param name="thisNode">the "srai" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string srai(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { string sNode = thisNode.InnerText; cResponse myReply = myBot.chat(sNode, sUserID); string sReply = ""; foreach (string sSentence in myReply.alOutput) { sReply += sSentence + " "; } return(sReply); }
/// <summary> /// Adds a new category to the Graphmaster structure /// </summary> /// <param name="sPath">the PATH element of the new cCategory. NB Normalization should be done to the PATH before calling this method</param> /// <param name="sTemplate">the TEMPLATE element for the new cCategory</param> public void addCat(string sPath, string sTemplate, string filename) { // create the Category cCategory cat = new cCategory(sPath, sTemplate); // for debugging cat.filename = filename; // just to make sure the Path is tidy string sEnd = sPath.Trim(); // o.k. add the category to the root node of the graphmaster and let the cNodeMapper // class methods deal with it. rootNodeMapper.addCat(sEnd, cat); }
/// <summary> /// Adds a new category to the Graphmaster structure /// </summary> /// <param name="sPath">the PATH element of the new cCategory. NB Normalization should be done to the PATH before calling this method</param> /// <param name="sTemplate">the TEMPLATE element for the new cCategory</param> public void addCat(string sPath, string sTemplate, string filename) { // create the Category cCategory cat = new cCategory(sPath, sTemplate); // for debugging cat.filename=filename; // just to make sure the Path is tidy string sEnd = sPath.Trim(); // o.k. add the category to the root node of the graphmaster and let the cNodeMapper // class methods deal with it. rootNodeMapper.addCat(sEnd, cat); }
/// <summary> /// deals with the "gender" tag /// </summary> /// <param name="thisNode">the "gender" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> private static string gender(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { string sNode = thisNode.InnerText; if (sNode == "") { string sContent = "<star/>"; XmlDocument tempDoc = new XmlDocument(); tempDoc.LoadXml(sContent); XmlNode myNode = tempDoc.SelectSingleNode("star"); sNode = star(myNode, thisCat, myBot, sUserID); } string sOutput = (string)cNormalizer.substitute(cGlobals.getGenderSubs(), sNode); return((string)sOutput.ToLower()); }
/// <summary> /// deals with the "bot" tag /// </summary> /// <param name="thisNode">the "bot" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string bot(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return a blank if (myAttributes.Count == 0) { return(""); } // o.k. we have an attribute to process // get the value associated with the "name" attribute string sName = thisNode.Attributes["name"].Value; return((string)myBot.getPredicate(sName)); }
/// <summary> /// deals with the "sr" tag /// </summary> /// <param name="thisNode">the "sr" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string sr(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { string sContent = "<star/>"; XmlDocument tempDoc = new XmlDocument(); tempDoc.LoadXml(sContent); XmlNode myNode = tempDoc.SelectSingleNode("star"); string sStar = star(myNode, thisCat, myBot, sUserID); string sNode = "<srai>" + sStar + "</srai>"; XmlDocument tempSR = new XmlDocument(); tempSR.LoadXml(sNode); XmlNode srNode = tempSR.SelectSingleNode("srai"); return((string)srai(srNode, thisCat, myBot, sUserID)); }
/// <summary> /// Adds a category to the graphmaster structure /// </summary> /// <param name="sEnd">a string to be mapped by this node</param> /// <param name="cat">the cCategory to be associated with the nodemapper</param> public void addCat(string sEnd, cCategory cat) { // this node represents the end of the sentence to be mapped so set // the cCategory for this node to the cCategory that was passed to the // function if (sEnd == "" || sEnd.Length <= 0) { thisCategory = cat; return; } // otherwise, this sentence requires further child nodemappers in order to // be fully mapped within the GraphMaster structure. // split the input into its component words string[] sWords = sEnd.Split(" ".ToCharArray()); // get the first word (to form the key for the child nodemapper) string sFirst = sWords[0]; // concatenate the rest of the sentence into a suffix (to act as the // sEnd argument in the child nodemapper) string endstr = sEnd.Substring(sFirst.Length, sEnd.Length - sFirst.Length); endstr = endstr.Trim(); // o.k. check we don't already have a child with the key from this sentence cNodeMapper wordmap = (cNodeMapper)htChildren[sFirst]; // if we do then pass the handling of this sentence down the branch to the // child nodemapper if (wordmap != null) { wordmap.addCat(endstr, cat); } // otherwise the child nodemapper doesn't yet exist, so create a new one else { cNodeMapper mapper = new cNodeMapper(); mapper.sWord = sFirst; mapper.addCat(endstr, cat); htChildren.Add(mapper.sWord, mapper); } }
/// <summary> /// deals with the "random" tag. /// </summary> /// <param name="thisNode">the "random" node</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the text that is the result of processing this tag</returns> private static string random(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // get the XML for processing string sContent = thisNode.OuterXml; XmlDocument choiceList = new XmlDocument(); choiceList.LoadXml(sContent); // get the child nodes defining the list of outputs (one of which to be randomly // selected) XmlNodeList Choices = choiceList.GetElementsByTagName("li"); // oops, no choices were found if (Choices.Count == 0) { return(""); } // o.k. select one of the choices at random and return it as a string Random r = new Random(); // Process the selection and make sure we don't have to do anything else with it XmlNode Chosen = Choices.Item(r.Next(Choices.Count)); // o.k. the <li> tag has AIML within it if (Chosen.HasChildNodes) { // process what is between the <li> tags string sNode = Chosen.InnerXml; cCategory LiCat = new cCategory(thisCat.sPath, "<template>" + sNode + "</template>"); LiCat.alInputStar = thisCat.alInputStar; LiCat.alThatStar = thisCat.alThatStar; LiCat.alTopicStar = thisCat.alTopicStar; cCategory nullCat = process(LiCat, myBot, sUserID); return(nullCat.sTemplate); } // otherwise return the plain text else { return((string)Chosen.InnerText); } }
/// <summary> /// deals with the "get" tag /// </summary> /// <param name="thisNode">the "get" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string getTag(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return a blank if (myAttributes.Count == 0) { return(""); } // o.k. we have an attribute to process // get the value associated with the "name" attribute string sName = thisNode.Attributes["name"].Value; cUser myUser = (cUser)cUsers.users[sUserID]; string sValue = (string)myUser.Predicates[sName]; if (sValue == null) { sValue = ""; } return(sValue.Trim()); }
/// <summary> /// deals with the "random" tag. /// </summary> /// <param name="thisNode">the "random" node</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the text that is the result of processing this tag</returns> private static string random (XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // get the XML for processing string sContent=thisNode.OuterXml; XmlDocument choiceList=new XmlDocument(); choiceList.LoadXml(sContent); // get the child nodes defining the list of outputs (one of which to be randomly // selected) XmlNodeList Choices=choiceList.GetElementsByTagName("li"); // oops, no choices were found if (Choices.Count==0) return ""; // o.k. select one of the choices at random and return it as a string Random r = new Random(); // Process the selection and make sure we don't have to do anything else with it XmlNode Chosen=Choices.Item(r.Next(Choices.Count)); // o.k. the <li> tag has AIML within it if (Chosen.HasChildNodes) { // process what is between the <li> tags string sNode=Chosen.InnerXml; cCategory LiCat = new cCategory(thisCat.sPath,"<template>"+sNode+"</template>"); LiCat.alInputStar=thisCat.alInputStar; LiCat.alThatStar=thisCat.alThatStar; LiCat.alTopicStar=thisCat.alTopicStar; cCategory nullCat=process(LiCat,myBot,sUserID); return nullCat.sTemplate; } // otherwise return the plain text else { return (string)Chosen.InnerText; } }
/// <summary> /// Given a cCategory, returns the same object but with the template section processed and changed appropriately /// </summary> /// <param name="InputCat">the cCategory whose AIML template section is to be processed</param> /// <param name="myBot">the botID of the bot whose Graphmaster is being interrogated</param> /// <param name="sUserID">the userID of the person who requires a reply</param> /// <returns>the Ccategory to return whose sTemplate is the final reply sentence for the given input</returns> public static cCategory process(cCategory InputCat, cBot myBot, string sUserID) { // get the XML for processing string sContent = InputCat.ToString(); XmlDocument Template = new XmlDocument(); Template.LoadXml(sContent); if (Template.HasChildNodes) { // get the child nodes found within the template. XmlNodeList TemplateChildren = Template.DocumentElement.ChildNodes; // the cCategory to return cCategory outputCategory = new cCategory(InputCat); // replace the output part of the cCategory with a blank (to be filled in // by the following processes) outputCategory.sTemplate = ""; // process each of the child nodes that have been found in the template foreach (XmlNode thisNode in TemplateChildren) { // go to a method that is basically a bit switch statement outputCategory.sTemplate += cProcessor.processtag(thisNode, InputCat, myBot, sUserID); } // do some simple normalization on the template outputCategory.checktext(); return(outputCategory); } else { // if no child nodes are found then the template must already contain a raw // string so just return it return(InputCat); } }
/// <summary> /// Searches for a cCategory within the Graphmaster structure given a certain input /// </summary> /// <param name="sInput">Normalised input concatenated with "that" and "topic" parameters - i.e. the PATH</param> /// <param name="sBotID">the botID of the bot whose GraphMaster structure this is</param> /// <param name="sUserID">the userID of the person who requires a reply</param> /// <returns>a string representing the bot's response</returns> public string[] evaluate(string sInput, cBot myBot, string sUserID) { // to hold the raw output string[] sRawOutput = { "", "" }; // deal with void input if (sInput.Length == 0) { return(sRawOutput); } // create a category to represent the returned category cCategory cat = rootNodeMapper.evaluate(sInput, 0, ""); // if we've found something then process it and return a string if (cat != null) { cCategory procCat = cProcessor.process(cat, myBot, sUserID); sRawOutput[0] = cat.filename; sRawOutput[1] = procCat.ToString().Trim(); return(sRawOutput); } // this shouldn't happen but is here just in case :-) else { if (cGlobals.isDebug) { sRawOutput[1] = "No match found for input path: " + sInput; } else { sRawOutput[1] = "I'm afraid I don't understand. Perhaps some of my AIML files are missing. You can download them from http://www.alicebot.org/ and place them in the following directory: " + cGlobals.AIMLPATH; } return(sRawOutput); } }
/// <summary> /// deals with the "think" tag /// </summary> /// <param name="thisNode">the "think" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> private static void think(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { string sNode=thisNode.InnerXml; cCategory thinkCat = new cCategory(thisCat.sPath,"<template>"+sNode+"</template>"); thinkCat.alInputStar=thisCat.alInputStar; thinkCat.alThatStar=thinkCat.alThatStar; thinkCat.alTopicStar=thinkCat.alTopicStar; cCategory nullCat=process(thinkCat,myBot,sUserID); }
/// <summary> /// deals with the "lower" tag /// </summary> /// <param name="thisNode">the "lower" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string lower(XmlNode thisNode, cCategory thisCat) { return (string)thisNode.InnerText.ToLower(); }
/// <summary> /// deals with the "sentence" tag /// </summary> /// <param name="thisNode">the "sentence" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string sentence(XmlNode thisNode, cCategory thisCat) { string sSentence=""; // lowercase it all first string sText=(string)thisNode.InnerText.ToString().ToLower().Trim(); // split into individual characters char[] cWords=sText.ToCharArray(); for(int i=0; i<cWords.Length;i++) { if(i==0) { char cChanged = char.ToUpper(cWords[i]); cWords[i]=cChanged; } else { if(i>1) { if((cWords[i-2]=='.')&(cWords[i-1]==' ')) { char cChanged = char.ToUpper(cWords[i]); cWords[i]=cChanged; } } } sSentence+=(string)Convert.ToString(cWords[i]); } return sSentence; }
/// <summary> /// deals with the "set" tag /// </summary> /// <param name="thisNode">the "set" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string setTag(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return a blank if (myAttributes.Count==0) { return ""; } // o.k. we have an attribute to process // get the value(s) associated with the "name" attribute string sName=thisNode.Attributes["name"].Value; string sContent=thisNode.InnerText; string sStarXML=thisNode.InnerXml; // ultimately holds the processed value for this predicate string sValue=""; // check for further processing if(sStarXML.StartsWith("<star")) { XmlDocument starXML=new XmlDocument(); starXML.LoadXml(sStarXML); // get the node that we need XmlNode starnode=starXML.SelectSingleNode("descendant::star"); sValue=star(starnode, thisCat,myBot,sUserID); } else { sValue=sContent; } // make sure any names are stored correctly if (sName.ToUpper()=="NAME") { string sNewValue=formal(sValue); sValue=sNewValue; thisNode.InnerText=" "+sValue; } cUser myUser=(cUser)cUsers.users[sUserID]; // handle the topic predicate otherwise it is a generic predicate if (sName.ToUpper()=="TOPIC") { if ((sValue.Length==0)||(sValue=="")||(sValue==" ")||(sValue==null)) { sValue="*"; } myUser.sTopic=sValue; } else { if(myUser.Predicates.Contains(sName)) { myUser.Predicates.Remove(sName); } myUser.Predicates.Add(sName,sValue); } return thisNode.InnerText; }
/// <summary> /// deals with the "topicstar" tag. /// </summary> /// <param name="thisNode">the "topicstar" node</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string topicstar(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // o.k. lets check that we actually have some TopicStars (there is a small possibility that this // might happen if the AIML code is flunky) if (thisCat.alTopicStar.Count > 0) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return the last ThatStar if (myAttributes.Count == 0) { return((string)thisCat.alTopicStar[0]); } // o.k. we have some attributes to process and, // yeah I know upper case attributes are naff but I've seen // some AIML with them in so I'm including this kludge just in case else { // to hold the location within the alTopicStar to be accessed int iIndex; string sIndex; // get the value associated with the "index" attribute sIndex = thisNode.Attributes["index"].Value; if (sIndex == null) { // get the value associated with the "INDEX" attribute sIndex = thisNode.Attributes["INDEX"].Value; } if (sIndex != null) { // get the iIndex of the right value iIndex = Convert.ToInt32(sIndex); // and remember that arrays start counting from 0! :-) iIndex -= 1; // check we're within range and act appropriately if ((iIndex < 0) || (iIndex > thisCat.alTopicStar.Count - 1)) { // check the bot logs! cGlobals.writeLog("The user " + sUserID + " caused the reference of a TopicStar attribute that was out of range.\n"); return(""); } else { // Voila! return((string)thisCat.alTopicStar[iIndex]); } } else { // the "index" is not an attribute so return the default value return((string)thisCat.alTopicStar[0]); } } } else { // if we get here it means the AIML is FUBAR cGlobals.writeLog("The user " + sUserID + " caused the return of a blank TopicStar. CHECK THE AIML path: " + thisCat.sPath + "\n"); return(""); } }
/// <summary> /// deals with the "sr" tag /// </summary> /// <param name="thisNode">the "sr" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string sr(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { string sContent="<star/>"; XmlDocument tempDoc = new XmlDocument(); tempDoc.LoadXml(sContent); XmlNode myNode=tempDoc.SelectSingleNode("star"); string sStar=star(myNode, thisCat, myBot, sUserID); string sNode="<srai>"+sStar+"</srai>"; XmlDocument tempSR = new XmlDocument(); tempSR.LoadXml(sNode); XmlNode srNode=tempSR.SelectSingleNode("srai"); return (string)srai(srNode,thisCat,myBot,sUserID); }
/// <summary> /// processes the individual aiml tags found within the "target" portion of a category /// </summary> /// <param name="thisNode">the xml node to be processed</param> /// <param name="InputCat">the category that the node is a part of</param> /// <param name="myBot">the bot (doh!)</param> /// <param name="sUserID">id's the user (what did you think it did?)</param> /// <returns></returns> private static string processtag(XmlNode thisNode, cCategory InputCat, cBot myBot, string sUserID) { // to be returned... string sOutput = ""; // holds the inner text from the child nodes in case they need to be displayed string sChildInnerText = ""; // get the name of the node string sNodeName = thisNode.Name.ToUpper(); // check for children and process them if ((thisNode.HasChildNodes) & ((sNodeName != "THINK") & (sNodeName != "RANDOM"))) { XmlNodeList TagChildren = thisNode.ChildNodes; foreach (XmlNode Child in TagChildren) { sChildInnerText += processtag(Child, InputCat, myBot, sUserID); } thisNode.InnerText = sChildInnerText; } // do the on this particular node switch (sNodeName) { case "RANDOM": sOutput += random(thisNode, InputCat, myBot, sUserID); break; case "SIZE": { sOutput += Convert.ToString(myBot.Size); break; } case "ID": sOutput += (string)sUserID; break; case "DATE": sOutput += (string)DateTime.Now.ToString(); break; case "VERSION": sOutput += cGlobals.VERSION; break; case "STAR": sOutput += " " + star(thisNode, InputCat, myBot, sUserID) + " "; break; case "THAT": sOutput += " " + that(thisNode, InputCat, myBot, sUserID) + " "; break; case "INPUT": sOutput += " " + input(thisNode, InputCat, myBot, sUserID) + " "; break; case "THATSTAR": sOutput += " " + thatstar(thisNode, InputCat, myBot, sUserID) + " "; break; case "TOPICSTAR": sOutput += " " + topicstar(thisNode, InputCat, myBot, sUserID) + " "; break; case "GET": sOutput += " " + getTag(thisNode, InputCat, myBot, sUserID) + " "; break; case "BOT": sOutput += " " + bot(thisNode, InputCat, myBot, sUserID) + " "; break; case "UPPERCASE": sOutput += upper(thisNode, InputCat); break; case "LOWERCASE": sOutput += lower(thisNode, InputCat); break; case "FORMAL": sOutput += formal(thisNode, InputCat); break; case "SENTENCE": sOutput += sentence(thisNode, InputCat); break; case "SR": sOutput += sr(thisNode, InputCat, myBot, sUserID); break; /*case "CONDITION": * outputCategory.sTemplate+= func(thisNode, InputCat); * break;*/ case "SET": sOutput += setTag(thisNode, InputCat, myBot, sUserID); break; case "GOSSIP": // Not really important so implement at a later date when some // use for it is found break; case "SRAI": sOutput += srai(thisNode, InputCat, myBot, sUserID); break; case "PERSON": sOutput += person(thisNode, InputCat, myBot, sUserID); break; case "PERSON2": sOutput += person2(thisNode, InputCat, myBot, sUserID); break; case "GENDER": sOutput += gender(thisNode, InputCat, myBot, sUserID); break; case "THINK": think(thisNode, InputCat, myBot, sUserID); break; /*case "LEARN": * outputCategory.sTemplate+= func(thisNode, InputCat); * break; * case "SYSTEM": * outputCategory.sTemplate+= func(thisNode, InputCat); * break; * case "JAVASCRIPT": * outputCategory.sTemplate+= func(thisNode, InputCat); * break; * /*case "": * outputCategory.sTemplate+= func(thisNode, InputCat); * break; * case "": * outputCategory.sTemplate+= func(thisNode, InputCat); * break; * case "": * outputCategory.sTemplate+= func(thisNode, InputCat); * break; * * ...add modifications here*/ default: sOutput += thisNode.InnerText.Trim(); break; } return(" " + sOutput); }
/// <summary> /// deals with the "topicstar" tag. /// </summary> /// <param name="thisNode">the "topicstar" node</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string topicstar (XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // o.k. lets check that we actually have some TopicStars (there is a small possibility that this // might happen if the AIML code is flunky) if (thisCat.alTopicStar.Count>0) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return the last ThatStar if (myAttributes.Count==0) { return (string)thisCat.alTopicStar[0]; } // o.k. we have some attributes to process and, // yeah I know upper case attributes are naff but I've seen // some AIML with them in so I'm including this kludge just in case else { // to hold the location within the alTopicStar to be accessed int iIndex; string sIndex; // get the value associated with the "index" attribute sIndex=thisNode.Attributes["index"].Value; if (sIndex==null) { // get the value associated with the "INDEX" attribute sIndex=thisNode.Attributes["INDEX"].Value; } if(sIndex!=null) { // get the iIndex of the right value iIndex=Convert.ToInt32(sIndex); // and remember that arrays start counting from 0! :-) iIndex-=1; // check we're within range and act appropriately if((iIndex<0)||(iIndex>thisCat.alTopicStar.Count-1)) { // check the bot logs! cGlobals.writeLog("The user "+sUserID+" caused the reference of a TopicStar attribute that was out of range.\n"); return ""; } else { // Voila! return (string)thisCat.alTopicStar[iIndex]; } } else { // the "index" is not an attribute so return the default value return (string)thisCat.alTopicStar[0]; } } } else { // if we get here it means the AIML is FUBAR cGlobals.writeLog("The user "+sUserID+" caused the return of a blank TopicStar. CHECK THE AIML path: "+thisCat.sPath+"\n"); return ""; } }
/// <summary> /// deals with the "input" tag /// </summary> /// <param name="thisNode">the "input" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string input(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // the conversation histories are stored in the cUser object for the user cUser thisUser = (cUser)cUsers.users[sUserID]; // o.k. lets check that we actually have some "That"s (there is a small possibility that this // might happen if the AIML code is flunky) if (thisUser.alInput.Count>0) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return the last That if (myAttributes.Count==0) { ArrayList alLastInput=(ArrayList)thisUser.alInput[0]; return (string)alLastInput[0]; } // o.k. we have some attributes to process and, // yeah I know upper case attributes are naff but I've seen // some AIML with them in so I'm including this kludge just in case else { string sIndex; // get the value associated with the "index" attribute sIndex=thisNode.Attributes["index"].Value; if (sIndex==null) { // get the value associated with the "INDEX" attribute sIndex=thisNode.Attributes["INDEX"].Value; } if(sIndex!=null) { // o.k. if we're here then there is an index to a particular "that" statement // the index can be either in one or two dimensions string sFirstDimension, sSecondDimension; int iFirst,iSecond; // extract the first dimension sFirstDimension=(string)sIndex[0].ToString(); iFirst=Convert.ToInt32(sFirstDimension); iFirst-=1; // check the first dimension is in range if((iFirst>thisUser.alInput.Count-1)||(iFirst<0)) { // write a warning message to the bot log cGlobals.writeLog("The user "+sUserID+" caused a reference to a <that> that was out of range. Check AIML for path:"+thisCat.sPath+"\n"); // for safety's sake return the default value for <that> ArrayList alLastInput=(ArrayList)thisUser.alInput[0]; return (string)alLastInput[0]; } // now check if we have a second dimension if(sIndex.Length==3) // sIndex will be something like "1,2" { sSecondDimension=(string)sIndex[2].ToString(); iSecond=Convert.ToInt32(sSecondDimension); iSecond-=1; } else { iSecond=0; } // get the appropriate arraylist of sentences ArrayList alLastInputSentences=(ArrayList)thisUser.alInput[iFirst]; // check the second dimension is in range if((iSecond>alLastInputSentences.Count-1)||(iSecond<0)) { // write a warning message to the bot log cGlobals.writeLog("The user "+sUserID+" caused a reference to a two dimensional <that> that was out of range. Check AIML for path:"+thisCat.sPath+"\n"); // for safety's sake return the default value for <that> ArrayList alLastInput=(ArrayList)thisUser.alInput[0]; return (string)alLastInput[0]; } // okay, we have two dimensions and they're in range so return the correct result! return (string)alLastInputSentences[iSecond]; } else { // index is not one of the attributes so just return the default ArrayList alLastInput=(ArrayList)thisUser.alInput[0]; return (string)alLastInput[0]; } } } else { // if we get here it means the AIML is FUBAR cGlobals.writeLog("The user "+sUserID+" caused the return of a blank INPUT. CHECK THE AIML path: "+thisCat.sPath+"\n"); return ""; } }
/// <summary> /// deals with the "input" tag /// </summary> /// <param name="thisNode">the "input" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string input(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // the conversation histories are stored in the cUser object for the user cUser thisUser = (cUser)cUsers.users[sUserID]; // o.k. lets check that we actually have some "That"s (there is a small possibility that this // might happen if the AIML code is flunky) if (thisUser.alInput.Count > 0) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return the last That if (myAttributes.Count == 0) { ArrayList alLastInput = (ArrayList)thisUser.alInput[0]; return((string)alLastInput[0]); } // o.k. we have some attributes to process and, // yeah I know upper case attributes are naff but I've seen // some AIML with them in so I'm including this kludge just in case else { string sIndex; // get the value associated with the "index" attribute sIndex = thisNode.Attributes["index"].Value; if (sIndex == null) { // get the value associated with the "INDEX" attribute sIndex = thisNode.Attributes["INDEX"].Value; } if (sIndex != null) { // o.k. if we're here then there is an index to a particular "that" statement // the index can be either in one or two dimensions string sFirstDimension, sSecondDimension; int iFirst, iSecond; // extract the first dimension sFirstDimension = (string)sIndex[0].ToString(); iFirst = Convert.ToInt32(sFirstDimension); iFirst -= 1; // check the first dimension is in range if ((iFirst > thisUser.alInput.Count - 1) || (iFirst < 0)) { // write a warning message to the bot log cGlobals.writeLog("The user " + sUserID + " caused a reference to a <that> that was out of range. Check AIML for path:" + thisCat.sPath + "\n"); // for safety's sake return the default value for <that> ArrayList alLastInput = (ArrayList)thisUser.alInput[0]; return((string)alLastInput[0]); } // now check if we have a second dimension if (sIndex.Length == 3) // sIndex will be something like "1,2" { sSecondDimension = (string)sIndex[2].ToString(); iSecond = Convert.ToInt32(sSecondDimension); iSecond -= 1; } else { iSecond = 0; } // get the appropriate arraylist of sentences ArrayList alLastInputSentences = (ArrayList)thisUser.alInput[iFirst]; // check the second dimension is in range if ((iSecond > alLastInputSentences.Count - 1) || (iSecond < 0)) { // write a warning message to the bot log cGlobals.writeLog("The user " + sUserID + " caused a reference to a two dimensional <that> that was out of range. Check AIML for path:" + thisCat.sPath + "\n"); // for safety's sake return the default value for <that> ArrayList alLastInput = (ArrayList)thisUser.alInput[0]; return((string)alLastInput[0]); } // okay, we have two dimensions and they're in range so return the correct result! return((string)alLastInputSentences[iSecond]); } else { // index is not one of the attributes so just return the default ArrayList alLastInput = (ArrayList)thisUser.alInput[0]; return((string)alLastInput[0]); } } } else { // if we get here it means the AIML is FUBAR cGlobals.writeLog("The user " + sUserID + " caused the return of a blank INPUT. CHECK THE AIML path: " + thisCat.sPath + "\n"); return(""); } }
/// <summary> /// Given a cCategory, returns the same object but with the template section processed and changed appropriately /// </summary> /// <param name="InputCat">the cCategory whose AIML template section is to be processed</param> /// <param name="myBot">the botID of the bot whose Graphmaster is being interrogated</param> /// <param name="sUserID">the userID of the person who requires a reply</param> /// <returns>the Ccategory to return whose sTemplate is the final reply sentence for the given input</returns> public static cCategory process(cCategory InputCat, cBot myBot, string sUserID) { // get the XML for processing string sContent=InputCat.ToString(); XmlDocument Template=new XmlDocument(); Template.LoadXml(sContent); if(Template.HasChildNodes) { // get the child nodes found within the template. XmlNodeList TemplateChildren=Template.DocumentElement.ChildNodes; // the cCategory to return cCategory outputCategory=new cCategory(InputCat); // replace the output part of the cCategory with a blank (to be filled in // by the following processes) outputCategory.sTemplate=""; // process each of the child nodes that have been found in the template foreach(XmlNode thisNode in TemplateChildren) { // go to a method that is basically a bit switch statement outputCategory.sTemplate+=cProcessor.processtag(thisNode,InputCat,myBot,sUserID); } // do some simple normalization on the template outputCategory.checktext(); return outputCategory; } else // if no child nodes are found then the template must already contain a raw // string so just return it return InputCat; }
/// <summary> /// Overload that allows the new cCategory to be a clone of the passed cCategory /// </summary> /// <param name="SourceCat">the cCategory to be cloned</param> public cCategory(cCategory SourceCat) { //this.alInputStar.AddRange((ArrayList)SourceCat.alInputStar); //this.alThatStar.AddRange((ArrayList)SourceCat.alThatStar); //this.alTopicStar.AddRange((ArrayList)SourceCat.alTopicStar); this.sPath=(string)SourceCat.sPath.ToString(); this.sTemplate=(string)SourceCat.sTemplate.ToString(); }
/// <summary> /// deals with the "set" tag /// </summary> /// <param name="thisNode">the "set" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string setTag(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return a blank if (myAttributes.Count == 0) { return(""); } // o.k. we have an attribute to process // get the value(s) associated with the "name" attribute string sName = thisNode.Attributes["name"].Value; string sContent = thisNode.InnerText; string sStarXML = thisNode.InnerXml; // ultimately holds the processed value for this predicate string sValue = ""; // check for further processing if (sStarXML.StartsWith("<star")) { XmlDocument starXML = new XmlDocument(); starXML.LoadXml(sStarXML); // get the node that we need XmlNode starnode = starXML.SelectSingleNode("descendant::star"); sValue = star(starnode, thisCat, myBot, sUserID); } else { sValue = sContent; } // make sure any names are stored correctly if (sName.ToUpper() == "NAME") { string sNewValue = formal(sValue); sValue = sNewValue; thisNode.InnerText = " " + sValue; } cUser myUser = (cUser)cUsers.users[sUserID]; // handle the topic predicate otherwise it is a generic predicate if (sName.ToUpper() == "TOPIC") { if ((sValue.Length == 0) || (sValue == "") || (sValue == " ") || (sValue == null)) { sValue = "*"; } myUser.sTopic = sValue; } else { if (myUser.Predicates.Contains(sName)) { myUser.Predicates.Remove(sName); } myUser.Predicates.Add(sName, sValue); } return(thisNode.InnerText); }
/// <summary> /// Adds a category to the graphmaster structure /// </summary> /// <param name="sEnd">a string to be mapped by this node</param> /// <param name="cat">the cCategory to be associated with the nodemapper</param> public void addCat(string sEnd, cCategory cat) { // this node represents the end of the sentence to be mapped so set // the cCategory for this node to the cCategory that was passed to the // function if (sEnd == "" || sEnd.Length <= 0) { thisCategory = cat; return; } // otherwise, this sentence requires further child nodemappers in order to // be fully mapped within the GraphMaster structure. // split the input into its component words string[] sWords = sEnd.Split( " ".ToCharArray() ); // get the first word (to form the key for the child nodemapper) string sFirst = sWords[0]; // concatenate the rest of the sentence into a suffix (to act as the // sEnd argument in the child nodemapper) string endstr = sEnd.Substring( sFirst.Length, sEnd.Length - sFirst.Length ); endstr = endstr.Trim(); // o.k. check we don't already have a child with the key from this sentence cNodeMapper wordmap = (cNodeMapper)htChildren[sFirst]; // if we do then pass the handling of this sentence down the branch to the // child nodemapper if (wordmap != null) { wordmap.addCat(endstr, cat); } // otherwise the child nodemapper doesn't yet exist, so create a new one else { cNodeMapper mapper = new cNodeMapper(); mapper.sWord = sFirst; mapper.addCat(endstr, cat); htChildren.Add(mapper.sWord, mapper); } }
/// <summary> /// deals with the "gender" tag /// </summary> /// <param name="thisNode">the "gender" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> private static string gender(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { string sNode=thisNode.InnerText; if (sNode=="") { string sContent="<star/>"; XmlDocument tempDoc = new XmlDocument(); tempDoc.LoadXml(sContent); XmlNode myNode=tempDoc.SelectSingleNode("star"); sNode=star(myNode, thisCat, myBot, sUserID); } string sOutput=(string)cNormalizer.substitute(cGlobals.getGenderSubs(),sNode); return (string)sOutput.ToLower(); }
/// <summary> /// deals with the "get" tag /// </summary> /// <param name="thisNode">the "get" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string getTag(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return a blank if (myAttributes.Count==0) { return ""; } // o.k. we have an attribute to process // get the value associated with the "name" attribute string sName=thisNode.Attributes["name"].Value; cUser myUser = (cUser)cUsers.users[sUserID]; string sValue=(string)myUser.Predicates[sName]; if (sValue==null) sValue=""; return sValue.Trim(); }
/// <summary> /// deals with the "srai" tag /// </summary> /// <param name="thisNode">the "srai" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string srai(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { string sNode=thisNode.InnerText; cResponse myReply = myBot.chat(sNode, sUserID); string sReply=""; foreach(string sSentence in myReply.alOutput) { sReply+=sSentence+" "; } return sReply; }
/// <summary> /// deals with the "bot" tag /// </summary> /// <param name="thisNode">the "bot" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string bot(XmlNode thisNode, cCategory thisCat, cBot myBot, string sUserID) { // get the attributes for this tag XmlAttributeCollection myAttributes = thisNode.Attributes; // no attributes? then just return a blank if (myAttributes.Count==0) { return ""; } // o.k. we have an attribute to process // get the value associated with the "name" attribute string sName=thisNode.Attributes["name"].Value; return (string)myBot.getPredicate(sName); }
/// <summary> /// processes the individual aiml tags found within the "target" portion of a category /// </summary> /// <param name="thisNode">the xml node to be processed</param> /// <param name="InputCat">the category that the node is a part of</param> /// <param name="myBot">the bot (doh!)</param> /// <param name="sUserID">id's the user (what did you think it did?)</param> /// <returns></returns> private static string processtag(XmlNode thisNode, cCategory InputCat, cBot myBot, string sUserID) { // to be returned... string sOutput=""; // holds the inner text from the child nodes in case they need to be displayed string sChildInnerText=""; // get the name of the node string sNodeName=thisNode.Name.ToUpper(); // check for children and process them if((thisNode.HasChildNodes)&((sNodeName!="THINK")&(sNodeName!="RANDOM"))) { XmlNodeList TagChildren=thisNode.ChildNodes; foreach(XmlNode Child in TagChildren) { sChildInnerText+=processtag(Child, InputCat,myBot,sUserID); } thisNode.InnerText=sChildInnerText; } // do the on this particular node switch(sNodeName) { case "RANDOM": sOutput+= random(thisNode, InputCat, myBot, sUserID); break; case "SIZE": { sOutput+= Convert.ToString(myBot.Size); break; } case "ID": sOutput+= (string)sUserID; break; case "DATE": sOutput+= (string)DateTime.Now.ToString(); break; case "VERSION": sOutput+= cGlobals.VERSION; break; case "STAR": sOutput+= " "+star(thisNode, InputCat, myBot, sUserID)+" "; break; case "THAT": sOutput+= " "+that(thisNode, InputCat, myBot, sUserID)+" "; break; case "INPUT": sOutput+= " "+input(thisNode, InputCat, myBot, sUserID)+" "; break; case "THATSTAR": sOutput+= " "+thatstar(thisNode, InputCat, myBot, sUserID)+" "; break; case "TOPICSTAR": sOutput+= " "+topicstar(thisNode, InputCat, myBot, sUserID)+" "; break; case "GET": sOutput+= " "+getTag(thisNode, InputCat, myBot, sUserID)+" "; break; case "BOT": sOutput+= " "+bot(thisNode, InputCat, myBot, sUserID)+" "; break; case "UPPERCASE": sOutput+= upper(thisNode, InputCat); break; case "LOWERCASE": sOutput+= lower(thisNode, InputCat); break; case "FORMAL": sOutput+= formal(thisNode, InputCat); break; case "SENTENCE": sOutput+= sentence(thisNode, InputCat); break; case "SR": sOutput+= sr(thisNode, InputCat, myBot, sUserID); break; /*case "CONDITION": outputCategory.sTemplate+= func(thisNode, InputCat); break;*/ case "SET": sOutput+= setTag(thisNode, InputCat, myBot, sUserID); break; case "GOSSIP": // Not really important so implement at a later date when some // use for it is found break; case "SRAI": sOutput+= srai(thisNode, InputCat, myBot, sUserID); break; case "PERSON": sOutput+= person(thisNode, InputCat, myBot, sUserID); break; case "PERSON2": sOutput+= person2(thisNode, InputCat, myBot, sUserID); break; case "GENDER": sOutput+= gender(thisNode, InputCat, myBot, sUserID); break; case "THINK": think(thisNode, InputCat, myBot, sUserID); break; /*case "LEARN": outputCategory.sTemplate+= func(thisNode, InputCat); break; case "SYSTEM": outputCategory.sTemplate+= func(thisNode, InputCat); break; case "JAVASCRIPT": outputCategory.sTemplate+= func(thisNode, InputCat); break; /*case "": outputCategory.sTemplate+= func(thisNode, InputCat); break; case "": outputCategory.sTemplate+= func(thisNode, InputCat); break; case "": outputCategory.sTemplate+= func(thisNode, InputCat); break; ...add modifications here*/ default: sOutput+=thisNode.InnerText.Trim(); break; } return " "+sOutput; }
/// <summary> /// deals with the "lower" tag /// </summary> /// <param name="thisNode">the "lower" node in question</param> /// <param name="thisCat">the current category</param> /// <param name="myBot">the bot whose graphmaster returned the "template"</param> /// <param name="sUserID">the user who requires a reply</param> /// <returns>the string that results in the processing of this node</returns> private static string lower(XmlNode thisNode, cCategory thisCat) { return((string)thisNode.InnerText.ToLower()); }
/// <summary> /// Searches the nodemapper with the input string /// </summary> /// <param name="sInput">Normalised input concatenated with "that" and "topic" parameters</param> /// <param name="iMatchState">Denotes what part of the input path this node is a part of: /// 0=raw input /// 1="that" /// 2="topic" /// Used when pushing values represented by wildcards onto ArrayLists for /// the star, thatstar and topicstar AIML values</param> /// <param name="sWildcard">contents of the user input absorbed by the AIML wildcards "_" and "*"</param> /// <returns>the appropriate cCategory for the given input</returns> public cCategory evaluate(string sInput, int iMatchState, string sWildcard) { // make sure the input string is nice and tidy string sInputTrim = sInput.Trim(); // o.k. if this is the end of a branch in the GraphMaster // return the cCategory for this node if (thisCategory != null && htChildren.Count == 0) { cCategory tempCat; if (thisCategory!=null) { tempCat=new cCategory(thisCategory); tempCat.filename=thisCategory.filename; cGlobals.writeLog("Category found using path: "+thisCategory.sPath+"\r\nAnd with this template: "+thisCategory.sTemplate+"\r\nFrom this file: "+thisCategory.filename); } else tempCat=null; return tempCat; } // if we've matched all the words in the input sentence and this is the end // of the line then return the cCategory for this node if (sInputTrim.Length == 0) { cCategory tempCat; if (thisCategory!=null) { tempCat=new cCategory(thisCategory); tempCat.filename=thisCategory.filename; } else tempCat=null; return tempCat; } // otherwise split the input into it's component words string[] sWords = sInputTrim.Split(" \r\n\t".ToCharArray()); // get the first word of the sentence string sFirstword; if ((sWords[0]=="<that>")||(sWords[0]=="<topic>")||(iMatchState>0)) { sFirstword=sWords[0]; } else { sFirstword = sWords[0].ToUpper(); } // and concatenate the rest of the input into a suffix string string sMessagesuffix = sInputTrim.Substring(sFirstword.Length, sInputTrim.Length - sFirstword.Length); // first option is to see if this nodemapper contains a child denoted by the "_" // wildcard. "_" comes first in precedence in the AIML alphabet if (htChildren.ContainsKey("_")) { // o.k. look for the path in the child nodemapper denoted by "_" cNodeMapper LeafNode = (cNodeMapper)htChildren["_"]; // Aha! we've found the right cNodeMapper (don't you just love it when stuff works!) if (LeafNode != null) { // move down into the identified branch of the GraphMaster structure cCategory tempcat = LeafNode.evaluate(sMessagesuffix, iMatchState, sWords[0]); // and if we get a result from the branch process and return it if (tempcat != null) { // capture and push the star content appropriate to the current matchstate switch(iMatchState) { case 0: if (sWildcard.Length>0) { tempcat.alInputStar.Insert(0, sWildcard); } break; case 1: if (sWildcard.Length>0) { tempcat.alThatStar.Insert(0, sWildcard); } break; case 2: if (sWildcard.Length>0) { tempcat.alTopicStar.Insert(0, sWildcard); } break; } return tempcat; } } } // second option - the nodemapper may have contained a "_" child, but led to no match // or it didn't contain a "_" child at all. So get the child nodemapper from this // nodemapper that matches the first word of the input sentence. if (htChildren.ContainsKey(sFirstword)) { // make sure we're not changing the iMatchState (encountering <that> or <topic> tags) if (sFirstword=="<that>") iMatchState=1; else if (sFirstword=="<topic>") iMatchState=2; else if ((iMatchState==2)&(sMessagesuffix.Length==0)) iMatchState=3; cNodeMapper LeafNode = (cNodeMapper)htChildren[sFirstword]; // Aha! we've found the right cNodeMapper if (LeafNode != null) { // move down into the identified branch of the GraphMaster structure cCategory tempcat = LeafNode.evaluate(sMessagesuffix, iMatchState,""); // and if we get a result from the branch return it if (tempcat != null) { // capture and push the star content appropriate to the PREVIOUS matchstate switch(iMatchState) { case 0: if (sWildcard.Length>0) { tempcat.alInputStar.Insert(0, sWildcard); } break; case 1: if (sWildcard.Length>0) { tempcat.alInputStar.Insert(0, sWildcard); } break; case 2: if (sWildcard.Length>0) { tempcat.alThatStar.Insert(0, sWildcard); } break; case 3: if (sWildcard.Length>0) { tempcat.alTopicStar.Insert(0, sWildcard); } break; } return tempcat; } } } // third option - the input part of the path might have been matched so far but hasn't // returned a match, so check to see it contains the "*" wildcard. "*" comes last in // precedence in the AIML alphabet. if (htChildren.ContainsKey("*")) { // o.k. look for the path in the child nodemapper denoted by "*" cNodeMapper LeafNode = (cNodeMapper)htChildren["*"]; // Aha! we've found the right cNodeMapper if (LeafNode != null) { // move down into the identified branch of the GraphMaster structure cCategory tempcat = LeafNode.evaluate(sMessagesuffix, iMatchState, sWords[0]); // and if we get a result from the branch process and return it if (tempcat != null) { // capture and push the star content appropriate to the current matchstate switch(iMatchState) { case 0: if (sWildcard.Length>0) { tempcat.alInputStar.Insert(0, sWildcard); } break; case 1: if (sWildcard.Length>0) { tempcat.alThatStar.Insert(0, sWildcard); } break; case 2: if (sWildcard.Length>0) { tempcat.alTopicStar.Insert(0, sWildcard); } break; } return tempcat; } } } // o.k. if the nodemapper has failed to match at all: the input contains neither // a "_", the sFirstWord text, or "*" as a means of denoting a child node. However, // if this node is itself representing a wildcard then the search continues to be // valid if we proceed with the tail. if ((sWord=="_")||(sWord=="*")) { return this.evaluate(sMessagesuffix, iMatchState, sWildcard+" "+sFirstword); } // If we get here then we're at a dead end so return a null. Hopefully, if the // AIML files have been set up to include a "* <that> * <topic> *" catch-all this // state won't be reached. return null; }
/// <summary> /// Searches the nodemapper with the input string /// </summary> /// <param name="sInput">Normalised input concatenated with "that" and "topic" parameters</param> /// <param name="iMatchState">Denotes what part of the input path this node is a part of: /// 0=raw input /// 1="that" /// 2="topic" /// Used when pushing values represented by wildcards onto ArrayLists for /// the star, thatstar and topicstar AIML values</param> /// <param name="sWildcard">contents of the user input absorbed by the AIML wildcards "_" and "*"</param> /// <returns>the appropriate cCategory for the given input</returns> public cCategory evaluate(string sInput, int iMatchState, string sWildcard) { // make sure the input string is nice and tidy string sInputTrim = sInput.Trim(); // o.k. if this is the end of a branch in the GraphMaster // return the cCategory for this node if (thisCategory != null && htChildren.Count == 0) { cCategory tempCat; if (thisCategory != null) { tempCat = new cCategory(thisCategory); tempCat.filename = thisCategory.filename; cGlobals.writeLog("Category found using path: " + thisCategory.sPath + "\r\nAnd with this template: " + thisCategory.sTemplate + "\r\nFrom this file: " + thisCategory.filename); } else { tempCat = null; } return(tempCat); } // if we've matched all the words in the input sentence and this is the end // of the line then return the cCategory for this node if (sInputTrim.Length == 0) { cCategory tempCat; if (thisCategory != null) { tempCat = new cCategory(thisCategory); tempCat.filename = thisCategory.filename; } else { tempCat = null; } return(tempCat); } // otherwise split the input into it's component words string[] sWords = sInputTrim.Split(" \r\n\t".ToCharArray()); // get the first word of the sentence string sFirstword; if ((sWords[0] == "<that>") || (sWords[0] == "<topic>") || (iMatchState > 0)) { sFirstword = sWords[0]; } else { sFirstword = sWords[0].ToUpper(); } // and concatenate the rest of the input into a suffix string string sMessagesuffix = sInputTrim.Substring(sFirstword.Length, sInputTrim.Length - sFirstword.Length); // first option is to see if this nodemapper contains a child denoted by the "_" // wildcard. "_" comes first in precedence in the AIML alphabet if (htChildren.ContainsKey("_")) { // o.k. look for the path in the child nodemapper denoted by "_" cNodeMapper LeafNode = (cNodeMapper)htChildren["_"]; // Aha! we've found the right cNodeMapper (don't you just love it when stuff works!) if (LeafNode != null) { // move down into the identified branch of the GraphMaster structure cCategory tempcat = LeafNode.evaluate(sMessagesuffix, iMatchState, sWords[0]); // and if we get a result from the branch process and return it if (tempcat != null) { // capture and push the star content appropriate to the current matchstate switch (iMatchState) { case 0: if (sWildcard.Length > 0) { tempcat.alInputStar.Insert(0, sWildcard); } break; case 1: if (sWildcard.Length > 0) { tempcat.alThatStar.Insert(0, sWildcard); } break; case 2: if (sWildcard.Length > 0) { tempcat.alTopicStar.Insert(0, sWildcard); } break; } return(tempcat); } } } // second option - the nodemapper may have contained a "_" child, but led to no match // or it didn't contain a "_" child at all. So get the child nodemapper from this // nodemapper that matches the first word of the input sentence. if (htChildren.ContainsKey(sFirstword)) { // make sure we're not changing the iMatchState (encountering <that> or <topic> tags) if (sFirstword == "<that>") { iMatchState = 1; } else if (sFirstword == "<topic>") { iMatchState = 2; } else if ((iMatchState == 2) & (sMessagesuffix.Length == 0)) { iMatchState = 3; } cNodeMapper LeafNode = (cNodeMapper)htChildren[sFirstword]; // Aha! we've found the right cNodeMapper if (LeafNode != null) { // move down into the identified branch of the GraphMaster structure cCategory tempcat = LeafNode.evaluate(sMessagesuffix, iMatchState, ""); // and if we get a result from the branch return it if (tempcat != null) { // capture and push the star content appropriate to the PREVIOUS matchstate switch (iMatchState) { case 0: if (sWildcard.Length > 0) { tempcat.alInputStar.Insert(0, sWildcard); } break; case 1: if (sWildcard.Length > 0) { tempcat.alInputStar.Insert(0, sWildcard); } break; case 2: if (sWildcard.Length > 0) { tempcat.alThatStar.Insert(0, sWildcard); } break; case 3: if (sWildcard.Length > 0) { tempcat.alTopicStar.Insert(0, sWildcard); } break; } return(tempcat); } } } // third option - the input part of the path might have been matched so far but hasn't // returned a match, so check to see it contains the "*" wildcard. "*" comes last in // precedence in the AIML alphabet. if (htChildren.ContainsKey("*")) { // o.k. look for the path in the child nodemapper denoted by "*" cNodeMapper LeafNode = (cNodeMapper)htChildren["*"]; // Aha! we've found the right cNodeMapper if (LeafNode != null) { // move down into the identified branch of the GraphMaster structure cCategory tempcat = LeafNode.evaluate(sMessagesuffix, iMatchState, sWords[0]); // and if we get a result from the branch process and return it if (tempcat != null) { // capture and push the star content appropriate to the current matchstate switch (iMatchState) { case 0: if (sWildcard.Length > 0) { tempcat.alInputStar.Insert(0, sWildcard); } break; case 1: if (sWildcard.Length > 0) { tempcat.alThatStar.Insert(0, sWildcard); } break; case 2: if (sWildcard.Length > 0) { tempcat.alTopicStar.Insert(0, sWildcard); } break; } return(tempcat); } } } // o.k. if the nodemapper has failed to match at all: the input contains neither // a "_", the sFirstWord text, or "*" as a means of denoting a child node. However, // if this node is itself representing a wildcard then the search continues to be // valid if we proceed with the tail. if ((sWord == "_") || (sWord == "*")) { return(this.evaluate(sMessagesuffix, iMatchState, sWildcard + " " + sFirstword)); } // If we get here then we're at a dead end so return a null. Hopefully, if the // AIML files have been set up to include a "* <that> * <topic> *" catch-all this // state won't be reached. return(null); }