Пример #1
0
        /// <summary>
        /// Expands a selected node, if possible.
        /// </summary>
        /// <param name="id">The selected node.</param>
        /// <param name="tree">The game tree object.</param>
        /// <returns></returns>
        private static int Expand(int id, GameTree tree)
        {
            // Get the node object that corresponds with the input ID.
            GameTreeNode node = tree.GetNode(id);

            // If the node has at least one unplayed child, is not a dead end, and is not a goal state.
            if (node.Unplayed.Count > 0 && !node.DeadEnd && !node.IsGoal)
            {
                // Choose an unplayed child at random.
                GameTreeEdge outgoing = node.Unplayed.PickRandom <GameTreeEdge>();

                // Remove the child from the list of unplayed edges.
                node.Unplayed.Remove(outgoing);

                // Create the node object and store the child's ID.
                int child = tree.CreateNode(node.Domain, node.Problem, outgoing).ID;

                // Save the parent node to disk.
                tree.SetNode(node);

                // Return the child ID.
                return(child);
            }

            // Return the leaf node.
            return(node.ID);
        }
Пример #2
0
        /// <summary>
        /// The base constructor.
        /// </summary>
        /// <param name="domain">The base domain object.</param>
        /// <param name="problem">The base problem object.</param>
        /// <param name="path">The path used to save files to disk.</param>
        public GameTree(Domain domain, Problem problem, string path)
        {
            // Store the domain, problem, and path.
            this.domain  = domain;
            this.problem = problem;
            this.path    = path;

            // Store the player's name.
            player = problem.Player.ToLower();

            // Get and store the NPC names.
            npcs = GetNPCs();

            // Establish the turn order.
            turnOrder = GetTurnOrder();

            // Create a new hashtable to store tree edges.
            tree = new Hashtable();

            // Initialize the session counters.
            nodeCounter    = 0;
            totalPlays     = 0;
            lowestDepth    = 0;
            goalStateCount = 0;
            deadEndCount   = 0;
            totalWins      = 0;
            totalLosses    = 0;

            // Create the tree's root node.
            GameTreeNode root = CreateNode(domain, problem, null);
        }
Пример #3
0
        /// <summary>
        /// Calculate and return the interval of the current node.
        /// </summary>
        /// <param name="node">The current node object.</param>
        /// <returns></returns>
        public double GetInterval(GameTreeNode node)
        {
            // If the node has been played before and is not a dead end.
            if (node.TimesPlayed > 0 && !node.DeadEnd)
            {
                // Calculate and return the node's interval.
                return(((double)node.Wins / (double)node.TimesPlayed) + Math.Sqrt((2 * Math.Log(TotalPlays)) / (double)node.TimesPlayed));
            }

            // Otherwise, return the error value.
            return(-1);
        }
Пример #4
0
        /// <summary>
        /// Creates a new node object.
        /// </summary>
        /// <param name="domain">The node's domain.</param>
        /// <param name="problem">The node's problem.</param>
        /// <param name="incoming">The node's incoming edge.</param>
        /// <returns></returns>
        public GameTreeNode CreateNode(Domain domain, Problem problem, GameTreeEdge incoming)
        {
            // Create a placeholder for the new node object.
            GameTreeNode node = null;

            // If the node is a root, initialize a root node.
            if (incoming == null)
            {
                node = new GameTreeNode(domain, problem, 0);
            }
            // Otherwise, it is a child node...
            else
            {
                // Store the current node's ID in the incoming edge.
                incoming.Child = ++nodeCounter;

                // Create the new node object.
                node = new GameTreeNode(domain, problem, incoming, GetSuccessorState(incoming), incoming.Child, GetDepth(incoming.Parent) + 1);

                // Add the edge to the tree hashtable.
                tree.Add(incoming.Child, incoming.Parent);
            }

            // If the node is a goal state, iterate the goal state counter.
            if (node.IsGoal)
            {
                goalStateCount++;
            }

            // If the node is at a lower depth than the previous record holder, update the depth counter.
            if (node.Depth > lowestDepth)
            {
                lowestDepth = node.Depth;
            }

            // Find and store the node's outgoing edges.
            node.Outgoing = GetOutgoingEdges(node, GetCurrentTurn(node));

            // Iterate through the node's outgoing edges.
            foreach (GameTreeEdge edge in node.Outgoing)
            {
                // And add each of them to the unplayed collection.
                node.Unplayed.Add(edge);
            }

            // Save the current node to disk.
            SetNode(node);

            // Return the current node object.
            return(node);
        }
