/// <summary> /// Find the node matching the string, if any. /// </summary> /// <param name="string"> The string to look for. </param> public virtual LetterNode TerminalNode(string @string) { char key = Convert.ToChar(@string[0]); LetterNode startNode = null; if (!_words.ContainsKey(key)) { return(null); } else { startNode = _words[key]; } LetterNode node = startNode; for (int i = 1; i < @string.Length; i++) { node = node.Child(@string[i]); if (node == null) { break; } } return(node); }
/// <summary> /// Find a forced win, if available. A forced win occurs from a /// particular node when the all of the Children of a child node are either /// terminals or forced wins. /// </summary> /// <param name="node"> The node from which to find a forced win. </param> public virtual LetterNode ForcedWin(LetterNode node) { LetterNode winningChild = null; foreach (LetterNode child in node.Children().Values) { if (!child.LeafNode) { winningChild = child; foreach (LetterNode grandChild in child.Children().Values) { if (!(grandChild.LeafNode || (ForcedWin(grandChild) != null))) { winningChild = null; break; } } } if (winningChild != null) { break; } } return(winningChild); }
/// <summary> /// Determine if the string is the start of a word in the dictionary. /// </summary> /// <param name="stem"> The substring to look for. </param> public virtual bool IsWordStem(string stem) { bool isWordStem = false; LetterNode node = TerminalNode(stem); if (node != null) { isWordStem = true; } return(isWordStem); }
/// <summary> /// Determine if the word is in the dictionary. /// </summary> /// <param name="word"> The word to look for. </param> public virtual bool IsFullWord(string word) { bool isFullWord = false; LetterNode node = TerminalNode(word); if ((node != null) && (node.LeafNode)) { isFullWord = true; } return(isFullWord); }
/// <summary> /// Find the longest word reachable from the specified node. /// </summary> /// <param name="node"> The node from which to find the longest word. </param> public virtual LetterNode LongestWord(LetterNode node) { LetterNode longestChild = null; foreach (LetterNode child in node.Children().Values) { if ((longestChild == null) || (child.MaxLength() > longestChild.MaxLength())) { longestChild = child; } } return(longestChild); }
/// <summary> /// A test harness for the LetterNode class. /// </summary> /// <param name="args"> The command line arguments passed in. </param> public static void TestLetterNode(string[] args) { if (args.Length > 0) { LetterNode node = new LetterNode(args[0]); for (int i = 1; i < args.Length; i++) { node.AddWord(args[i]); } Console.Write("Total words = " + node.LeafNodeCount()); Console.Write("Maximum word length = " + node.MaxLength()); } else { Console.Write("Usage: GhostGame " + typeof(LetterNode).FullName + " <word-list>"); } }
/// <summary> /// Get the player's next letter. This method is inherited from /// GhostPlayer. /// </summary> /// <param name="wordInPlay"> The word currently being played. </param> public virtual char Play(string wordInPlay) { char nextLetter = 'x'; LetterNode node = _dictionary.TerminalNode(wordInPlay); if (node != null) { LetterNode forcedWin = ForcedWin(node); if (forcedWin != null) { nextLetter = forcedWin.Letter(); } else { nextLetter = LongestWord(node).Letter(); } } return(nextLetter); }
/// <summary> /// Add a word to the dictionary if it meets two criteria: /// <ul> /// <li> The word must be at least MIN_WORD_LENGTH letters long. /// <li> The word must not start with another valid word. /// </ul> /// The second criteria is enforced by the LetterNode class. /// </summary> /// <param name="word"> The word to add to the dictionary. </param> private void AddWord(string word) { if (word.Length >= MIN_WORD_LENGTH) { char key = Convert.ToChar(word[0]); LetterNode node = null; if (_words.ContainsKey(key)) { node = _words[key]; } if (node == null) { _words[key] = new LetterNode(word); } else { node.AddWord(word); } } }
/// <summary> /// Add a word to the tree rooted at this node. This method implements /// the Ghost rule that no word may begin with another word. That /// requires two rules: /// /// - If this node has no child nodes, the passed word begins with /// this word and must not be added. /// - If the passed word has a length of zero, this is the end of the /// word and all child nodes must be deleted. /// </summary> /// <param name="word"> The word, or substring, to add to this node. </param> public void AddWord(string word) { if (word[0] == _letter) { if (_nodes.Count > 0) { if (word.Length == 1) { _nodes.Clear(); } else { char key = Convert.ToChar(word[1]); LetterNode nextNode = null; if (_nodes.ContainsKey(key)) { nextNode = _nodes[key]; } if (nextNode == null) { _nodes[key] = new LetterNode(word.Substring(1)); } else { nextNode.AddWord(word.Substring(1)); } } } } else { throw new ArgumentException("Invalid first letter."); } }