Пример #1
0
    /// <summary>
    /// Assigns a heuristic minimax score to the given node
    /// </summary>
    /// <param name="node">node to mark with score</param>
    /// <param name="maximizing">whether or not we're maximizing</param>
    void AssignHeuristicMinimaxScore(
        MinimaxTreeNode <Configuration> node,
        bool maximizing)
    {
        Configuration existingConfig = node.Value;

        // might have reached an end-of-game configuration
        if (existingConfig.Empty)
        {
            AssignEndOfGameMinimaxScore(node, maximizing);
        }
        // Player will select all except one bear if the board has one non-empty bin with many bears
        else if (existingConfig.NonEmptyBins.Count == 1 && existingConfig.NumBears > 1)
        {
            // Player A/B will win the game by leaving out one bear for Player B/A
            AssignEndOfGameMinimaxScore(node, maximizing);
        }
        // Player will select all bears in the other bin if board has two non-empty bins with one bin having exactly one bear
        else if (existingConfig.NonEmptyBins.Count == 2 && existingConfig.NonEmptyBins.Contains(1))
        {
            AssignEndOfGameMinimaxScore(node, maximizing);
        }
        else
        {
            // use a heuristic evaluation function to score the node
            node.MinimaxScore = 0.5f;
        }
    }
Пример #2
0
    /// <summary>
    /// Handles the thinking timer finishing
    /// </summary>
    void HandleThinkingTimerFinished()
    {
        // Timer has finished so now the player needs to pick the
        // best child node

        // do the search and pick the move
        Minimax(tree.Root, true);

        // now we are looking for the configuration
        // that has the maximum score

        // find child node with maximum score
        // get all immediate children of root
        IList <MinimaxTreeNode <Configuration> > children =
            tree.Root.Children;
        // pick a random child (e.g. first)
        MinimaxTreeNode <Configuration> maxChildNode = children[0];

        // iterate over children to find one with largest minimax score
        for (int i = 1; i < children.Count; i++)
        {
            if (children[i].MinimaxScore > maxChildNode.MinimaxScore)
            {
                maxChildNode = children[i];
            }
        }

        // chosen child is probably the best node to pick

        // provide new configuration (obtained from child node)
        // as second argument
        turnOverEvent.Invoke(myName, maxChildNode.Value);
    }
Пример #3
0
        static void Main(string[] args)
        {
            var gameTree = new Tree <MinimaxTreeNode <int> >
            {
                Root = new MinimaxTreeNode <int>("A", true)
            };

            var minNode1 = new MinimaxTreeNode <int>("B", false)
                           .AddChildNode(3)
                           .AddChildNode(12)
                           .AddChildNode(8);

            gameTree.Root.Nodes.Add(minNode1);

            var minNode2 = new MinimaxTreeNode <int>("C", false)
                           .AddChildNode(2)
                           .AddChildNode(4)
                           .AddChildNode(6);

            gameTree.Root.Nodes.Add(minNode2);

            var minNode3 = new MinimaxTreeNode <int>("D", false)
                           .AddChildNode(14)
                           .AddChildNode(5)
                           .AddChildNode(2);

            gameTree.Root.Nodes.Add(minNode3);

            CalculateNodesValues(gameTree.Root);
            Console.ReadKey();
        }
        protected override int EstimationFunction(MinimaxTreeNode node)
        {
            // winning condition for me
            if (WhoHasMoreBoxes(node, out int blue, out int red) == whoAmI && node.NonExistingLines.Count == 0)
            {
                return(int.MaxValue);
            }

            List <Box> newBoxes = AICommon.TryClosingBoxes(node.ExistingLines,
                                                           (node.Player == MinimaxPlayerType.MAX ? Player.BLUE : Player.RED),
                                                           node.DeltaMove,
                                                           out int[] surroundingEdges);

            // select state in which user can close boxes
            if (newBoxes.Count > 0)
            {
                return(int.MaxValue - 1);
            }

            // this state could lead opponent to close the box, hence negative estimation
            if (surroundingEdges[0] == 2 && surroundingEdges[1] == 2)
            {
                return(-2);
            }
            else if (surroundingEdges[0] == 2 || surroundingEdges[1] == 2)
            {
                return(-1);
            }

            // state is neutral for result for the provided tree depth
            return(0);
        }
Пример #5
0
    /// <summary>
    /// Builds the tree
    /// </summary>
    /// <param name="boardConfiguration">current board configuration</param>
    /// <returns>tree</returns>
    MinimaxTree <Configuration> BuildTree(
        Configuration boardConfiguration)
    {
        // build tree to appropriate depth
        MinimaxTree <Configuration> tree =
            new MinimaxTree <Configuration>(boardConfiguration);

        nodeList.Clear();
        nodeList.AddLast(tree.Root);
        while (nodeList.Count > 0)
        {
            MinimaxTreeNode <Configuration> currentNode =
                nodeList.First.Value;
            nodeList.RemoveFirst();
            List <Configuration> children =
                GetNextConfigurations(currentNode.Value);
            foreach (Configuration child in children)
            {
                // STUDENTS: only add to tree if within search depth

                MinimaxTreeNode <Configuration> childNode =
                    new MinimaxTreeNode <Configuration>(
                        child, currentNode);
                if (Level(childNode) <= searchDepth)
                {
                    tree.AddNode(childNode);
                    nodeList.AddLast(childNode);
                }
            }
        }
        return(tree);
    }
Пример #6
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="value">value for the node</param>
 /// <param name="parent">parent for the node</param>
 public MinimaxTreeNode(T value, MinimaxTreeNode <T> parent)
 {
     this.value  = value;
     this.parent = parent;
     children    = new List <MinimaxTreeNode <T> >();
     this.depth  = parent != null ? parent.Depth + 1 : 0;
 }
Пример #7
0
    /// <summary>
    /// Assigns a heuristic minimax score to the given node
    /// </summary>
    /// <param name="node">node to mark with score</param>
    /// <param name="maximizing">whether or not we're maximizing</param>
    void AssignHeuristicMinimaxScore(MinimaxTreeNode <Configuration> node,
                                     bool maximizing)
    {
        // might have reached an end-of-game configuration
        if (node.Value.Empty)
        {
            AssignEndOfGameMinimaxScore(node, maximizing);
        }
        else
        {
            // use a heuristic evaluation function to score the node

            // if the goal is to maximize, the value of the next play
            // it will decrease accoding to the number of teddies in
            // the bins.
            // This will affect more at the very end of the game and
            // not much at the begining as taking all teddy bears
            // from the last bin is a bad play.

            int heuristic = 0;
            foreach (int quantity in node.Value.Bins)
            {
                heuristic += maximizing ? quantity * -1 : quantity;
            }
            node.MinimaxScore = heuristic;
        }
    }
Пример #8
0
    /// <summary>
    /// Builds the tree
    /// </summary>
    /// <param name="boardConfiguration">current board configuration</param>
    /// <returns>tree</returns>
    MinimaxTree <Configuration> BuildTree(
        Configuration boardConfiguration)
    {
        // build tree to appropriate depth
        MinimaxTree <Configuration> tree =
            new MinimaxTree <Configuration>(boardConfiguration);

        // clear the list of nodes
        nodeList.Clear();

        // add the root as the initial node
        nodeList.AddLast(tree.Root);

        // iterate over all nodes while there are nodes to process
        while (nodeList.Count > 0)
        {
            // get the currentNode
            MinimaxTreeNode <Configuration> currentNode =
                nodeList.First.Value;

            // remove it from the list of nodes (to avoid processing it again)
            nodeList.RemoveFirst();

            // Get all possible configurations that might result from
            // from the one for the current node (as long as they
            // contain bears).
            // These configurations represent possible child nodes of this node.
            List <Configuration> possibleConfigurations =
                GetNextConfigurations(currentNode.Value);

            // get the depth of this node
            int childDepth = getChildDepth(currentNode);

            // if we are still within the search depth
            if (childDepth <= searchDepth)
            {
                // iterate over all possible configurations where
                // bins contain bears
                foreach (Configuration configuration in possibleConfigurations)
                {
                    // create a new child node with this configuration
                    MinimaxTreeNode <Configuration> childNode =
                        new MinimaxTreeNode <Configuration>(
                            configuration, currentNode);

                    // add the child node to the tree
                    tree.AddNode(childNode);

                    // and add it to the node list
                    nodeList.AddLast(childNode);
                }
            }
        }
        return(tree);
    }
Пример #9
0
    /// <summary>
    /// Determines depth of the current node
    /// </summary>
    /// <param name="currentNode">Current node</param>
    /// <returns>Depth in int</returns>
    int CurrentNodeDepth(MinimaxTreeNode <Configuration> currentNode)
    {
        int currentNodeDepth = 0;

        while (currentNode.Parent != null)
        {
            currentNodeDepth += 1;
            currentNode       = currentNode.Parent;
        }
        return(currentNodeDepth);
    }
Пример #10
0
    int Level(MinimaxTreeNode <Configuration> node)
    {
        int lev = 0;

        while (node.Parent != null)
        {
            lev++;
            node = node.Parent;
        }
        return(lev);
    }
