graph logic depends on MoonTools.Core.Graph
parent
85f99a565c
commit
790e36b2d3
|
@ -11,8 +11,10 @@ namespace Encompass
|
||||||
/// They are responsible for reading the World state, reading messages, emitting messages, and creating or mutating Entities and Components.
|
/// They are responsible for reading the World state, reading messages, emitting messages, and creating or mutating Entities and Components.
|
||||||
/// Engines run once per World Update.
|
/// Engines run once per World Update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class Engine
|
public abstract class Engine : IEquatable<Engine>
|
||||||
{
|
{
|
||||||
|
public Guid ID;
|
||||||
|
|
||||||
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
|
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
|
||||||
internal readonly HashSet<Type> receiveTypes = new HashSet<Type>();
|
internal readonly HashSet<Type> receiveTypes = new HashSet<Type>();
|
||||||
internal readonly Dictionary<Type, int> writePriorities = new Dictionary<Type, int>();
|
internal readonly Dictionary<Type, int> writePriorities = new Dictionary<Type, int>();
|
||||||
|
@ -24,6 +26,8 @@ namespace Encompass
|
||||||
|
|
||||||
protected Engine()
|
protected Engine()
|
||||||
{
|
{
|
||||||
|
ID = Guid.NewGuid();
|
||||||
|
|
||||||
var sendsAttribute = GetType().GetCustomAttribute<Sends>(false);
|
var sendsAttribute = GetType().GetCustomAttribute<Sends>(false);
|
||||||
if (sendsAttribute != null)
|
if (sendsAttribute != null)
|
||||||
{
|
{
|
||||||
|
@ -62,6 +66,26 @@ namespace Encompass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is Engine)
|
||||||
|
{
|
||||||
|
return this.Equals((Engine)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(Engine other)
|
||||||
|
{
|
||||||
|
return other.ID == ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return ID.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
internal void AssignEntityManager(EntityManager entityManager)
|
internal void AssignEntityManager(EntityManager entityManager)
|
||||||
{
|
{
|
||||||
this.entityManager = entityManager;
|
this.entityManager = entityManager;
|
||||||
|
|
|
@ -1,420 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Encompass
|
|
||||||
{
|
|
||||||
public enum SearchSymbol
|
|
||||||
{
|
|
||||||
start,
|
|
||||||
finish
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DirectedGraph<T>
|
|
||||||
{
|
|
||||||
private class SimpleCycleComparer : IEqualityComparer<IEnumerable<T>>
|
|
||||||
{
|
|
||||||
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
|
|
||||||
{
|
|
||||||
return x.SequenceEqual(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(IEnumerable<T> obj)
|
|
||||||
{
|
|
||||||
return obj.Aggregate(0, (current, next) => current.GetHashCode() ^ next.GetHashCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<T> _vertices = new List<T>();
|
|
||||||
protected Dictionary<T, HashSet<T>> _neighbors = new Dictionary<T, HashSet<T>>();
|
|
||||||
|
|
||||||
public IEnumerable<T> Vertices { get { return _vertices; } }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GRAPH STRUCTURE METHODS
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void AddVertex(T vertex)
|
|
||||||
{
|
|
||||||
if (!VertexExists(vertex))
|
|
||||||
{
|
|
||||||
_vertices.Add(vertex);
|
|
||||||
_neighbors.Add(vertex, new HashSet<T>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddVertices(params T[] vertices)
|
|
||||||
{
|
|
||||||
foreach (var vertex in vertices)
|
|
||||||
{
|
|
||||||
AddVertex(vertex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool VertexExists(T vertex)
|
|
||||||
{
|
|
||||||
return Vertices.Contains(vertex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveVertex(T vertex)
|
|
||||||
{
|
|
||||||
var edgesToRemove = new List<Tuple<T, T>>();
|
|
||||||
|
|
||||||
if (VertexExists(vertex))
|
|
||||||
{
|
|
||||||
foreach (var entry in _neighbors)
|
|
||||||
{
|
|
||||||
if (entry.Value.Contains(vertex))
|
|
||||||
{
|
|
||||||
edgesToRemove.Add(Tuple.Create(entry.Key, vertex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var edge in edgesToRemove)
|
|
||||||
{
|
|
||||||
RemoveEdge(edge.Item1, edge.Item2);
|
|
||||||
}
|
|
||||||
|
|
||||||
_vertices.Remove(vertex);
|
|
||||||
_neighbors.Remove(vertex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddEdge(T v, T u)
|
|
||||||
{
|
|
||||||
if (VertexExists(v) && VertexExists(u))
|
|
||||||
{
|
|
||||||
_neighbors[v].Add(u);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddEdges(params Tuple<T, T>[] edges)
|
|
||||||
{
|
|
||||||
foreach (var edge in edges)
|
|
||||||
{
|
|
||||||
AddEdge(edge.Item1, edge.Item2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveEdge(T v, T u)
|
|
||||||
{
|
|
||||||
_neighbors[v].Remove(u);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<T> Neighbors(T vertex)
|
|
||||||
{
|
|
||||||
if (VertexExists(vertex))
|
|
||||||
{
|
|
||||||
return _neighbors[vertex];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Enumerable.Empty<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GRAPH ANALYSIS METHODS
|
|
||||||
*/
|
|
||||||
|
|
||||||
public Dictionary<T, Dictionary<SearchSymbol, uint>> NodeDFS()
|
|
||||||
{
|
|
||||||
var discovered = new HashSet<T>();
|
|
||||||
uint time = 0;
|
|
||||||
var output = new Dictionary<T, Dictionary<SearchSymbol, uint>>();
|
|
||||||
|
|
||||||
foreach (var vertex in Vertices)
|
|
||||||
{
|
|
||||||
output.Add(vertex, new Dictionary<SearchSymbol, uint>());
|
|
||||||
}
|
|
||||||
|
|
||||||
void dfsHelper(T v)
|
|
||||||
{
|
|
||||||
discovered.Add(v);
|
|
||||||
time++;
|
|
||||||
output[v].Add(SearchSymbol.start, time);
|
|
||||||
|
|
||||||
foreach (var neighbor in Neighbors(v))
|
|
||||||
{
|
|
||||||
if (!discovered.Contains(neighbor))
|
|
||||||
{
|
|
||||||
dfsHelper(neighbor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
time++;
|
|
||||||
output[v].Add(SearchSymbol.finish, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var vertex in Vertices)
|
|
||||||
{
|
|
||||||
if (!discovered.Contains(vertex))
|
|
||||||
{
|
|
||||||
dfsHelper(vertex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Cyclic()
|
|
||||||
{
|
|
||||||
return StronglyConnectedComponents().Any((scc) => scc.Count() > 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<T> TopologicalSort()
|
|
||||||
{
|
|
||||||
var dfs = NodeDFS();
|
|
||||||
var priority = new SortedList<uint, T>();
|
|
||||||
foreach (var entry in dfs)
|
|
||||||
{
|
|
||||||
priority.Add(entry.Value[SearchSymbol.finish], entry.Key);
|
|
||||||
}
|
|
||||||
return priority.Values.Reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<IEnumerable<T>> StronglyConnectedComponents()
|
|
||||||
{
|
|
||||||
var preorder = new Dictionary<T, uint>();
|
|
||||||
var lowlink = new Dictionary<T, uint>();
|
|
||||||
var sccFound = new Dictionary<T, bool>();
|
|
||||||
var sccQueue = new Stack<T>();
|
|
||||||
|
|
||||||
var result = new List<List<T>>();
|
|
||||||
|
|
||||||
uint preorderCounter = 0;
|
|
||||||
|
|
||||||
foreach (var source in Vertices)
|
|
||||||
{
|
|
||||||
if (!sccFound.ContainsKey(source))
|
|
||||||
{
|
|
||||||
var queue = new Stack<T>();
|
|
||||||
queue.Push(source);
|
|
||||||
|
|
||||||
while (queue.Count > 0)
|
|
||||||
{
|
|
||||||
var v = queue.Peek();
|
|
||||||
if (!preorder.ContainsKey(v))
|
|
||||||
{
|
|
||||||
preorderCounter++;
|
|
||||||
preorder[v] = preorderCounter;
|
|
||||||
}
|
|
||||||
|
|
||||||
var done = true;
|
|
||||||
var vNeighbors = Neighbors(v);
|
|
||||||
foreach (var w in vNeighbors)
|
|
||||||
{
|
|
||||||
if (!preorder.ContainsKey(w))
|
|
||||||
{
|
|
||||||
queue.Push(w);
|
|
||||||
done = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (done)
|
|
||||||
{
|
|
||||||
lowlink[v] = preorder[v];
|
|
||||||
foreach (var w in vNeighbors)
|
|
||||||
{
|
|
||||||
if (!sccFound.ContainsKey(w))
|
|
||||||
{
|
|
||||||
if (preorder[w] > preorder[v])
|
|
||||||
{
|
|
||||||
lowlink[v] = Math.Min(lowlink[v], lowlink[w]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lowlink[v] = Math.Min(lowlink[v], preorder[w]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
queue.Pop();
|
|
||||||
if (lowlink[v] == preorder[v])
|
|
||||||
{
|
|
||||||
sccFound[v] = true;
|
|
||||||
var scc = new List<T>
|
|
||||||
{
|
|
||||||
v
|
|
||||||
};
|
|
||||||
while (sccQueue.Count > 0 && preorder[sccQueue.Peek()] > preorder[v])
|
|
||||||
{
|
|
||||||
var k = sccQueue.Pop();
|
|
||||||
sccFound[k] = true;
|
|
||||||
scc.Add(k);
|
|
||||||
}
|
|
||||||
result.Add(scc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sccQueue.Push(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<IEnumerable<T>> SimpleCycles()
|
|
||||||
{
|
|
||||||
void unblock(T thisnode, HashSet<T> blocked, Dictionary<T, HashSet<T>> B)
|
|
||||||
{
|
|
||||||
var stack = new Stack<T>();
|
|
||||||
stack.Push(thisnode);
|
|
||||||
|
|
||||||
while (stack.Count > 0)
|
|
||||||
{
|
|
||||||
var node = stack.Pop();
|
|
||||||
if (blocked.Contains(thisnode))
|
|
||||||
{
|
|
||||||
blocked.Remove(thisnode);
|
|
||||||
if (B.ContainsKey(node))
|
|
||||||
{
|
|
||||||
foreach (var n in B[node])
|
|
||||||
{
|
|
||||||
if (!stack.Contains(n))
|
|
||||||
{
|
|
||||||
stack.Push(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
B[node].Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<List<T>> result = new List<List<T>>();
|
|
||||||
var subGraph = Clone();
|
|
||||||
|
|
||||||
var sccs = new Stack<IEnumerable<T>>();
|
|
||||||
foreach (var scc in StronglyConnectedComponents())
|
|
||||||
{
|
|
||||||
sccs.Push(scc);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (sccs.Count > 0)
|
|
||||||
{
|
|
||||||
var scc = new Stack<T>(sccs.Pop());
|
|
||||||
var startNode = scc.Pop();
|
|
||||||
var path = new Stack<T>();
|
|
||||||
path.Push(startNode);
|
|
||||||
var blocked = new HashSet<T>
|
|
||||||
{
|
|
||||||
startNode
|
|
||||||
};
|
|
||||||
var closed = new HashSet<T>();
|
|
||||||
var B = new Dictionary<T, HashSet<T>>();
|
|
||||||
var stack = new Stack<Tuple<T, Stack<T>>>();
|
|
||||||
stack.Push(Tuple.Create(startNode, new Stack<T>(subGraph.Neighbors(startNode))));
|
|
||||||
|
|
||||||
while (stack.Count > 0)
|
|
||||||
{
|
|
||||||
var entry = stack.Peek();
|
|
||||||
var thisnode = entry.Item1;
|
|
||||||
var neighbors = entry.Item2;
|
|
||||||
|
|
||||||
if (neighbors.Count > 0)
|
|
||||||
{
|
|
||||||
var nextNode = neighbors.Pop();
|
|
||||||
|
|
||||||
if (nextNode.Equals(startNode))
|
|
||||||
{
|
|
||||||
var resultPath = new List<T>();
|
|
||||||
foreach (var v in path)
|
|
||||||
{
|
|
||||||
resultPath.Add(v);
|
|
||||||
}
|
|
||||||
result.Add(resultPath);
|
|
||||||
foreach (var v in path)
|
|
||||||
{
|
|
||||||
closed.Add(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!blocked.Contains(nextNode))
|
|
||||||
{
|
|
||||||
path.Push(nextNode);
|
|
||||||
stack.Push(Tuple.Create(nextNode, new Stack<T>(subGraph.Neighbors(nextNode))));
|
|
||||||
closed.Remove(nextNode);
|
|
||||||
blocked.Add(nextNode);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (neighbors.Count == 0)
|
|
||||||
{
|
|
||||||
if (closed.Contains(thisnode))
|
|
||||||
{
|
|
||||||
unblock(thisnode, blocked, B);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var neighbor in subGraph.Neighbors(thisnode))
|
|
||||||
{
|
|
||||||
if (!B.ContainsKey(neighbor))
|
|
||||||
{
|
|
||||||
B[neighbor] = new HashSet<T>();
|
|
||||||
}
|
|
||||||
B[neighbor].Add(thisnode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.Pop();
|
|
||||||
path.Pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subGraph.RemoveVertex(startNode);
|
|
||||||
var H = subGraph.SubGraph(scc.ToArray());
|
|
||||||
var HSccs = H.StronglyConnectedComponents();
|
|
||||||
foreach (var HScc in HSccs)
|
|
||||||
{
|
|
||||||
sccs.Push(HScc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.Distinct(new SimpleCycleComparer());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectedGraph<T> Clone()
|
|
||||||
{
|
|
||||||
var clone = new DirectedGraph<T>();
|
|
||||||
clone.AddVertices(Vertices.ToArray());
|
|
||||||
|
|
||||||
foreach (var v in Vertices)
|
|
||||||
{
|
|
||||||
foreach (var n in Neighbors(v))
|
|
||||||
{
|
|
||||||
clone.AddEdge(v, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectedGraph<T> SubGraph(params T[] subVertices)
|
|
||||||
{
|
|
||||||
var subGraph = new DirectedGraph<T>();
|
|
||||||
subGraph.AddVertices(subVertices.ToArray());
|
|
||||||
|
|
||||||
foreach (var v in Vertices)
|
|
||||||
{
|
|
||||||
if (Vertices.Contains(v))
|
|
||||||
{
|
|
||||||
var neighbors = Neighbors(v);
|
|
||||||
foreach (var u in neighbors)
|
|
||||||
{
|
|
||||||
if (subVertices.Contains(u))
|
|
||||||
{
|
|
||||||
subGraph.AddEdge(v, u);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return subGraph;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,6 +4,8 @@ using System.Reflection;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Encompass.Exceptions;
|
using Encompass.Exceptions;
|
||||||
using Encompass.Engines;
|
using Encompass.Engines;
|
||||||
|
using MoonTools.Core.Graph;
|
||||||
|
using MoonTools.Core.Graph.Extensions;
|
||||||
|
|
||||||
namespace Encompass
|
namespace Encompass
|
||||||
{
|
{
|
||||||
|
@ -18,7 +20,7 @@ namespace Encompass
|
||||||
public class WorldBuilder
|
public class WorldBuilder
|
||||||
{
|
{
|
||||||
private readonly List<Engine> engines = new List<Engine>();
|
private readonly List<Engine> engines = new List<Engine>();
|
||||||
private readonly DirectedGraph<Engine> engineGraph = new DirectedGraph<Engine>();
|
private readonly DirectedGraph<Engine, Unit> engineGraph = GraphBuilder.DirectedGraph<Engine>();
|
||||||
|
|
||||||
private readonly ComponentManager componentManager;
|
private readonly ComponentManager componentManager;
|
||||||
private readonly EntityManager entityManager;
|
private readonly EntityManager entityManager;
|
||||||
|
@ -102,7 +104,7 @@ namespace Encompass
|
||||||
engine.AssignComponentMessageManager(componentMessageManager);
|
engine.AssignComponentMessageManager(componentMessageManager);
|
||||||
|
|
||||||
engines.Add(engine);
|
engines.Add(engine);
|
||||||
engineGraph.AddVertex(engine);
|
engineGraph.AddNode(engine);
|
||||||
|
|
||||||
var messageReceiveTypes = engine.receiveTypes;
|
var messageReceiveTypes = engine.receiveTypes;
|
||||||
var messageSendTypes = engine.sendTypes;
|
var messageSendTypes = engine.sendTypes;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<RootNamespace>Encompass</RootNamespace>
|
<RootNamespace>Encompass</RootNamespace>
|
||||||
|
@ -24,5 +24,6 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Collections.Pooled" Version="1.0.82"/>
|
<PackageReference Include="Collections.Pooled" Version="1.0.82"/>
|
||||||
|
<PackageReference Include="MoonTools.Core.Graph" Version="1.0.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -1,374 +0,0 @@
|
||||||
using NUnit.Framework;
|
|
||||||
using FluentAssertions;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using Encompass;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Tests
|
|
||||||
{
|
|
||||||
public class DirectedGraphTest
|
|
||||||
{
|
|
||||||
[Test]
|
|
||||||
public void AddVertex()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertex(4);
|
|
||||||
|
|
||||||
Assert.That(myGraph.Vertices, Does.Contain(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void AddVertices()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(4, 20, 69);
|
|
||||||
|
|
||||||
Assert.IsTrue(myGraph.VertexExists(4));
|
|
||||||
Assert.IsTrue(myGraph.VertexExists(20));
|
|
||||||
Assert.IsTrue(myGraph.VertexExists(69));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void AddEdge()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(5, 6);
|
|
||||||
myGraph.AddEdge(5, 6);
|
|
||||||
|
|
||||||
Assert.That(myGraph.Neighbors(5), Does.Contain(6));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void AddEdges()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(1, 2, 3, 4);
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(2, 4),
|
|
||||||
Tuple.Create(3, 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
Assert.That(myGraph.Neighbors(1), Does.Contain(2));
|
|
||||||
Assert.That(myGraph.Neighbors(2), Does.Contain(3));
|
|
||||||
Assert.That(myGraph.Neighbors(2), Does.Contain(4));
|
|
||||||
Assert.That(myGraph.Neighbors(3), Does.Contain(4));
|
|
||||||
Assert.That(myGraph.Neighbors(1), Does.Not.Contain(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void RemoveEdge()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(1, 2, 3, 4);
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(2, 4),
|
|
||||||
Tuple.Create(3, 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
myGraph.RemoveEdge(2, 3);
|
|
||||||
|
|
||||||
Assert.That(myGraph.Neighbors(2), Does.Not.Contain(3));
|
|
||||||
Assert.That(myGraph.Neighbors(2), Does.Contain(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void RemoveVertex()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(1, 2, 3, 4);
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(2, 4),
|
|
||||||
Tuple.Create(3, 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
myGraph.RemoveVertex(2);
|
|
||||||
|
|
||||||
myGraph.Vertices.Should().NotContain(2);
|
|
||||||
myGraph.Neighbors(1).Should().NotContain(2);
|
|
||||||
myGraph.Neighbors(3).Should().Contain(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void NodeDFS()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<char>();
|
|
||||||
myGraph.AddVertices('a', 'b', 'c', 'd');
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create('a', 'b'),
|
|
||||||
Tuple.Create('a', 'c'),
|
|
||||||
Tuple.Create('b', 'd')
|
|
||||||
);
|
|
||||||
|
|
||||||
var result = myGraph.NodeDFS();
|
|
||||||
|
|
||||||
Assert.That(result['a'][SearchSymbol.start], Is.EqualTo(1));
|
|
||||||
Assert.That(result['a'][SearchSymbol.finish], Is.EqualTo(8));
|
|
||||||
|
|
||||||
Assert.That(result['b'][SearchSymbol.start], Is.EqualTo(2));
|
|
||||||
Assert.That(result['b'][SearchSymbol.finish], Is.EqualTo(5));
|
|
||||||
|
|
||||||
Assert.That(result['c'][SearchSymbol.start], Is.EqualTo(6));
|
|
||||||
Assert.That(result['c'][SearchSymbol.finish], Is.EqualTo(7));
|
|
||||||
|
|
||||||
Assert.That(result['d'][SearchSymbol.start], Is.EqualTo(3));
|
|
||||||
Assert.That(result['d'][SearchSymbol.finish], Is.EqualTo(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TopologicalSortSimple()
|
|
||||||
{
|
|
||||||
var simpleGraph = new DirectedGraph<char>();
|
|
||||||
simpleGraph.AddVertices('a', 'b', 'c', 'd');
|
|
||||||
simpleGraph.AddEdges(
|
|
||||||
Tuple.Create('a', 'b'),
|
|
||||||
Tuple.Create('a', 'c'),
|
|
||||||
Tuple.Create('b', 'a'),
|
|
||||||
Tuple.Create('b', 'd')
|
|
||||||
);
|
|
||||||
|
|
||||||
Assert.That(simpleGraph.TopologicalSort(), Is.EqualTo(new char[] { 'a', 'c', 'b', 'd' }));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TopologicalSortComplex()
|
|
||||||
{
|
|
||||||
var complexGraph = new DirectedGraph<char>();
|
|
||||||
complexGraph.AddVertices('a', 'b', 'c', 'd', 'e', 'f', 'g', 't', 'm');
|
|
||||||
complexGraph.AddEdges(
|
|
||||||
Tuple.Create('a', 'b'),
|
|
||||||
Tuple.Create('a', 'c'),
|
|
||||||
Tuple.Create('a', 'd'),
|
|
||||||
Tuple.Create('b', 'f'),
|
|
||||||
Tuple.Create('b', 'g'),
|
|
||||||
Tuple.Create('c', 'g'),
|
|
||||||
Tuple.Create('e', 't'),
|
|
||||||
Tuple.Create('t', 'm')
|
|
||||||
);
|
|
||||||
|
|
||||||
Assert.That(
|
|
||||||
complexGraph.TopologicalSort(),
|
|
||||||
Is.EqualTo(new char[] { 'e', 't', 'm', 'a', 'd', 'c', 'b', 'g', 'f' })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void StronglyConnectedComponentsSimple()
|
|
||||||
{
|
|
||||||
var simpleGraph = new DirectedGraph<int>();
|
|
||||||
simpleGraph.AddVertices(1, 2, 3);
|
|
||||||
simpleGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(3, 2),
|
|
||||||
Tuple.Create(2, 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
var result = simpleGraph.StronglyConnectedComponents();
|
|
||||||
var scc = new int[] { 1, 2, 3 };
|
|
||||||
|
|
||||||
result.Should().ContainEquivalentOf(scc);
|
|
||||||
Assert.That(result.Count, Is.EqualTo(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void StronglyConnectedComponentsMedium()
|
|
||||||
{
|
|
||||||
var mediumGraph = new DirectedGraph<int>();
|
|
||||||
mediumGraph.AddVertices(1, 2, 3, 4);
|
|
||||||
mediumGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(1, 3),
|
|
||||||
Tuple.Create(1, 4),
|
|
||||||
Tuple.Create(4, 2),
|
|
||||||
Tuple.Create(3, 4),
|
|
||||||
Tuple.Create(2, 3)
|
|
||||||
);
|
|
||||||
|
|
||||||
var result = mediumGraph.StronglyConnectedComponents();
|
|
||||||
var sccA = new int[] { 2, 3, 4 };
|
|
||||||
var sccB = new int[] { 1 };
|
|
||||||
|
|
||||||
result.Should().ContainEquivalentOf(sccA);
|
|
||||||
result.Should().ContainEquivalentOf(sccB);
|
|
||||||
Assert.That(result.Count, Is.EqualTo(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void StronglyConnectedComponentsComplex()
|
|
||||||
{
|
|
||||||
var complexGraph = new DirectedGraph<int>();
|
|
||||||
complexGraph.AddVertices(1, 2, 3, 4, 5, 6, 7, 8);
|
|
||||||
complexGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(2, 8),
|
|
||||||
Tuple.Create(3, 4),
|
|
||||||
Tuple.Create(3, 7),
|
|
||||||
Tuple.Create(4, 5),
|
|
||||||
Tuple.Create(5, 3),
|
|
||||||
Tuple.Create(5, 6),
|
|
||||||
Tuple.Create(7, 4),
|
|
||||||
Tuple.Create(7, 6),
|
|
||||||
Tuple.Create(8, 1),
|
|
||||||
Tuple.Create(8, 7)
|
|
||||||
);
|
|
||||||
|
|
||||||
var result = complexGraph.StronglyConnectedComponents();
|
|
||||||
var sccA = new int[] { 3, 4, 5, 7 };
|
|
||||||
var sccB = new int[] { 1, 2, 8 };
|
|
||||||
var sccC = new int[] { 6 };
|
|
||||||
|
|
||||||
result.Should().ContainEquivalentOf(sccA);
|
|
||||||
result.Should().ContainEquivalentOf(sccB);
|
|
||||||
result.Should().ContainEquivalentOf(sccC);
|
|
||||||
Assert.That(result.Count, Is.EqualTo(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Clone()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(1, 2, 3, 4);
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 1),
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(2, 1),
|
|
||||||
Tuple.Create(3, 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
var clone = myGraph.Clone();
|
|
||||||
Assert.That(clone, Is.Not.EqualTo(myGraph));
|
|
||||||
clone.Vertices.Should().BeEquivalentTo(1, 2, 3, 4);
|
|
||||||
clone.Neighbors(1).Should().BeEquivalentTo(1, 2);
|
|
||||||
clone.Neighbors(2).Should().BeEquivalentTo(3, 1);
|
|
||||||
clone.Neighbors(3).Should().BeEquivalentTo(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void SubGraph()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(1, 2, 3, 4);
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 1),
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(2, 1),
|
|
||||||
Tuple.Create(3, 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
var subGraph = myGraph.SubGraph(1, 2, 3);
|
|
||||||
subGraph.Vertices.Should().BeEquivalentTo(1, 2, 3);
|
|
||||||
subGraph.Neighbors(1).Should().BeEquivalentTo(1, 2);
|
|
||||||
subGraph.Neighbors(2).Should().BeEquivalentTo(1, 3);
|
|
||||||
subGraph.Neighbors(3).Should().NotContain(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void SimpleCyclesSimple()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(0, 1, 2);
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create(0, 0),
|
|
||||||
Tuple.Create(0, 1),
|
|
||||||
Tuple.Create(0, 2),
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 0),
|
|
||||||
Tuple.Create(2, 1),
|
|
||||||
Tuple.Create(2, 2)
|
|
||||||
);
|
|
||||||
|
|
||||||
var result = myGraph.SimpleCycles();
|
|
||||||
|
|
||||||
var cycleA = new int[] { 0 };
|
|
||||||
var cycleB = new int[] { 0, 1, 2 };
|
|
||||||
var cycleC = new int[] { 0, 2 };
|
|
||||||
var cycleD = new int[] { 1, 2 };
|
|
||||||
var cycleE = new int[] { 2 };
|
|
||||||
|
|
||||||
result.Should().ContainEquivalentOf(cycleA);
|
|
||||||
result.Should().ContainEquivalentOf(cycleB);
|
|
||||||
result.Should().ContainEquivalentOf(cycleC);
|
|
||||||
result.Should().ContainEquivalentOf(cycleD);
|
|
||||||
result.Should().ContainEquivalentOf(cycleE);
|
|
||||||
result.Should().HaveCount(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void SimpleCyclesComplex()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create(0, 1),
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(3, 0),
|
|
||||||
Tuple.Create(0, 3),
|
|
||||||
Tuple.Create(3, 4),
|
|
||||||
Tuple.Create(4, 5),
|
|
||||||
Tuple.Create(5, 0),
|
|
||||||
Tuple.Create(0, 1),
|
|
||||||
Tuple.Create(1, 6),
|
|
||||||
Tuple.Create(6, 7),
|
|
||||||
Tuple.Create(7, 8),
|
|
||||||
Tuple.Create(8, 0),
|
|
||||||
Tuple.Create(8, 9)
|
|
||||||
);
|
|
||||||
|
|
||||||
var result = myGraph.SimpleCycles();
|
|
||||||
var cycleA = new int[] { 0, 3 };
|
|
||||||
var cycleB = new int[] { 0, 1, 2, 3, 4, 5 };
|
|
||||||
var cycleC = new int[] { 0, 1, 2, 3 };
|
|
||||||
var cycleD = new int[] { 0, 3, 4, 5 };
|
|
||||||
var cycleE = new int[] { 0, 1, 6, 7, 8 };
|
|
||||||
|
|
||||||
result.Should().ContainEquivalentOf(cycleA);
|
|
||||||
result.Should().ContainEquivalentOf(cycleB);
|
|
||||||
result.Should().ContainEquivalentOf(cycleC);
|
|
||||||
result.Should().ContainEquivalentOf(cycleD);
|
|
||||||
result.Should().ContainEquivalentOf(cycleE);
|
|
||||||
result.Should().HaveCount(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Cyclic()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(1, 2, 3, 4);
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(3, 1),
|
|
||||||
Tuple.Create(3, 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
Assert.That(myGraph.Cyclic(), Is.True);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Acyclic()
|
|
||||||
{
|
|
||||||
var myGraph = new DirectedGraph<int>();
|
|
||||||
myGraph.AddVertices(1, 2, 3, 4);
|
|
||||||
myGraph.AddEdges(
|
|
||||||
Tuple.Create(1, 2),
|
|
||||||
Tuple.Create(2, 3),
|
|
||||||
Tuple.Create(3, 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
Assert.That(myGraph.Cyclic(), Is.False);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -462,13 +462,20 @@ namespace Tests
|
||||||
{
|
{
|
||||||
var worldBuilder = new WorldBuilder();
|
var worldBuilder = new WorldBuilder();
|
||||||
|
|
||||||
|
worldBuilder.AddEngine(new AEngine());
|
||||||
|
worldBuilder.AddEngine(new BEngine());
|
||||||
|
worldBuilder.AddEngine(new CEngine());
|
||||||
|
worldBuilder.AddEngine(new DEngine());
|
||||||
|
|
||||||
|
Assert.DoesNotThrow(() => worldBuilder.Build());
|
||||||
|
|
||||||
|
worldBuilder = new WorldBuilder();
|
||||||
|
|
||||||
var engineA = worldBuilder.AddEngine(new AEngine());
|
var engineA = worldBuilder.AddEngine(new AEngine());
|
||||||
var engineB = worldBuilder.AddEngine(new BEngine());
|
var engineB = worldBuilder.AddEngine(new BEngine());
|
||||||
var engineC = worldBuilder.AddEngine(new CEngine());
|
var engineC = worldBuilder.AddEngine(new CEngine());
|
||||||
var engineD = worldBuilder.AddEngine(new DEngine());
|
var engineD = worldBuilder.AddEngine(new DEngine());
|
||||||
|
|
||||||
Assert.DoesNotThrow(() => worldBuilder.Build());
|
|
||||||
|
|
||||||
var world = worldBuilder.Build();
|
var world = worldBuilder.Build();
|
||||||
|
|
||||||
world.Update(0.01f);
|
world.Update(0.01f);
|
||||||
|
|
Loading…
Reference in New Issue