Пример #5
0
        /// <summary>
        /// Returns a list of outgoing edges for a given node.
        /// </summary>
        /// <param name="node">The node object.</param>
        /// <param name="actor">The name of the current actor.</param>
        /// <returns>A list of outgoing edges.</returns>
        public List <GameTreeEdge> GetOutgoingEdges(GameTreeNode node, string actor)
        {
            // Initialize the list of outgoing edges.
            List <GameTreeEdge> outgoing = new List <GameTreeEdge>();

            // Iterate through the actions enabled for the input actor in the current node's state.
            foreach (Operator action in StateSpaceTools.GetActions(actor, node.Domain, node.Problem, node.State))
            {
                // Add an outgoing edge for each of these actions to the list.
                outgoing.Add(new GameTreeEdge(action, node.ID));
            }

            // Return the list of outgoing edges.
            return(outgoing);
        }
Пример #6
0
        /// <summary>
        /// Simulates a playout from the current node.
        /// </summary>
        /// <param name="id">The current node's ID.</param>
        /// <returns>Whether the playout was won or lost.</returns>
        public bool Simulate(int id)
        {
            // Read the node's object from disk.
            GameTreeNode node = GetNode(id);

            // Iterate the total plays.
            totalPlays++;

            // Initialize a dead end check.
            bool check = false;

            // If the node is not a dead end before it is played, we need to check.
            if (!node.DeadEnd)
            {
                check = true;
            }

            // Play a game and store the result.
            bool result = node.Play();

            // Store a win.
            if (result)
            {
                totalWins++;
            }
            // Otherwise, store a loss.
            else
            {
                totalLosses++;
            }

            // If we found a new dead end, record it.
            if (check && node.DeadEnd)
            {
                deadEndCount++;
            }

            // Save the node to disk.
            SetNode(node);

            // Return the result.
            return(result);
        }
Пример #7
0
        /// <summary>
        /// Selects a leaf node to expand.
        /// </summary>
        /// <param name="id">The ID of the current node.</param>
        /// <param name="tree">The game tree object.</param>
        /// <returns></returns>
        private static int Select(int id, GameTree tree)
        {
            // Get the node object that corresponds to the current ID.
            GameTreeNode node = tree.GetNode(id);

            // If the node has not been played before, has at least one outgoing edge, is not a dead end, and is not a goal state...
            if (node.Unplayed.Count == 0 && node.Outgoing.Count > 0 && !node.DeadEnd && !node.IsGoal)
            {
                // Create a variable to store the child with the highest interval.
                int highestChild = -1;

                // If it's not the player's turn...
                if (!tree.TurnOrder[node.Depth % tree.TurnOrder.Count].Equals(tree.Player))
                {
                    // Set the highest child to be the first child.
                    highestChild = node.Outgoing[0].Child;

                    // Loop through every remaining child.
                    for (int i = 1; i < node.Outgoing.Count; i++)
                    {
                        // If the current child has a higher interval than the stored child.
                        if (tree.GetInterval(node.Outgoing[i].Child) > tree.GetInterval(highestChild))
                        {
                            // Store the current child.
                            highestChild = node.Outgoing[i].Child;
                        }
                    }
                }
                // If it's the player's turn, chose an outgoing edge at random.
                else
                {
                    highestChild = node.Outgoing.PickRandom <GameTreeEdge>().Child;
                }

                // Recursively call this method with the selected child.
                return(Select(highestChild, tree));
            }

            // Return the leaf node.
            return(node.ID);
        }
Пример #8
0
        /// <summary>
        /// Propagates a roll out's result back through the tree.
        /// </summary>
        /// <param name="result">Whether the roll out resulted in a win or loss.</param>
        /// <param name="node">The current node.</param>
        /// <param name="tree">The game tree object.</param>
        private static void Propagate(bool result, int node, GameTree tree)
        {
            // Get the current node's parent ID.
            int parent = tree.GetParent(node);

            // Loop until we hit the root node's null parent link.
            while (parent != -1)
            {
                // Get the parent's node object from the game tree.
                GameTreeNode parentNode = tree.GetNode(parent);

                // Add the win/loss to the parent's node object.
                parentNode.AddResult(result);

                // Save the parent's node object to disk.
                tree.SetNode(parentNode);

                // Set the parent ID to the grandparent ID.
                parent = tree.GetParent(parent);
            }
        }
Пример #9
0
 /// <summary>
 /// Write a node object to disk.
 /// </summary>
 /// <param name="node">The node's ID.</param>
 public void SetNode(GameTreeNode node)
 {
     // Given the path and ID, write the node object to disk.
     BinarySerializer.SerializeObject <GameTreeNode>(path + node.ID, node);
 }
Пример #10
0
 /// <summary>
 /// Get the current turn taker.
 /// </summary>
 /// <param name="node">The current node.</param>
 /// <returns>The name of the character who gets to act.</returns>
 private string GetCurrentTurn(GameTreeNode node)
 {
     // Returns the character who gets to act at this level.
     return(turnOrder[node.Depth % turnOrder.Count]);
 }