Пример #11
0
        public MinimaxOverview(MinimaxTreeNode rootNode,
                               int tableSizeX,
                               int tableSizeY)
        {
            InitializeComponent();

            this.rootNode   = rootNode;
            this.tableSizeX = tableSizeX;
            this.tableSizeY = tableSizeY;

            MakeTreeStructure(rootNode, null);
        }
Пример #12
0
        private void MinimaxTree_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (e.Node != null)
            {
                ModifiedTreeNode node = (ModifiedTreeNode)e.Node;
                selectedNode = node.MinimaxTreeNode;

                deltaLine = selectedNode.DeltaMove;

                canvas.Refresh();
            }
        }
        protected override int EstimationFunction(MinimaxTreeNode node)
        {
            int blue, red;

            // winning condition for me
            if (WhoHasMoreBoxes(node, out blue, out red) == whoAmI && node.NonExistingLines.Count == 0)
            {
                return(int.MaxValue);
            }
            // winning condition for opponent
            else if (WhoHasMoreBoxes(node, out blue, out red) != whoAmI && node.NonExistingLines.Count == 0)
            {
                return(int.MinValue);
            }

            List <Box> newBoxes = AICommon.TryClosingBoxes(node.ExistingLines,
                                                           (node.Player == MinimaxPlayerType.MAX ? Player.BLUE : Player.RED),
                                                           node.DeltaMove,
                                                           out int[] surroundingEdges);

            // box closing
            if (newBoxes.Count == 1)
            {
                return(int.MaxValue - 2);    // will close one box
            }
            else if (newBoxes.Count == 2)
            {
                return(int.MaxValue - 1);    // will close two boxes
            }
            // keep state where I will have more boxes
            if ((blue > red && whoAmI == Player.BLUE) || (red > blue && whoAmI == Player.RED))
            {
                return(int.MaxValue / 2 - Math.Abs(blue - red));
            }

            // this state could lead opponent to close the box, hence negative estimation
            if (surroundingEdges[0] == 2 && surroundingEdges[1] == 2)
            {
                return(-2);
            }
            else if (surroundingEdges[0] == 2 || surroundingEdges[1] == 2)
            {
                return(-1);
            }

            if ((blue < red && whoAmI == Player.BLUE) || (red < blue && whoAmI == Player.RED))
            {
                return(int.MinValue / 2 + Math.Abs(blue - red));
            }

            // state is neutral for result for the provided tree depth
            return(0);
        }
Пример #14
0
    /// <summary>
    /// Gets the child depth.
    /// </summary>
    /// <returns>The child depth.</returns>
    /// <param name="currentNode">Current node.</param>
    private int getChildDepth(MinimaxTreeNode <Configuration> currentNode)
    {
        // get child depth by counting number of ancestors (0 = root)
        int childDepth = 0;
        MinimaxTreeNode <Configuration> parentNode = currentNode.Parent;

        while (parentNode != null)
        {
            parentNode = parentNode.Parent;
            childDepth++;
        }
        return(childDepth);
    }
Пример #15
0
    void printChildren(MinimaxTreeNode <char> _child)
    {
        IList <MinimaxTreeNode <char> > children = _child.Children;

        if (children.Count > 0)
        {
            foreach (MinimaxTreeNode <char> child in children)
            {
                Debug.Log("Node item value: " + child.Value + ", Node Parent: " + child.Parent.Value);
                printChildren(child);
            }
        }
    }
Пример #16
0
    private List <int> CountTeddysPerBinFull(MinimaxTreeNode <Configuration> node)
    {
        List <int> teddysPerBin = new List <int>();

        foreach (int bin in node.Value.NonEmptyBins)
        {
            teddysPerBin.Add(bin);
        }

        teddysPerBin.Sort();

        return(teddysPerBin);
    }
Пример #17
0
 /// <summary>
 /// Removes the given node as a child this node
 /// </summary>
 /// <param name="child">child to remove</param>
 /// <returns>true if the child was removed, false otherwise</returns>
 public bool RemoveChild(MinimaxTreeNode <T> child)
 {
     // only remove children in list
     if (children.Contains(child))
     {
         child.Parent = null;
         return(children.Remove(child));
     }
     else
     {
         return(false);
     }
 }
