implement directed weighted multigraph
parent
0fb16abcbb
commit
36f62e1b4f
|
@ -10,7 +10,7 @@ namespace MoonTools.Core.Graph
|
||||||
finish
|
finish
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DirectedGraph<TNode, TEdgeData> : IGraph<TNode, TEdgeData>
|
public class DirectedGraph<TNode, TEdgeData> : IGraph<TNode, TEdgeData> where TNode : IEquatable<TNode>
|
||||||
{
|
{
|
||||||
protected HashSet<TNode> nodes = new HashSet<TNode>();
|
protected HashSet<TNode> nodes = new HashSet<TNode>();
|
||||||
protected HashSet<(TNode, TNode)> edges = new HashSet<(TNode, TNode)>();
|
protected HashSet<(TNode, TNode)> edges = new HashSet<(TNode, TNode)>();
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using MoreLinq;
|
||||||
|
|
||||||
|
namespace MoonTools.Core.Graph
|
||||||
|
{
|
||||||
|
public class DirectedWeightedMultiGraph<TNode, TEdgeData> : IGraph<TNode, TEdgeData> where TNode : IEquatable<TNode>
|
||||||
|
{
|
||||||
|
protected HashSet<TNode> nodes = new HashSet<TNode>();
|
||||||
|
protected Dictionary<TNode, HashSet<TNode>> neighbors = new Dictionary<TNode, HashSet<TNode>>();
|
||||||
|
protected Dictionary<(TNode, TNode), HashSet<Guid>> edges = new Dictionary<(TNode, TNode), HashSet<Guid>>();
|
||||||
|
protected Dictionary<Guid, (TNode, TNode)> IDToEdge = new Dictionary<Guid, (TNode, TNode)>();
|
||||||
|
protected Dictionary<Guid, int> weights = new Dictionary<Guid, int>();
|
||||||
|
protected Dictionary<Guid, TEdgeData> edgeToEdgeData = new Dictionary<Guid, TEdgeData>();
|
||||||
|
|
||||||
|
// store search sets to prevent GC
|
||||||
|
protected HashSet<TNode> openSet = new HashSet<TNode>();
|
||||||
|
protected HashSet<TNode> closedSet = new HashSet<TNode>();
|
||||||
|
protected Dictionary<TNode, int> gScore = new Dictionary<TNode, int>();
|
||||||
|
protected Dictionary<TNode, int> fScore = new Dictionary<TNode, int>();
|
||||||
|
protected Dictionary<TNode, Guid> cameFrom = new Dictionary<TNode, Guid>();
|
||||||
|
|
||||||
|
public IEnumerable<TNode> Nodes => nodes;
|
||||||
|
|
||||||
|
public void AddNode(TNode node)
|
||||||
|
{
|
||||||
|
nodes.Add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddNodes(params TNode[] nodes)
|
||||||
|
{
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
AddNode(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEdge(TNode v, TNode u, int weight, TEdgeData data)
|
||||||
|
{
|
||||||
|
if (Exists(v) && Exists(u))
|
||||||
|
{
|
||||||
|
var id = Guid.NewGuid();
|
||||||
|
if (!neighbors.ContainsKey(v))
|
||||||
|
{
|
||||||
|
neighbors[v] = new HashSet<TNode>();
|
||||||
|
}
|
||||||
|
neighbors[v].Add(u);
|
||||||
|
weights.Add(id, weight);
|
||||||
|
if (!edges.ContainsKey((v, u)))
|
||||||
|
{
|
||||||
|
edges[(v, u)] = new HashSet<Guid>();
|
||||||
|
}
|
||||||
|
edges[(v, u)].Add(id);
|
||||||
|
edgeToEdgeData.Add(id, data);
|
||||||
|
IDToEdge.Add(id, (v, u));
|
||||||
|
}
|
||||||
|
else if (!Exists(v))
|
||||||
|
{
|
||||||
|
throw new InvalidVertexException("Vertex {0} does not exist in the graph", v);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidVertexException("Vertex {0} does not exist in the graph", u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEdges(params (TNode, TNode, int, TEdgeData)[] edges)
|
||||||
|
{
|
||||||
|
foreach (var edge in edges)
|
||||||
|
{
|
||||||
|
AddEdge(edge.Item1, edge.Item2, edge.Item3, edge.Item4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
nodes.Clear();
|
||||||
|
neighbors.Clear();
|
||||||
|
weights.Clear();
|
||||||
|
edgeToEdgeData.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckNodes(params TNode[] givenNodes)
|
||||||
|
{
|
||||||
|
foreach (var node in givenNodes)
|
||||||
|
{
|
||||||
|
if (!Exists(node))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Vertex {node} does not exist in the graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Guid> EdgeIDs(TNode v, TNode u)
|
||||||
|
{
|
||||||
|
CheckNodes(v, u);
|
||||||
|
return edges.ContainsKey((v, u)) ? edges[(v, u)] : Enumerable.Empty<Guid>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists(TNode node)
|
||||||
|
{
|
||||||
|
return nodes.Contains(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists(TNode v, TNode u)
|
||||||
|
{
|
||||||
|
CheckNodes(v, u);
|
||||||
|
return edges.ContainsKey((v, u));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<TNode> Neighbors(TNode node)
|
||||||
|
{
|
||||||
|
CheckNodes(node);
|
||||||
|
return neighbors.ContainsKey(node) ? neighbors[node] : Enumerable.Empty<TNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<int> Weights(TNode v, TNode u)
|
||||||
|
{
|
||||||
|
CheckNodes(v, u);
|
||||||
|
return edges[(v, u)].Select(id => weights[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TEdgeData EdgeData(Guid id)
|
||||||
|
{
|
||||||
|
if (!edgeToEdgeData.ContainsKey(id))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Edge {id} does not exist in the graph.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return edgeToEdgeData[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Guid> ReconstructPath(Dictionary<TNode, Guid> cameFrom, TNode currentNode)
|
||||||
|
{
|
||||||
|
while (cameFrom.ContainsKey(currentNode))
|
||||||
|
{
|
||||||
|
var edgeID = cameFrom[currentNode];
|
||||||
|
var edge = IDToEdge[edgeID];
|
||||||
|
currentNode = edge.Item1;
|
||||||
|
yield return edgeID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Guid> AStarShortestPath(TNode start, TNode end, Func<TNode, TNode, int> heuristic)
|
||||||
|
{
|
||||||
|
CheckNodes(start, end);
|
||||||
|
|
||||||
|
openSet.Clear();
|
||||||
|
closedSet.Clear();
|
||||||
|
gScore.Clear();
|
||||||
|
fScore.Clear();
|
||||||
|
cameFrom.Clear();
|
||||||
|
|
||||||
|
openSet.Add(start);
|
||||||
|
|
||||||
|
gScore[start] = 0;
|
||||||
|
fScore[start] = heuristic(start, end);
|
||||||
|
|
||||||
|
while (openSet.Any())
|
||||||
|
{
|
||||||
|
var currentNode = openSet.MinBy(node => fScore[node]).First();
|
||||||
|
|
||||||
|
if (currentNode.Equals(end))
|
||||||
|
{
|
||||||
|
return ReconstructPath(cameFrom, currentNode).Reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
openSet.Remove(currentNode);
|
||||||
|
closedSet.Add(currentNode);
|
||||||
|
|
||||||
|
foreach (var neighbor in Neighbors(currentNode))
|
||||||
|
{
|
||||||
|
if (!closedSet.Contains(neighbor))
|
||||||
|
{
|
||||||
|
var lowestEdgeID = EdgeIDs(currentNode, neighbor).MinBy(id => weights[id]).First();
|
||||||
|
var weight = weights[lowestEdgeID];
|
||||||
|
|
||||||
|
var tentativeGScore = gScore.ContainsKey(currentNode) ? gScore[currentNode] + weight : int.MaxValue;
|
||||||
|
|
||||||
|
if (!openSet.Contains(neighbor) || tentativeGScore < gScore[neighbor])
|
||||||
|
{
|
||||||
|
cameFrom[neighbor] = lowestEdgeID;
|
||||||
|
gScore[neighbor] = tentativeGScore;
|
||||||
|
fScore[neighbor] = tentativeGScore + heuristic(neighbor, end);
|
||||||
|
openSet.Add(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enumerable.Empty<Guid>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MoonTools.Core.Graph
|
namespace MoonTools.Core.Graph
|
||||||
{
|
{
|
||||||
public interface IGraph<TNode, TEdgeData>
|
public interface IGraph<TNode, TEdgeData> where TNode : System.IEquatable<TNode>
|
||||||
{
|
{
|
||||||
IEnumerable<TNode> Nodes { get; }
|
IEnumerable<TNode> Nodes { get; }
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
<PropertyGroup>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
</PropertyGroup>
|
||||||
</PropertyGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="morelinq" Version="3.2.0"/>
|
||||||
</Project>
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -1,7 +0,0 @@
|
||||||
namespace MoonTools.Core.Graph
|
|
||||||
{
|
|
||||||
public class UndirectedGraph<T>
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,8 +8,6 @@ using MoonTools.Core.Graph;
|
||||||
|
|
||||||
namespace Tests
|
namespace Tests
|
||||||
{
|
{
|
||||||
struct EdgeData { }
|
|
||||||
|
|
||||||
public class DirectedGraphTest
|
public class DirectedGraphTest
|
||||||
{
|
{
|
||||||
EdgeData dummyEdgeData;
|
EdgeData dummyEdgeData;
|
||||||
|
@ -399,17 +397,12 @@ namespace Tests
|
||||||
myGraph.Exists((1, 2)).Should().BeTrue();
|
myGraph.Exists((1, 2)).Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestEdgeData
|
|
||||||
{
|
|
||||||
public int testNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void EdgeData()
|
public void EdgeData()
|
||||||
{
|
{
|
||||||
var myGraph = new DirectedGraph<int, TestEdgeData>();
|
var myGraph = new DirectedGraph<int, NumEdgeData>();
|
||||||
myGraph.AddNodes(1, 2);
|
myGraph.AddNodes(1, 2);
|
||||||
myGraph.AddEdge(1, 2, new TestEdgeData { testNum = 4 });
|
myGraph.AddEdge(1, 2, new NumEdgeData { testNum = 4 });
|
||||||
|
|
||||||
myGraph.EdgeData((1, 2)).testNum.Should().Be(4);
|
myGraph.EdgeData((1, 2)).testNum.Should().Be(4);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using FluentAssertions;
|
||||||
|
|
||||||
|
using MoonTools.Core.Graph;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Tests
|
||||||
|
{
|
||||||
|
public class DirectedWeightedMultiGraphTests
|
||||||
|
{
|
||||||
|
EdgeData dummyEdgeData;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddNode()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNode(4);
|
||||||
|
|
||||||
|
Assert.That(myGraph.Nodes, Does.Contain(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddNodes()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(4, 20, 69);
|
||||||
|
|
||||||
|
myGraph.Exists(4).Should().BeTrue();
|
||||||
|
myGraph.Exists(20).Should().BeTrue();
|
||||||
|
myGraph.Exists(69).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddEdge()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(5, 6);
|
||||||
|
myGraph.AddEdge(5, 6, 10, dummyEdgeData);
|
||||||
|
|
||||||
|
myGraph.Neighbors(5).Should().Contain(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddEdges()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
(1, 2, 5, dummyEdgeData),
|
||||||
|
(2, 3, 6, dummyEdgeData),
|
||||||
|
(2, 4, 7, dummyEdgeData),
|
||||||
|
(3, 4, 8, dummyEdgeData)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.Neighbors(1).Should().Contain(2);
|
||||||
|
myGraph.Neighbors(2).Should().Contain(3);
|
||||||
|
myGraph.Neighbors(2).Should().Contain(4);
|
||||||
|
myGraph.Neighbors(3).Should().Contain(4);
|
||||||
|
myGraph.Neighbors(1).Should().NotContain(4);
|
||||||
|
|
||||||
|
myGraph.EdgeIDs(1, 2).Should().HaveCount(1);
|
||||||
|
myGraph.Weights(1, 2).Should().Contain(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddMultiEdges()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
(1, 2, 5, dummyEdgeData),
|
||||||
|
(2, 3, 6, dummyEdgeData),
|
||||||
|
(2, 4, 7, dummyEdgeData),
|
||||||
|
(2, 4, 8, dummyEdgeData)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.Neighbors(1).Should().Contain(2);
|
||||||
|
myGraph.Neighbors(2).Should().Contain(3);
|
||||||
|
myGraph.Neighbors(2).Should().Contain(4);
|
||||||
|
myGraph.Neighbors(1).Should().NotContain(4);
|
||||||
|
|
||||||
|
myGraph.EdgeIDs(2, 4).Should().HaveCount(2);
|
||||||
|
myGraph.Weights(2, 4).Should().HaveCount(2);
|
||||||
|
myGraph.Weights(2, 4).Should().Contain(7);
|
||||||
|
myGraph.Weights(2, 4).Should().Contain(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(1, 2, 3, 4);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
(1, 2, 5, dummyEdgeData),
|
||||||
|
(2, 3, 6, dummyEdgeData),
|
||||||
|
(2, 4, 7, dummyEdgeData),
|
||||||
|
(2, 4, 8, dummyEdgeData)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.Clear();
|
||||||
|
|
||||||
|
myGraph.Invoking(x => x.Neighbors(1)).Should().Throw<System.ArgumentException>();
|
||||||
|
myGraph.Invoking(x => x.Weights(1, 2)).Should().Throw<System.ArgumentException>();
|
||||||
|
myGraph.Invoking(x => x.EdgeIDs(1, 2)).Should().Throw<System.ArgumentException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Edges()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(1, 2, 3);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
(1, 2, 4, dummyEdgeData),
|
||||||
|
(1, 2, 3, dummyEdgeData),
|
||||||
|
(2, 3, 5, dummyEdgeData)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.EdgeIDs(1, 2).Should().HaveCount(2);
|
||||||
|
myGraph.EdgeIDs(2, 3).Should().HaveCount(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void NodeExists()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(1, 2, 3);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
(1, 2, 4, dummyEdgeData),
|
||||||
|
(1, 2, 3, dummyEdgeData),
|
||||||
|
(2, 3, 5, dummyEdgeData)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.Exists(1).Should().BeTrue();
|
||||||
|
myGraph.Exists(2).Should().BeTrue();
|
||||||
|
myGraph.Exists(3).Should().BeTrue();
|
||||||
|
myGraph.Exists(4).Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EdgeExists()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(1, 2, 3);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
(1, 2, 4, dummyEdgeData),
|
||||||
|
(1, 2, 3, dummyEdgeData),
|
||||||
|
(2, 3, 5, dummyEdgeData)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.Exists(1, 2).Should().BeTrue();
|
||||||
|
myGraph.Exists(2, 3).Should().BeTrue();
|
||||||
|
myGraph.Exists(1, 3).Should().BeFalse();
|
||||||
|
myGraph.Invoking(x => x.Exists(3, 4)).Should().Throw<System.ArgumentException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Neighbors()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(1, 2, 3);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
(1, 2, 4, dummyEdgeData),
|
||||||
|
(1, 2, 3, dummyEdgeData),
|
||||||
|
(2, 3, 5, dummyEdgeData)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.Neighbors(1).Should().Contain(2);
|
||||||
|
myGraph.Neighbors(2).Should().Contain(3);
|
||||||
|
myGraph.Neighbors(1).Should().NotContain(3);
|
||||||
|
myGraph.Invoking(x => x.Neighbors(4)).Should().Throw<System.ArgumentException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Weights()
|
||||||
|
{
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, EdgeData>();
|
||||||
|
myGraph.AddNodes(1, 2, 3);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
(1, 2, 4, dummyEdgeData),
|
||||||
|
(1, 2, 3, dummyEdgeData),
|
||||||
|
(2, 3, 5, dummyEdgeData)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.Weights(1, 2).Should().Contain(3);
|
||||||
|
myGraph.Weights(1, 2).Should().Contain(4);
|
||||||
|
myGraph.Weights(2, 3).Should().Contain(5);
|
||||||
|
myGraph.Invoking(x => x.Weights(3, 4)).Should().Throw<System.ArgumentException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EdgeData()
|
||||||
|
{
|
||||||
|
var a = new NumEdgeData { testNum = 3 };
|
||||||
|
var b = new NumEdgeData { testNum = 4 };
|
||||||
|
var c = new NumEdgeData { testNum = 5 };
|
||||||
|
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<int, NumEdgeData>();
|
||||||
|
myGraph.AddNodes(1, 2, 3);
|
||||||
|
myGraph.AddEdges(
|
||||||
|
(1, 2, 4, a),
|
||||||
|
(1, 2, 3, b),
|
||||||
|
(2, 3, 5, c)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph.EdgeIDs(1, 2).Select(id => myGraph.EdgeData(id)).Should().Contain(a);
|
||||||
|
myGraph.EdgeIDs(1, 2).Select(id => myGraph.EdgeData(id)).Should().Contain(b);
|
||||||
|
myGraph.EdgeIDs(2, 3).Select(id => myGraph.EdgeData(id)).Should().Contain(c);
|
||||||
|
myGraph.Invoking(x => x.EdgeData(new System.Guid())).Should().Throw<System.ArgumentException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AStarShortestPath()
|
||||||
|
{
|
||||||
|
var run = new MoveTypeEdgeData { moveType = MoveType.Run };
|
||||||
|
var jump = new MoveTypeEdgeData { moveType = MoveType.Jump };
|
||||||
|
var wallJump = new MoveTypeEdgeData { moveType = MoveType.WallJump };
|
||||||
|
|
||||||
|
var myGraph = new DirectedWeightedMultiGraph<char, MoveTypeEdgeData>();
|
||||||
|
myGraph.AddNodes('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h');
|
||||||
|
myGraph.AddEdges(
|
||||||
|
('a', 'b', 2, run),
|
||||||
|
('a', 'c', 1, jump),
|
||||||
|
('a', 'c', 3, run),
|
||||||
|
('a', 'e', 4, wallJump),
|
||||||
|
('b', 'd', 2, jump),
|
||||||
|
('b', 'd', 5, run),
|
||||||
|
('b', 'e', 1, run),
|
||||||
|
('c', 'g', 2, run),
|
||||||
|
('c', 'g', 4, jump),
|
||||||
|
('c', 'h', 11, run),
|
||||||
|
('d', 'c', 3, jump),
|
||||||
|
('d', 'f', 2, run),
|
||||||
|
('d', 'h', 3, wallJump),
|
||||||
|
('e', 'f', 5, run),
|
||||||
|
('f', 'd', 2, run),
|
||||||
|
('f', 'h', 6, wallJump),
|
||||||
|
('g', 'h', 7, run),
|
||||||
|
('h', 'f', 1, jump)
|
||||||
|
);
|
||||||
|
|
||||||
|
myGraph
|
||||||
|
.AStarShortestPath('a', 'h', (x, y) => 15)
|
||||||
|
.Select(id => myGraph.EdgeData(id))
|
||||||
|
.Should()
|
||||||
|
.ContainInOrder(
|
||||||
|
run, jump, wallJump
|
||||||
|
)
|
||||||
|
.And
|
||||||
|
.HaveCount(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Tests
|
||||||
|
{
|
||||||
|
public enum MoveType
|
||||||
|
{
|
||||||
|
Run,
|
||||||
|
Jump,
|
||||||
|
WallJump
|
||||||
|
}
|
||||||
|
public struct EdgeData { }
|
||||||
|
public struct NumEdgeData
|
||||||
|
{
|
||||||
|
public int testNum;
|
||||||
|
}
|
||||||
|
public struct MoveTypeEdgeData
|
||||||
|
{
|
||||||
|
public MoveType moveType;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue