/// <summary> /// Selects best first move using minimax /// </summary> /// <param name="args">command-line arguments</param> static void Main(string[] args) { // build and mark the tree with minimax scores MinimaxTree <char> tree = BuildTree(); InitializeLeafScores(); bool maximizing = true; Minimax(tree.Root, maximizing); // find optimal minimax path StringBuilder optimalPath = new StringBuilder(); optimalPath.Append("Optimal Path: "); MinimaxTreeNode <char> currentNode = tree.Root; optimalPath.Append($"[{currentNode.Value}={currentNode.MinimaxScore}]"); MinimaxTreeNode <char> nextNode = GetBestChild(currentNode, maximizing); while (nextNode != null) { currentNode = nextNode; optimalPath.Append($"=>[{currentNode.Value}], [{currentNode.Value}={currentNode.MinimaxScore}]"); maximizing = !maximizing; nextNode = GetBestChild(currentNode, maximizing); } // print best move Console.WriteLine(optimalPath.ToString()); Console.WriteLine(); }
/// <summary> /// Selects best first move using minimax /// </summary> /// <param name="args">command-line arguments</param> static void Main(string[] args) { // build and mark the tree with minimax scores MinimaxTree <char> tree = BuildTree(); //Attach scores to each of the nodes in the tree Minimax(tree.Root, true); // find child node with maximum score IList <MinimaxTreeNode <char> > children = tree.Root.Children; //Save the children of the root MinimaxTreeNode <char> maxChildNode = children[0]; //Max child node is the first child (Assuming that the left choice is the best) //Just in case we are wrong we check here if the minimax score is right and the maxchild is the greatest, if not, then reassign it for (int i = 1; i < children.Count; i++) { if (children[i].MinimaxScore > maxChildNode.MinimaxScore) { maxChildNode = children[i]; } } // print best move Console.WriteLine("Best move is to char " + maxChildNode.Value); Console.ReadLine(); }
/// <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); } }
/// <summary> /// Assigns the 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> static void Minimax(MinimaxTreeNode <char> tree, bool maximizing) { //Recurse the children IList <MinimaxTreeNode <char> > children = tree.Children; if (children.Count > 0) { foreach (MinimaxTreeNode <char> 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 <char> 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 AssignMinimaxScore(tree); } }
/// <summary> /// Clears all the nodes from the tree /// </summary> 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; }
/// <summary> /// Removes the given node from the tree. If the node isn't /// found in the tree, the method returns false. /// /// Note that the subtree with the node to remove as its /// root is pruned from the tree /// </summary> /// <param name="removeNode">node to remove</param> /// <returns>true if the node is removed, false otherwise</returns> 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); } }
static void Minimax(MinimaxTreeNode <char> node, bool maximizing) { IList <MinimaxTreeNode <char> > children = node.Children; // recurse on children if (children.Count > 0) { // obtain score on children while toggling minmaxing foreach (MinimaxTreeNode <char> child in children) { Minimax(child, !maximizing); } // initialize default if (maximizing) { node.MinimaxScore = int.MinValue; } else { node.MinimaxScore = int.MaxValue; } // find max or min from children foreach (MinimaxTreeNode <char> child in children) { if (maximizing) { node.MinimaxScore = Math.Max(node.MinimaxScore, child.MinimaxScore); } else { node.MinimaxScore = Math.Min(node.MinimaxScore, child.MinimaxScore); } } } else { // leaf nodes as base case AssignMinimaxScore(node, maximizing); } }
/// <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)); } }
static MinimaxTreeNode <char> GetBestChild(MinimaxTreeNode <char> parent, bool maximizing) { IList <MinimaxTreeNode <char> > children = parent.Children; if (children.Count == 0) { return(null); } MinimaxTreeNode <char> bestChild = children[0]; for (int i = 1; i < children.Count; i++) { if ((maximizing && children[i].MinimaxScore > bestChild.MinimaxScore) || (!maximizing && children[i].MinimaxScore < bestChild.MinimaxScore)) { bestChild = children[i]; } } return(bestChild); }
/// <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); } }
static bool AssignMinimaxScore(MinimaxTreeNode <char> node, bool maximizing) { if (scores.ContainsKey(node.Value)) { node.MinimaxScore = scores[node.Value]; return(true); } else { // not initialized, set worst possible as default if (maximizing) { node.MinimaxScore = int.MinValue; } else { node.MinimaxScore = int.MaxValue; } return(false); } }
/// <summary> /// Builds the tree /// </summary> /// <returns>tree</returns> static MinimaxTree <char> BuildTree() { MinimaxTree <char> tree = new MinimaxTree <char>('A'); MinimaxTreeNode <char> bNode = new MinimaxTreeNode <char>('B', tree.Root); tree.AddNode(bNode); MinimaxTreeNode <char> cNode = new MinimaxTreeNode <char>('C', tree.Root); tree.AddNode(cNode); MinimaxTreeNode <char> dNode = new MinimaxTreeNode <char>('D', tree.Root); tree.AddNode(dNode); MinimaxTreeNode <char> eNode = new MinimaxTreeNode <char>('E', bNode); tree.AddNode(eNode); MinimaxTreeNode <char> fNode = new MinimaxTreeNode <char>('F', bNode); tree.AddNode(fNode); MinimaxTreeNode <char> gNode = new MinimaxTreeNode <char>('G', bNode); tree.AddNode(gNode); MinimaxTreeNode <char> hNode = new MinimaxTreeNode <char>('H', cNode); tree.AddNode(hNode); MinimaxTreeNode <char> iNode = new MinimaxTreeNode <char>('I', cNode); tree.AddNode(iNode); MinimaxTreeNode <char> jNode = new MinimaxTreeNode <char>('J', dNode); tree.AddNode(jNode); MinimaxTreeNode <char> kNode = new MinimaxTreeNode <char>('K', dNode); tree.AddNode(kNode); MinimaxTreeNode <char> lNode = new MinimaxTreeNode <char>('L', eNode); tree.AddNode(lNode); MinimaxTreeNode <char> mNode = new MinimaxTreeNode <char>('M', eNode); tree.AddNode(mNode); MinimaxTreeNode <char> nNode = new MinimaxTreeNode <char>('N', fNode); tree.AddNode(nNode); MinimaxTreeNode <char> oNode = new MinimaxTreeNode <char>('O', fNode); tree.AddNode(oNode); MinimaxTreeNode <char> pNode = new MinimaxTreeNode <char>('P', gNode); tree.AddNode(pNode); MinimaxTreeNode <char> qNode = new MinimaxTreeNode <char>('Q', gNode); tree.AddNode(qNode); MinimaxTreeNode <char> rNode = new MinimaxTreeNode <char>('R', hNode); tree.AddNode(rNode); MinimaxTreeNode <char> sNode = new MinimaxTreeNode <char>('S', hNode); tree.AddNode(sNode); MinimaxTreeNode <char> tNode = new MinimaxTreeNode <char>('T', iNode); tree.AddNode(tNode); MinimaxTreeNode <char> uNode = new MinimaxTreeNode <char>('U', iNode); tree.AddNode(uNode); MinimaxTreeNode <char> vNode = new MinimaxTreeNode <char>('V', jNode); tree.AddNode(vNode); MinimaxTreeNode <char> wNode = new MinimaxTreeNode <char>('W', jNode); tree.AddNode(wNode); MinimaxTreeNode <char> xNode = new MinimaxTreeNode <char>('X', kNode); tree.AddNode(xNode); MinimaxTreeNode <char> yNode = new MinimaxTreeNode <char>('Y', kNode); tree.AddNode(yNode); return(tree); }
/// <summary> /// Assigns a minimax score to the given node /// </summary> /// <param name="node">node to mark with score</param> static void AssignMinimaxScore(MinimaxTreeNode <char> node) { switch (node.Value) { case 'L': node.MinimaxScore = 7; break; case 'M': node.MinimaxScore = 6; break; case 'N': node.MinimaxScore = 8; break; case 'O': node.MinimaxScore = 5; break; case 'P': node.MinimaxScore = 2; break; case 'Q': node.MinimaxScore = 3; break; case 'R': node.MinimaxScore = 0; break; case 'S': node.MinimaxScore = -2; break; case 'T': node.MinimaxScore = 6; break; case 'U': node.MinimaxScore = 2; break; case 'V': node.MinimaxScore = 5; break; case 'W': node.MinimaxScore = 8; break; case 'X': node.MinimaxScore = 9; break; case 'Y': node.MinimaxScore = 2; break; } }
/// <summary> /// Constructor /// </summary> /// <param name="value">value of the root node</param> public MinimaxTree(T value) { root = new MinimaxTreeNode <T>(value, null); nodes.Add(root); }
/// <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> >(); }