Пример #18
0
    /// <summary>
    /// Assigns minimax scores to the tree nodes
    /// </summary>
    /// <param name="tree">tree to mark with scores</param>
    /// <param name="maximizing">whether or not we're maximizing</param>
    void Minimax(MinimaxTreeNode <Configuration> tree,
                 bool maximizing)
    {
        // recurse on children
        IList <MinimaxTreeNode <Configuration> > children = tree.Children;

        if (children.Count > 0)
        {
            foreach (MinimaxTreeNode <Configuration> child in children)
            {
                // toggle maximizing as we move down
                Minimax(child, !maximizing);
            }

            // set default node minimax score
            if (maximizing)
            {
                tree.MinimaxScore = int.MinValue;
            }
            else
            {
                tree.MinimaxScore = int.MaxValue;
            }

            // find maximum or minimum value in children
            foreach (MinimaxTreeNode <Configuration> child in children)
            {
                if (maximizing)
                {
                    // check for higher minimax score
                    if (child.MinimaxScore > tree.MinimaxScore)
                    {
                        tree.MinimaxScore = child.MinimaxScore;
                    }
                }
                else
                {
                    // minimizing, check for lower minimax score
                    if (child.MinimaxScore < tree.MinimaxScore)
                    {
                        tree.MinimaxScore = child.MinimaxScore;
                    }
                }
            }
        }
        else
        {
            // leaf nodes are the base case
            AssignHeuristicMinimaxScore(tree, maximizing);
        }
    }
Пример #19
0
 /// <summary>
 /// Assigns the end of game minimax score
 /// </summary>
 /// <param name="node">node to mark with score</param>
 /// <param name="maximizing">whether or not we're maximizing</param>
 void AssignEndOfGameMinimaxScore(MinimaxTreeNode <Configuration> node,
                                  bool maximizing)
 {
     if (maximizing)
     {
         // other player took the last teddy
         node.MinimaxScore = 1;
     }
     else
     {
         // we took the last teddy
         node.MinimaxScore = 0;
     }
 }
Пример #20
0
        protected Minimax(List <LineBetweenCircles> existingLines,
                          List <LineBetweenCircles> nonExistingLines,
                          List <Box> boxes,
                          Player whoAmI,
                          int maxTreeDepth)
        {
            this.initialExistingLines    = existingLines;
            this.initialNonExistingLines = nonExistingLines;
            this.initialBoxes            = boxes;
            this.whoAmI       = whoAmI;
            this.maxTreeDepth = maxTreeDepth;

            rootNode = ConstructRootNode();
        }
Пример #21
0
        private MinimaxTreeNode ConstructRootNode()
        {
            MinimaxTreeNode node = new MinimaxTreeNode();

            node.EstimationScore = 0;
            node.Player          = MinimaxPlayerType.MIN;

            node.ExistingLines.AddRange(initialExistingLines);
            node.NonExistingLines.AddRange(initialNonExistingLines);
            node.Boxes.AddRange(initialBoxes);

            ConstructTree(node, 0, MinimaxPlayerType.MIN, int.MinValue, int.MaxValue);

            return(node);
        }
Пример #22
0
    void Clear()
    {
        // remove all the children from each node
        // so nodes can be garbage collected
        foreach (MinimaxTreeNode <T> node in nodes)
        {
            node.Parent = null;
            node.RemoveAllChildren();
        }

        // now remove all the nodes from the tree and set root to null
        for (int i = nodes.Count - 1; i >= 0; i--)
        {
            nodes.RemoveAt(i);
        }
        root = null;
    }
Пример #23
0
    void Start()
    {
        // build and mark the tree with minimax scores
        MinimaxTree <char> tree = BuildTree();

        // print out tree
        Debug.Log("Root item value: " + tree.Root.Value);
        IList <MinimaxTreeNode <char> > children = tree.Root.Children;

        if (children.Count > 0)
        {
            foreach (MinimaxTreeNode <char> child in children)
            {
                Debug.Log("Node item value: " + child.Value + ", Node Parent: " + child.Parent.Value);
                printChildren(child);
            }
        }

        // sets whether we are maximizing
        bool maximizing = false;

        Minimax(tree.Root, maximizing);

        // find child node with maximum score
        MinimaxTreeNode <char> maxChildNode = children[0];

        for (int i = 1; i < children.Count; i++)
        {
            if (maximizing)
            {
                if (children[i].MinimaxScore > maxChildNode.MinimaxScore)
                {
                    maxChildNode = children[i];
                }
            }
            else
            {
                if (children[i].MinimaxScore < maxChildNode.MinimaxScore)
                {
                    maxChildNode = children[i];
                }
            }
        }

        Debug.Log("Best move is to node: " + maxChildNode.Value);
    }
