public bool Equals(TreeSearchNode <S, A> other) { if (other is null) { return(false); } if (ReferenceEquals(this, other)) { return(true); } return(GetHashCode() == other.GetHashCode()); }
/// <summary> /// Determines if this TreeSearchNode is an ancestor of the provided TreeSearchNode (i.e. when traversing the tree upwards, if this TreeSearchNode is encountered). /// </summary> /// <param name="descendant">TreeSearchNode that is a potential descendant of this TreeSearchNode.</param> /// <returns>Whether or not the argument TreeSearchNode is a descendant of this TreeSearchNode.</returns> public bool IsAncestorOf(TreeSearchNode <S, A> descendant) { while (!descendant.IsRoot()) { if (descendant.Equals(this)) { return(true); } descendant = descendant.Parent; } return(false); }
/// <summary> /// Determines if this TreeSearchNode is a descendant of the provided TreeSearchNode (i.e. when traversing the tree upwards, if the argument TreeSearchNode is encountered). /// </summary> /// <param name="ancestor">TreeSearchNode that is a potential ancestor of this TreeSearchNode.</param> /// <returns>Whether or not the argument TreeSearchNode is an ancestor of this TreeSearchNode.</returns> public bool IsDescendantOf(TreeSearchNode <S, A> ancestor) { // A TreeSearchNode cannot be it's own ancestor if (Equals(ancestor)) { return(false); } var node = this; while (!node.IsRoot()) { node = node.Parent; if (node.Equals(ancestor)) { return(true); } } return(false); }
/// <summary> /// Returns the child node of the argument node that has the best score to visits ratio. /// </summary> /// <param name="context">The context of the search.</param> /// <param name="node">The node from which to select the best child.</param> /// <returns>The child node of the argument node that has the best score to visits ratio.</returns> public TreeSearchNode <P, A> SelectFinalNode(SearchContext <D, P, A, S, Sol> context, TreeSearchNode <P, A> node) { var max = double.MinValue; var numberOfChildren = node.Children.Count; // This makes sure a random node is returned if all ratios are equal. var maxIndex = new Random().Next(numberOfChildren); for (var i = 0; i < numberOfChildren; i++) { var child = node.Children.ElementAt(i); // Don't consider nodes without visits (also to avoid divide-by-zero). if (child.Visits == 0) { continue; } var nodeScore = child.Score; var childRatio = nodeScore / child.Visits; if (!(childRatio > max)) { continue; } max = childRatio; maxIndex = i; } // Return the child with the maximum ratio. return(node.Children.ElementAt(maxIndex)); }
public int Compare(TreeSearchNode <P, A> x, TreeSearchNode <P, A> y) { return(y.CalculateScore(_nodeEvaluation).CompareTo(x.CalculateScore(_nodeEvaluation))); }
/// <summary> /// Selects the next node, given the argument node. /// </summary> /// <param name="context">The context of the search.</param> /// <param name="node">The node from which to select the next node.</param> /// <returns>The next node.</returns> public TreeSearchNode <P, A> SelectNextNode(SearchContext <D, P, A, S, Sol> context, TreeSearchNode <P, A> node) { // Determine the minimum number of visits on the parent node required before using evaluation. var numberOfChildren = node.Children.Count; var minVisitsOnParent = MinVisits * numberOfChildren; // In default behaviour, we will have iterated over all children once before arriving at the first call to the Selection Strategy. // If the parent node hasn't been visited a minimum number of times, select the next appropriate child. if (minVisitsOnParent > node.Visits) { return(node.Children.ElementAt(node.Visits % numberOfChildren)); } if (minVisitsOnParent == node.Visits) { node.Children = node.Children.OrderByDescending(i => i.CalculateScore(NodeEvaluation)).ToList(); } else { // The first child is always the one picked; so it is the only node we need to sort to a new location. // Pick it, and ensure it's at its required location. var firstChild = node.Children.First(); // Update the score and remove dirty. firstChild.CalculateScore(NodeEvaluation); var i = 1; for (; i < node.Children.Count; i++) { if (firstChild.CalculateScore(NodeEvaluation) >= node.Children.ElementAt(i).CalculateScore(NodeEvaluation)) { break; } } i--; if (i <= 0) { return(node.Children.First()); } // Move everyone by one, and set the child at its newest index. if (i == 1) { // Special case where we optimise for when we are just better than the second item (often). var items = node.Children.ToArray(); items[0] = items[1]; items[1] = firstChild; node.Children = items.ToList(); } else { var items = node.Children.ToArray(); Array.Copy(items, 1, items, 0, i); items[i] = firstChild; node.Children = items.ToList(); } } return(node.Children.First()); }
/// <summary> /// Propagate an evaluation value of the argument state starting from the argument node back up to the root node. /// </summary> /// <param name="context">The context of the search.</param> /// <param name="evaluation">The strategy used to evaluate the state.</param> /// <param name="node">The node from which the backpropagation starts.</param> /// <param name="state">The state that should be evaluated.</param> public void BackPropagate(SearchContext <D, P, A, S, Sol> context, IStateEvaluation <D, P, A, S, Sol, TreeSearchNode <P, A> > evaluation, TreeSearchNode <P, A> node, P state) { // Evaluate the state with respect to the argument node. var value = evaluation.Evaluate(context, node, state); do { // Visit the node with the evaluation value. node.Visit(value); // Keep moving up the tree while there is a valid parent. } while ((node = node.Parent) != null); }
/// <summary> /// Propagate an evaluation value of the argument state starting from the argument node back up to the root node. /// </summary> /// <param name="context">The context of the search.</param> /// <param name="evaluation">The strategy used to evaluate the state.</param> /// <param name="node">The node from which the backpropagation starts.</param> /// <param name="state">The state that should be evaluated.</param> public void BackPropagate(SearchContext <D, P, A, S, Sol> context, IStateEvaluation <D, P, A, S, Sol, TreeSearchNode <P, A> > evaluation, TreeSearchNode <P, A> node, P state) { // Evaluate the state with respect to the argument node. var value = evaluation.Evaluate(context, node, state); // The root player is the current player in the search's source state. var rootPlayer = context.Source.CurrentPlayer(); do { // Check whether or not this node is a root player's node. var targetPlayer = node.IsRoot() || rootPlayer == node.Payload.Player(); // Visits the node with a coloured evaluation value. node.Visit(targetPlayer ? value : -value); // Keep moving up the tree while there is a valid parent. } while ((node = node.Parent) != null); }
/// <summary> /// Adds a child to this TreeSearchNode's Children. /// </summary> /// <param name="child">The TreeSearchNode to add as a child.</param> public void AddChild(TreeSearchNode <S, A> child) { Children.Add(child); }
/// <summary> /// Constructor that creates a TreeSearchNode with a parent node, a world-state and a payload. /// </summary> /// <param name="parent">The parent node of the node.</param> /// <param name="state">The state that this TreeSearchNode represents.</param> /// <param name="payload"><see cref="Node{A}.Payload"/></param> public TreeSearchNode(TreeSearchNode <S, A> parent, S state, A payload) : base(state, payload) { Parent = parent; Children = new List <TreeSearchNode <S, A> >(); }