/// <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.");
            }
        }