Пример #24
0
        protected Player WhoHasMoreBoxes(MinimaxTreeNode node, out int blue, out int red)
        {
            blue = red = 0;

            for (int i = 0; i < node.Boxes.Count; i++)
            {
                if (node.Boxes[i].ClosingPlayer == Player.BLUE)
                {
                    blue++;
                }
                else
                {
                    red++;
                }
            }

            return(blue > red ? Player.BLUE : Player.RED);
        }
Пример #25
0
 /// <summary>
 /// Adds the given node to the tree. If the given node is
 /// null the method returns false. If the parent node is null
 /// or isn't in the tree the method returns false. If the given
 /// node is already a child of the parent node the method returns
 /// false
 /// </summary>
 /// <param name="node">node to add</param>
 /// <returns>true if the node is added, false otherwise</returns>
 public bool AddNode(MinimaxTreeNode <T> node)
 {
     if (node == null || node.Parent == null || !nodes.Contains(node.Parent))
     {
         return(false);
     }
     else if (node.Parent.Children.Contains(node))
     {
         // node already a child of parent
         return(false);
     }
     else
     {
         // add child as tree node and as a child to parent
         nodes.Add(node);
         return(node.Parent.AddChild(node));
     }
 }
Пример #26
0
        public static int CalculateNodesValues(MinimaxTreeNode <int> node)
        {
            if (!node.HasNodes)
            {
                return(node.Value);
            }

            var childrenValues = new List <int>();

            foreach (var child in node.Nodes)
            {
                childrenValues.Add(CalculateNodesValues((MinimaxTreeNode <int>)child));
            }

            node.Value = node.IsMaxNode
                                 ? childrenValues.Max()
                                : childrenValues.Min();
            return(node.Value);
        }
Пример #27
0
    /// <summary>
    /// Assigns a heuristic minimax score to the given node
    /// </summary>
    /// <param name="node">node to mark with score</param>
    /// <param name="maximizing">whether or not we're maximizing</param>
    void AssignHeuristicMinimaxScore4(
        MinimaxTreeNode <Configuration> node,
        bool maximizing)
    {
        // might have reached an end-of-game configuration
        if (node.Value.Empty)
        {
            AssignEndOfGameMinimaxScore(node, maximizing);
        }
        else
        {
            // Default
            node.MinimaxScore = 0.5f;

            // use a heuristic evaluation function to score the node
            // Find cases where the player should win
            // Player should win if there's only two bears left (if maximizing)
            if (node.Value.TotalBearCount % 2 == 1)
            {
                if (maximizing)
                {
                    node.MinimaxScore = 0.75f;
                }
                else
                {
                    node.MinimaxScore = 0.25f;
                }
            }
            // Avoid loosing... perhaps
            // Give this a very low score, to avoid losing
            else if (node.Value.TotalBearCount == 1)
            {
                if (maximizing)
                {
                    node.MinimaxScore = 0;
                }
                else
                {
                    node.MinimaxScore = 1;
                }
            }
        }
    }
Пример #28
0
    public bool RemoveNode(MinimaxTreeNode <T> removeNode)
    {
        if (removeNode == null)
        {
            return(false);
        }
        else if (removeNode == root)
        {
            // removing the root clears the tree
            Clear();
            return(true);
        }
        else
        {
            // remove as child of parent
            bool success = removeNode.Parent.RemoveChild(removeNode);
            if (!success)
            {
                return(false);
            }

            // remove node from tree
            success = nodes.Remove(removeNode);
            if (!success)
            {
                return(false);
            }

            // check for branch node
            if (removeNode.Children.Count > 0)
            {
                // recursively prune subtree
                IList <MinimaxTreeNode <T> > children = removeNode.Children;
                for (int i = children.Count - 1; i >= 0; i--)
                {
                    RemoveNode(children[i]);
                }
            }

            return(true);
        }
    }
Пример #29
0
 /// <summary>
 /// Adds the given node as a child this node
 /// </summary>
 /// <param name="child">child to add</param>
 /// <returns>true if the child was added, false otherwise</returns>
 public bool AddChild(MinimaxTreeNode <T> child)
 {
     // don't add duplicate children
     if (children.Contains(child))
     {
         return(false);
     }
     else if (child == this)
     {
         // don't add self as child
         return(false);
     }
     else
     {
         // add as child and add self as parent
         children.Add(child);
         child.Parent = this;
         return(true);
     }
 }
Пример #30
0
    int Depth(MinimaxTreeNode <Configuration> node)
    {
        MinimaxTreeNode <Configuration> parentNode = node.Parent;

        if (parentNode == null)
        {
            //it is root
            return(0);
        }

        int depth = 1;

        while (parentNode != null)
        {
            parentNode = parentNode.Parent;
            depth++;
        }

        return(depth);
    }