From 7207424daf2e8cad8a99f6c11b0a8d9377e693b8 Mon Sep 17 00:00:00 2001
From: taoria <445625470@qq.com>
Date: Mon, 22 Aug 2022 17:53:12 +0800
Subject: [PATCH] feature:four method to iterate the node,making most type of
 graph available.
---
 TNodeCore/Runtime/Components/RuntimeGraph.cs  |  24 ++-
 .../RuntimeModels/IRuntimeNodeGraph.cs        |  22 ++-
 .../Runtime/RuntimeModels/StaticGraph.cs      |  32 +++-
 TNodeCore/Runtime/Tools/GraphTool.cs          | 140 +++++++++++++++---
 4 files changed, 183 insertions(+), 35 deletions(-)
diff --git a/TNodeCore/Runtime/Components/RuntimeGraph.cs b/TNodeCore/Runtime/Components/RuntimeGraph.cs
index 3159915..30483ea 100644
--- a/TNodeCore/Runtime/Components/RuntimeGraph.cs
+++ b/TNodeCore/Runtime/Components/RuntimeGraph.cs
@@ -70,7 +70,7 @@ namespace TNodeCore.Runtime.Components{
 #if UNITY_EDITOR
             BuildSceneNode();
 #endif
-            _runtimeNodeEnumerator = _graphTool.BreathFirstSearch();
+            ResetState();
             _build = true;
         }
 
@@ -171,7 +171,21 @@ namespace TNodeCore.Runtime.Components{
         }
 
         public void ResetState(){
-            _runtimeNodeEnumerator = _graphTool.BreathFirstSearch();
+            switch (AccessMethod){
+                case AccessMethod.Bfs:
+                    _runtimeNodeEnumerator = _graphTool.BreathFirstSearch();
+                    break;
+                case AccessMethod.Dfs:
+                    _runtimeNodeEnumerator = _graphTool.DeepFirstSearchWithCondition();
+                    break;
+                case AccessMethod.StateTransition:
+                    _runtimeNodeEnumerator = _graphTool.IterateDirectlyTraversal();
+                    break;
+                case AccessMethod.Dependency:
+                    _runtimeNodeEnumerator = _graphTool.IterateNext();
+                    break;
+            }
+
         }
 
         public NodeData CurrentNode(){
@@ -268,6 +282,8 @@ namespace TNodeCore.Runtime.Components{
             _graphTool.DirectlyTraversal();
         }
 
+        public AccessMethod AccessMethod{ get; set; } = AccessMethod.Dependency;
+
         public RuntimeNode GetRuntimeNode(NodeData nodeData){
             if(!_build)
                 Build();
@@ -333,8 +349,4 @@ namespace TNodeCore.Runtime.Components{
         }
     }
     
-    public enum ProcessingStrategy{
-        BreadthFirst,
-        DepthFirst
-    }
 }
