/// <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; } }
/// <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); }
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); }
/// <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); }
/// <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; }
/// <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; } }
/// <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); }
/// <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); }
int Level(MinimaxTreeNode <Configuration> node) { int lev = 0; while (node.Parent != null) { lev++; node = node.Parent; } return(lev); }
public MinimaxOverview(MinimaxTreeNode rootNode, int tableSizeX, int tableSizeY) { InitializeComponent(); this.rootNode = rootNode; this.tableSizeX = tableSizeX; this.tableSizeY = tableSizeY; MakeTreeStructure(rootNode, null); }
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); }
/// <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); }
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); } } }
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); }
/// <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 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); } }
/// <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; } }
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(); }
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); }
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; }
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); }
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); }
/// <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)); } }
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); }
/// <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; } } } }
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); } }
/// <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); } }
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); }