/// <summary> /// Traversal of <see cref="DecisionTree"/> is done by recusrively calling the <see cref="ADecisionNode.TreeTraversal(ADecisionNode)"/>. /// </summary> public static RefList <AIDecisionTreeChoice> traverseDecisionTree(DecisionTree p_decisionTree) { RefList <AIDecisionTreeChoice> l_choices = new RefList <AIDecisionTreeChoice>(); RefList <TraversalStack> l_traversalStacks = new RefList <TraversalStack>(); l_traversalStacks.Add(TraversalStack.build(p_decisionTree.RootNode)); while (l_traversalStacks.Count > 0) { ref TraversalStack l_currentTraversalStack = ref l_traversalStacks.ValueRef(l_traversalStacks.Count - 1); //If there if the current node has links if (l_currentTraversalStack.DecisionNode.LinkedNodes != null) { if (l_currentTraversalStack.LinkIterationCounter < l_currentTraversalStack.DecisionNode.LinkedNodes.Count) { //We traverse the link and go one level deeper ADecisionNode l_nextNode = l_currentTraversalStack.DecisionNode.LinkedNodes[l_currentTraversalStack.LinkIterationCounter]; l_currentTraversalStack.LinkIterationCounter += 1; TraversalStack l_oneLevelDepperStack = TraversalStack.build(l_nextNode); l_traversalStacks.AddRef(ref l_oneLevelDepperStack); l_nextNode.TreeTraversal(l_currentTraversalStack.DecisionNode); } else { #region Updating stack l_traversalStacks.RemoveAt(l_traversalStacks.Count - 1); #endregion } } else // No links have been found, this means that the current node is a leaf node. { #region Creating the choice as the current node is a leaf ADecisionNode[] l_choiceNodes = new ADecisionNode[l_traversalStacks.Count]; for (int i = 0; i < l_traversalStacks.Count; i++) { l_choiceNodes[i] = l_traversalStacks.ValueRef(i).DecisionNode; } AIDecisionTreeChoice l_choice = AIDecisionTreeChoice.build(l_choiceNodes); l_choices.AddRef(ref l_choice); #endregion l_traversalStacks.RemoveAt(l_traversalStacks.Count - 1); } }
/// <summary> /// Picking the best choice between <paramref name="p_choices"/>. /// The choice is made by associating a score to every choices. The choice with the highest score is picked. /// </summary> /// <param name="p_choices"> Compared choices. </param> /// <returns> The picked choice </returns> public static ref AIDecisionTreeChoice defaultTestPickChoice(RefList <AIDecisionTreeChoice> p_choices, Entity p_calledEntity) { // Becuase PathScore represents the distance crossed and that we want to minimize movement, // we normalize the PathScore by it's potential maxmimum value calculated if the Entity were using all it's ActionPoints to move. ActionPoint l_calledEntityActionPoint = EntityComponent.get_component <ActionPoint>(p_calledEntity); float l_maxPathScoreThatCanBeCrossed = _ActionPoint.Calculations.actionPointToCrossableWorldDistance(l_calledEntityActionPoint.ActionPointData.CurrentActionPoints); RefList <AIDecisionScore> l_choiceScores = new RefList <AIDecisionScore>(p_choices.Count); for (int i = 0; i < p_choices.Count; i++) { AIDecisionScore l_choiceScore = AIDecisionScore.build(); ref AIDecisionTreeChoice l_aIDecisionTreeChoice = ref p_choices.ValueRef(i); for (int j = 0; j < l_aIDecisionTreeChoice.DecisionNodesChoiceOrdered.Length; j++) { switch (l_aIDecisionTreeChoice.DecisionNodesChoiceOrdered[j]) { case MoveToNavigationNodeNode l_moveToNavigationNodeNode: { l_choiceScore.PathScore += math.max(l_maxPathScoreThatCanBeCrossed - l_moveToNavigationNodeNode.PathCost, 0.0f); break; } case AttackNode l_attackNode: { l_choiceScore.DamageScore += l_attackNode.DamageDone; break; } case HealNode l_healNode: { l_choiceScore.HealScore += l_healNode.RecoveredHealth; break; } } } l_choiceScores.AddRef(ref l_choiceScore); }