\ No newline at end of file
diff --git a/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs b/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs
index fcce0b4..a7752c7 100644
--- a/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs
+++ b/TNodeCore/Runtime/RuntimeModels/IRuntimeNodeGraph.cs
@@ -3,13 +3,33 @@ using System.Collections.Generic;
 using TNodeCore.Runtime.Models;
 
 namespace TNodeCore.Runtime.RuntimeModels{
+    public enum AccessMethod{
+        //Iterate all nodes by breadth first search,start with the entry nodes and resolve the dependencies
+        Bfs,
+        //Iterate all nodes by deep first search,but run the dependencies that are not ready first
+        Dfs,
+        ///Start from the entry node,if multiple entry nodes exist,run first of them ,and then ,from this entry node,transit to nodes that met the condition,if 
+        ///there is no node that met the condition ,stay in the state ,if there are multiple nodes that met the condition,run first of them
+        /// If the run node depends on other nodes,run the dependencies first.
+        StateTransition,
+        /// 
+        /// Iterate all nodes by  a topological order 
+        /// 
+        Dependency,
+        
+        
+    }
+    
     public interface IRuntimeNodeGraph{
+        public AccessMethod AccessMethod{ get; set; }
+        
         public RuntimeNode GetRuntimeNode(NodeData nodeData);
         public RuntimeNode GetRuntimeNode(string id);
         public BlackboardData GetBlackboardData();
         public List GetRuntimeNodes();
         public Dictionary GetRuntimeNodesDictionary();
-
+        
+        
         public NodeData GetNode(string id);
         List GetRuntimeNodesOfType(Type type);
         List GetRuntimeNodesOfType();
diff --git a/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs b/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs
index e184151..b648526 100644
--- a/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs
+++ b/TNodeCore/Runtime/RuntimeModels/StaticGraph.cs
@@ -8,7 +8,7 @@ using TNodeCore.Runtime.Models;
 namespace TNodeCore.Runtime.RuntimeModels{
     public class StaticGraph:IRuntimeNodeGraph{
         private Dictionary _nodes;
-        private IEnumerator _breathFirstEnumerator;
+        private IEnumerator _runtimeNodeEnumerator;
         
         private readonly GraphTool _graphTool;
         private readonly GraphData _originalData;
@@ -47,13 +47,29 @@ namespace TNodeCore.Runtime.RuntimeModels{
                 ModifyLinks(link);
             }
             _graphTool = new GraphTool(this);
-            _breathFirstEnumerator = _graphTool.BreathFirstSearch();
+            _runtimeNodeEnumerator = _graphTool.BreathFirstSearch();
         }
 
         public void ResetState(){
-            _breathFirstEnumerator = _graphTool.BreathFirstSearch();
+            switch (AccessMethod){
+                case AccessMethod.Bfs:
+                    _runtimeNodeEnumerator = _graphTool.BreathFirstSearch();
+                    break;
+                case AccessMethod.Dfs:
+                    _runtimeNodeEnumerator = _graphTool.DeepFirstSearchWithCondition();
+                    break;
+                case AccessMethod.StateTransition:
+                    _runtimeNodeEnumerator = _graphTool.IterateDirectlyTraversal();
+                    break;
+                case AccessMethod.Dependency:
+                    _runtimeNodeEnumerator = _graphTool.IterateNext();
+                    break;
+            }
+
         }
 
+        public AccessMethod AccessMethod{ get; set; } = AccessMethod.Bfs;
+
         public RuntimeNode GetRuntimeNode(NodeData nodeData){
             return _nodes[nodeData.id];
         }
@@ -92,15 +108,15 @@ namespace TNodeCore.Runtime.RuntimeModels{
         }
 
         public RuntimeNode MoveNext(){
-           _breathFirstEnumerator.MoveNext();
-           return _breathFirstEnumerator.Current;
+           _runtimeNodeEnumerator.MoveNext();
+           return _runtimeNodeEnumerator.Current;
         }
 
         public RuntimeNode CurrentRuntimeNode(){
-            if (_breathFirstEnumerator.Current == null){
-                _breathFirstEnumerator.MoveNext();
+            if (_runtimeNodeEnumerator.Current == null){
+                _runtimeNodeEnumerator.MoveNext();
             }
-            return _breathFirstEnumerator.Current;
+            return _runtimeNodeEnumerator.Current;
         }
     }
 
diff --git a/TNodeCore/Runtime/Tools/GraphTool.cs b/TNodeCore/Runtime/Tools/GraphTool.cs
index 392a6a5..e577173 100644
--- a/TNodeCore/Runtime/Tools/GraphTool.cs
+++ b/TNodeCore/Runtime/Tools/GraphTool.cs
@@ -1,6 +1,9 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Linq;
+using TNode.TNodeCore.Runtime.Components;
 using TNodeCore.Runtime;
 using TNodeCore.Runtime.Components;
 using TNodeCore.Runtime.Extensions;
@@ -50,68 +53,164 @@ namespace TNode.TNodeCore.Runtime.Tools{
                 }
                 node.NodeData.Process();
             }
+        }
+        //A IEnumerator version of the DirectlyTraversal,used to run the graph in a coroutine or somewhere you need
+        public IEnumerator IterateDirectlyTraversal(){
+            if (TopologicalSorted==false){
+                throw new Exception("The graph is not sorted,there may be a circular dependency,use another access method instead");
+            }
+            foreach (var node in TopologicalOrder){
+                var links = node.InputLinks;
+                foreach (var link in links){
+                    HandlingLink(link);
+                }
+                node.NodeData.Process();
+                yield return node;
+            }
+        }
+        /// 
+        /// usually used in state transition 
+        /// 
+        /// 
+        public IEnumerator IterateNext(){
+            var currentNode = NonDependencyNode.FirstOrDefault();
+            if (currentNode == null){
+                yield break;
+            }
+
+            currentNode.NodeData.Process();
+            yield return currentNode;
+
+            while(currentNode.OutputLinks.Any()){
+                if (currentNode is ConditionalRuntimeNode conditionalRuntimeNode){
+                    currentNode = RuntimeNodes[conditionalRuntimeNode.GetNextNodeId()];
+                }
+                else{
+                    var link = currentNode.OutputLinks.FirstOrDefault();
+                    if (link != null){
+                        HandlingLink(link);
+                        currentNode = RuntimeNodes[link.inPort.nodeDataId];     
+                    }
+                }
+                currentNode.NodeData.Process();
+                yield return currentNode;
+            }
+
+           
         }
         //Try to enable state transition from node to node.
         public IEnumerator DeepFirstSearchWithCondition(){
+            //Define the basic data structure for a traversal of the graph
             Stack stack = new Stack();
+            HashSet alreadyContained = new HashSet();
+            HashSet visited = new HashSet();
             foreach (var runtimeNode in NonDependencyNode){
                 stack.Push(runtimeNode);
             }
             while (stack.Count > 0){
                 var node = stack.Pop();
                 
+                visited.Add(node);
                 if (node is ConditionalRuntimeNode conditionalRuntimeNode){
                     var ids = conditionalRuntimeNode.GetConditionalNextIds();
                     
                     var nextNodes =  ids.Select(id=>RuntimeNodes[id]).ToList();
                     
                     foreach (var runtimeNode in nextNodes){
-                        stack.Push(runtimeNode);
+                        AddToCollectionIfMeetCondition(alreadyContained, visited,runtimeNode, stack);
                     }
                 }
                 else{
-                    var nextNodes = node.OutputLinks.Select(link => RuntimeNodes[link.inPort.nodeDataId]);
-                    foreach (var runtimeNode in nextNodes){
-                        stack.Push(runtimeNode);
+                    foreach (var runtimeNode in node.OutputLinks.Select(link => RuntimeNodes[link.inPort.nodeDataId])){
+                        AddToCollectionIfMeetCondition(alreadyContained, visited,runtimeNode, stack);
                     }
-                    node.OutputLinks.ForEach(HandlingLink);
                 }
+                node.OutputLinks.ForEach(HandlingLink);
                 node.NodeData.Process();
                 yield return node;
             }
         }
+        /// 
+        /// Breath first search for the graph.Not a standard BFS algorithm since all entries will be executed first.
+        /// 
+        /// The IEnumerator to iterate the node
         public IEnumerator BreathFirstSearch(){
+            //Define the basic data structure for a traversal of the graph
             Queue queue = new Queue();
+            //Already contained method to avoid duplicate traversal
             HashSet alreadyContained = new HashSet();
+            //Visited method to avoid duplicate traversal
             HashSet visited = new HashSet();
+            //Firstly add all entry node to the queue
             foreach (var runtimeNode in NonDependencyNode){
                 queue.Enqueue(runtimeNode);
                 alreadyContained.Add(runtimeNode);
             }
+            //Iterate the queue to implement bfs
             while (queue.Count > 0){
                 var node = queue.Dequeue();
                 visited.Add(node);
+                //Conditional node will be traversed in a special way,only links fit the condition will be traversed
                 if (node is ConditionalRuntimeNode conditionalRuntimeNode){
                     var ids = conditionalRuntimeNode.GetConditionalNextIds();
                     
                     var nextNodes =  ids.Select(id=>RuntimeNodes[id]).ToList();
                     
                     foreach (var runtimeNode in nextNodes){
-                        AddNodeToQueueIfMeetCondition(alreadyContained, runtimeNode, queue);
+                        AddToCollectionIfMeetCondition(alreadyContained, visited,runtimeNode, queue);
                     }
                 }
                 else{
                     foreach (var runtimeNode in node.OutputLinks.Select(link => RuntimeNodes[link.inPort.nodeDataId])){
-                        AddNodeToQueueIfMeetCondition(alreadyContained, runtimeNode, queue);
+                        AddToCollectionIfMeetCondition(alreadyContained, visited,runtimeNode, queue);
                     }
-                    node.OutputLinks.ForEach(HandlingLink);
                 }
                 node.NodeData.Process();
+                //Handle the links of the node
+                node.OutputLinks.ForEach(HandlingLink);
                 yield return node;
             }
         }
 
-        private void AddNodeToQueueIfMeetCondition(HashSet alreadyContained, RuntimeNode runtimeNode, Queue queue){
+        private void AddToCollectionIfMeetCondition(HashSet alreadyContained,HashSet visited, RuntimeNode runtimeNode, Queue queue){
+            //Check if the node is already contained in the queue or already visited
+     
+            if (visited.Contains(runtimeNode)) return;
+            //the already contained guard is used to avoid duplicate traversal because the graph may start with multiple entries and all entry node should be run first.
+            //Thus cause the same node could be add to the queue multiple times.
+            if (alreadyContained.Contains(runtimeNode)) return;
+ 
+            //Check if the visited node has all previous node of the node
+            var dependentNodes = runtimeNode.GetDependentNodesId().Select(x => RuntimeNodes[x]);
+            var allDependenciesVisited = dependentNodes.Aggregate(true, (a, b) =>
+                alreadyContained.Contains(b) && a
+            );
+            //If the current node is not prepared,another routine will execute it when all is ready
+            if (allDependenciesVisited == false) return;
+            
+            //If all conditions are met, add the node to the queue
+            queue.Enqueue(runtimeNode);
+            alreadyContained.Add(runtimeNode);
+        }
+        private void AddToCollectionIfMeetCondition(HashSet alreadyContained,HashSet visited, RuntimeNode runtimeNode, Stack stack){
+            //Check if the node is already contained in the stack
+            if (alreadyContained.Contains(runtimeNode)) return;
+            if (visited.Contains(runtimeNode)) return;
+            //Check if the visited node has all previous node of the node
+            var dependentNodes = runtimeNode.GetDependentNodesId().Select(x => RuntimeNodes[x]);
+            var allDependenciesVisited = dependentNodes.Aggregate(true, (a, b) =>
+                alreadyContained.Contains(b) && a
+            );
+            //If the current node is not prepared,run it dependently.
+            if (allDependenciesVisited == false){
+                RunNodeDependently(runtimeNode,0,false);
+            }
+            
+            //If all conditions are met, add the node to the stack
+            stack.Push(runtimeNode);
+            alreadyContained.Add(runtimeNode);
+        }
+        private void AddNodeToStackIfMeetCondition(HashSet alreadyContained, RuntimeNode runtimeNode, Stack stack){
             //Check if the node is already contained in the queue
             if (alreadyContained.Contains(runtimeNode)) return;
             
@@ -123,7 +222,7 @@ namespace TNode.TNodeCore.Runtime.Tools{
             if (allDependenciesVisited == false) return;
             
             //If all conditions are met, add the node to the queue
-            queue.Enqueue(runtimeNode);
+            stack.Push(runtimeNode);
             alreadyContained.Add(runtimeNode);
         }
 
@@ -146,10 +245,14 @@ namespace TNode.TNodeCore.Runtime.Tools{
         /// 
         /// The node you want to resolve dependency
         /// search depth,no need provide a number when use outside
-        public void RunNodeDependently(RuntimeNode runtimeNode,int dependencyLevel=0){
+        /// if the the node of the 0 level should be processed,which is the node you want to run,be processed by the method
+        public void RunNodeDependently(RuntimeNode runtimeNode,int dependencyLevel=0,bool processTargetNode=true){
             var links = runtimeNode.InputLinks;
             foreach (var link in links){
                 var outputNode = RuntimeNodes[link.outPort.nodeDataId];
+                if (outputNode is ConditionalRuntimeNode){
+                    continue;
+                }
                 RunNodeDependently(outputNode,dependencyLevel+1);
                 HandlingLink(link);
             }
@@ -163,8 +266,13 @@ namespace TNode.TNodeCore.Runtime.Tools{
             if (runtimeNode.OutputLinks.Count == 0 && dependencyLevel != 0){
                 return;
             }
-            runtimeNode.NodeData.Process();
+
+            if (processTargetNode||dependencyLevel != 0){
+                runtimeNode.NodeData.Process();
+            }
+            
         }
+
         /// 
         /// Max depth of dependency traversal,in case of some special situation. the dependency level bigger than this number will be considered as a loop.
         /// 
@@ -196,15 +304,7 @@ namespace TNode.TNodeCore.Runtime.Tools{
         /// List of nodes you need to traversal to build graph tool
         /// Map stores the mapping of node data id to runtime node
         /// The graph you want to build graph tool for
-        public GraphTool(List list){
-            CreateDummyRuntimeGraph();
-        }
 
-        private void CreateDummyRuntimeGraph(){
-            
-            
-            
-        }
 
         public GraphTool(IRuntimeNodeGraph graph){
             RuntimeNodes = graph.GetRuntimeNodesDictionary();