directed weighted multigraph SSSP
							parent
							
								
									1740679052
								
							
						
					
					
						commit
						9e8bc14a40
					
				| 
						 | 
				
			
			@ -34,7 +34,13 @@ namespace MoonTools.Core.Graph
 | 
			
		|||
        public IEnumerable<int> Weights(TNode v, TNode u)
 | 
			
		||||
        {
 | 
			
		||||
            CheckNodes(v, u);
 | 
			
		||||
            return edges[(v, u)].Select(id => weights[id]);
 | 
			
		||||
            return edges[(v, u)].Select(id => Weight(id));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int Weight(Guid id)
 | 
			
		||||
        {
 | 
			
		||||
            if (!IDToEdge.ContainsKey(id)) { throw new ArgumentException($"Edge with id {id} does not exist in the graph"); }
 | 
			
		||||
            return weights[id];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private IEnumerable<Guid> ReconstructPath(PooledDictionary<TNode, Guid> cameFrom, TNode currentNode)
 | 
			
		||||
| 
						 | 
				
			
			@ -115,5 +121,149 @@ namespace MoonTools.Core.Graph
 | 
			
		|||
 | 
			
		||||
            yield break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private IEnumerable<Guid> ShortestPath(TNode start, TNode end, Func<TNode, IEnumerable<(TNode, Guid, int)>> SSSPAlgorithm)
 | 
			
		||||
        {
 | 
			
		||||
            CheckNodes(start, end);
 | 
			
		||||
 | 
			
		||||
            var cameFrom = new PooledDictionary<TNode, Guid>(ClearMode.Always);
 | 
			
		||||
            var reachable = new PooledSet<TNode>(ClearMode.Always);
 | 
			
		||||
 | 
			
		||||
            foreach (var (node, previous, weight) in SSSPAlgorithm(start))
 | 
			
		||||
            {
 | 
			
		||||
                cameFrom[node] = previous;
 | 
			
		||||
                reachable.Add(node);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!reachable.Contains(end))
 | 
			
		||||
            {
 | 
			
		||||
                cameFrom.Dispose();
 | 
			
		||||
                reachable.Dispose();
 | 
			
		||||
                yield break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (var edge in ReconstructPath(cameFrom, end).Reverse())
 | 
			
		||||
            {
 | 
			
		||||
                yield return edge;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            cameFrom.Dispose();
 | 
			
		||||
            reachable.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<(TNode, Guid, int)> DijkstraSingleSourceShortestPath(TNode source)
 | 
			
		||||
        {
 | 
			
		||||
            if (weights.Values.Any(w => w < 0)) { throw new NegativeWeightException("Dijkstra cannot be used on a graph with negative edge weights. Try Bellman-Ford"); }
 | 
			
		||||
            CheckNodes(source);
 | 
			
		||||
 | 
			
		||||
            var distance = new PooledDictionary<TNode, int>(ClearMode.Always);
 | 
			
		||||
            var previousEdgeIDs = new PooledDictionary<TNode, Guid>(ClearMode.Always);
 | 
			
		||||
 | 
			
		||||
            foreach (var node in Nodes)
 | 
			
		||||
            {
 | 
			
		||||
                distance[node] = int.MaxValue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            distance[source] = 0;
 | 
			
		||||
 | 
			
		||||
            var q = Nodes.ToPooledList();
 | 
			
		||||
 | 
			
		||||
            while (q.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                var node = q.MinBy(n => distance[n]).First();
 | 
			
		||||
                q.Remove(node);
 | 
			
		||||
                if (distance[node] == int.MaxValue) { break; }
 | 
			
		||||
 | 
			
		||||
                foreach (var neighbor in Neighbors(node))
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var edgeID in EdgeIDs(node, neighbor))
 | 
			
		||||
                    {
 | 
			
		||||
                        var weight = Weight(edgeID);
 | 
			
		||||
 | 
			
		||||
                        var alt = distance[node] + weight;
 | 
			
		||||
                        if (alt < distance[neighbor])
 | 
			
		||||
                        {
 | 
			
		||||
                            distance[neighbor] = alt;
 | 
			
		||||
                            previousEdgeIDs[neighbor] = edgeID;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (var node in Nodes)
 | 
			
		||||
            {
 | 
			
		||||
                if (previousEdgeIDs.ContainsKey(node) && distance.ContainsKey(node))
 | 
			
		||||
                {
 | 
			
		||||
                    yield return (node, previousEdgeIDs[node], distance[node]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            distance.Dispose();
 | 
			
		||||
            previousEdgeIDs.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<Guid> DijkstraShortestPath(TNode start, TNode end)
 | 
			
		||||
        {
 | 
			
		||||
            return ShortestPath(start, end, DijkstraSingleSourceShortestPath);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<(TNode, Guid, int)> BellmanFordSingleSourceShortestPath(TNode source)
 | 
			
		||||
        {
 | 
			
		||||
            CheckNodes(source);
 | 
			
		||||
 | 
			
		||||
            var distance = new PooledDictionary<TNode, int>(ClearMode.Always);
 | 
			
		||||
            var previous = new PooledDictionary<TNode, Guid>(ClearMode.Always);
 | 
			
		||||
 | 
			
		||||
            foreach (var node in Nodes)
 | 
			
		||||
            {
 | 
			
		||||
                distance[node] = int.MaxValue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            distance[source] = 0;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < Order; i++)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var edgeID in IDToEdge.Keys)
 | 
			
		||||
                {
 | 
			
		||||
                    var weight = Weight(edgeID);
 | 
			
		||||
                    var (v, u) = IDToEdge[edgeID];
 | 
			
		||||
 | 
			
		||||
                    if (distance[v] + weight < distance[u])
 | 
			
		||||
                    {
 | 
			
		||||
                        distance[u] = distance[v] + weight;
 | 
			
		||||
                        previous[u] = edgeID;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (var edgeID in IDToEdge.Keys)
 | 
			
		||||
            {
 | 
			
		||||
                var (v, u) = IDToEdge[edgeID];
 | 
			
		||||
 | 
			
		||||
                foreach (var weight in Weights(v, u))
 | 
			
		||||
                {
 | 
			
		||||
                    if (distance[v] + weight < distance[u])
 | 
			
		||||
                    {
 | 
			
		||||
                        throw new NegativeCycleException();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (var node in Nodes)
 | 
			
		||||
            {
 | 
			
		||||
                if (previous.ContainsKey(node) && distance.ContainsKey(node))
 | 
			
		||||
                {
 | 
			
		||||
                    yield return (node, previous[node], distance[node]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            distance.Dispose();
 | 
			
		||||
            previous.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<Guid> BellmanFordShortestPath(TNode start, TNode end)
 | 
			
		||||
        {
 | 
			
		||||
            return ShortestPath(start, end, BellmanFordSingleSourceShortestPath);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -47,13 +47,25 @@ namespace MoonTools.Core.Graph
 | 
			
		|||
            return edges.ContainsKey((v, u));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void CheckID(Guid id)
 | 
			
		||||
        {
 | 
			
		||||
            if (!Exists(id)) { throw new ArgumentException($"Edge {id} does not exist in the graph."); }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Exists(Guid id)
 | 
			
		||||
        {
 | 
			
		||||
            return IDToEdge.ContainsKey(id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public (TNode, TNode) EdgeNodes(Guid id)
 | 
			
		||||
        {
 | 
			
		||||
            CheckID(id);
 | 
			
		||||
            return IDToEdge[id];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public TEdgeData EdgeData(Guid id)
 | 
			
		||||
        {
 | 
			
		||||
            if (!edgeToEdgeData.ContainsKey(id))
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentException($"Edge {id} does not exist in the graph.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CheckID(id);
 | 
			
		||||
            return edgeToEdgeData[id];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ A GC-friendly graph theory library for C# intended for use with games.
 | 
			
		|||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
`Graph` implements the following graph structures:
 | 
			
		||||
`Graph` implements various algorithms on the following graph structures:
 | 
			
		||||
 | 
			
		||||
* Directed
 | 
			
		||||
* Directed Weighted
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -252,5 +252,241 @@ namespace Tests
 | 
			
		|||
            // have to call Count() because otherwise the lazy evaluation wont trigger
 | 
			
		||||
            myGraph.Invoking(x => x.AStarShortestPath('a', 'z', (x, y) => 1).Count()).Should().Throw<System.ArgumentException>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Test]
 | 
			
		||||
        public void DijsktraSingleSourceShortestPath()
 | 
			
		||||
        {
 | 
			
		||||
            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');
 | 
			
		||||
 | 
			
		||||
            var edgeA = myGraph.AddEdge('a', 'b', 2, run);
 | 
			
		||||
            var edgeB = myGraph.AddEdge('a', 'c', 1, jump);
 | 
			
		||||
            var edgeC = myGraph.AddEdge('b', 'd', 2, jump);
 | 
			
		||||
            var edgeD = myGraph.AddEdge('b', 'e', 1, run);
 | 
			
		||||
            var edgeE = myGraph.AddEdge('d', 'f', 2, run);
 | 
			
		||||
            var edgeF = myGraph.AddEdge('c', 'g', 2, run);
 | 
			
		||||
            var edgeG = myGraph.AddEdge('d', 'h', 3, wallJump);
 | 
			
		||||
 | 
			
		||||
            myGraph.AddEdges(
 | 
			
		||||
                ('a', 'c', 3, run),
 | 
			
		||||
                ('a', 'e', 4, wallJump),
 | 
			
		||||
                ('b', 'd', 5, run),
 | 
			
		||||
                ('c', 'g', 4, jump),
 | 
			
		||||
                ('c', 'h', 11, run),
 | 
			
		||||
                ('d', 'c', 3, jump),
 | 
			
		||||
                ('e', 'f', 5, run),
 | 
			
		||||
                ('f', 'd', 2, run),
 | 
			
		||||
                ('f', 'h', 6, wallJump),
 | 
			
		||||
                ('g', 'h', 7, run),
 | 
			
		||||
                ('h', 'f', 1, jump),
 | 
			
		||||
                ('a', 'a', 3, jump) // cheeky lil self-edge
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            myGraph
 | 
			
		||||
                .DijkstraSingleSourceShortestPath('a')
 | 
			
		||||
                .Should()
 | 
			
		||||
                .Contain(('b', edgeA, 2)).And
 | 
			
		||||
                .Contain(('c', edgeB, 1)).And
 | 
			
		||||
                .Contain(('d', edgeC, 4)).And
 | 
			
		||||
                .Contain(('e', edgeD, 3)).And
 | 
			
		||||
                .Contain(('f', edgeE, 6)).And
 | 
			
		||||
                .Contain(('g', edgeF, 3)).And
 | 
			
		||||
                .Contain(('h', edgeG, 7)).And
 | 
			
		||||
                .HaveCount(7);
 | 
			
		||||
 | 
			
		||||
            // have to call Count() because otherwise the lazy evaluation wont trigger
 | 
			
		||||
            myGraph.Invoking(x => x.DijkstraSingleSourceShortestPath('z').Count()).Should().Throw<System.ArgumentException>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Test]
 | 
			
		||||
        public void DijkstraShortestPath()
 | 
			
		||||
        {
 | 
			
		||||
            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');
 | 
			
		||||
 | 
			
		||||
            var edgeA = myGraph.AddEdge('a', 'b', 2, run);
 | 
			
		||||
            var edgeB = myGraph.AddEdge('a', 'c', 1, jump);
 | 
			
		||||
            var edgeC = myGraph.AddEdge('b', 'd', 2, jump);
 | 
			
		||||
            var edgeD = myGraph.AddEdge('b', 'e', 1, run);
 | 
			
		||||
            var edgeE = myGraph.AddEdge('d', 'f', 2, run);
 | 
			
		||||
            var edgeF = myGraph.AddEdge('c', 'g', 2, run);
 | 
			
		||||
            var edgeG = myGraph.AddEdge('d', 'h', 3, wallJump);
 | 
			
		||||
 | 
			
		||||
            myGraph.AddEdges(
 | 
			
		||||
                ('a', 'c', 3, run),
 | 
			
		||||
                ('a', 'e', 4, wallJump),
 | 
			
		||||
                ('b', 'd', 5, run),
 | 
			
		||||
                ('c', 'g', 4, jump),
 | 
			
		||||
                ('c', 'h', 11, run),
 | 
			
		||||
                ('d', 'c', 3, jump),
 | 
			
		||||
                ('e', 'f', 5, run),
 | 
			
		||||
                ('f', 'd', 2, run),
 | 
			
		||||
                ('f', 'h', 6, wallJump),
 | 
			
		||||
                ('g', 'h', 7, run),
 | 
			
		||||
                ('h', 'f', 1, jump),
 | 
			
		||||
                ('a', 'a', 3, jump) // cheeky lil self-edge
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            myGraph
 | 
			
		||||
                .DijkstraShortestPath('a', 'h')
 | 
			
		||||
                .Select(edgeID => myGraph.EdgeData(edgeID))
 | 
			
		||||
                .Should()
 | 
			
		||||
                .ContainInOrder(
 | 
			
		||||
                    run, jump, wallJump
 | 
			
		||||
                )
 | 
			
		||||
                .And
 | 
			
		||||
                .HaveCount(3);
 | 
			
		||||
 | 
			
		||||
            myGraph.Invoking(x => x.DijkstraShortestPath('a', 'z').Count()).Should().Throw<System.ArgumentException>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Test]
 | 
			
		||||
        public void BellmanFordSingleSourceShortestPath()
 | 
			
		||||
        {
 | 
			
		||||
            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');
 | 
			
		||||
 | 
			
		||||
            var edgeA = myGraph.AddEdge('a', 'b', 2, run);
 | 
			
		||||
            var edgeB = myGraph.AddEdge('a', 'c', 1, jump);
 | 
			
		||||
            var edgeC = myGraph.AddEdge('b', 'd', 2, jump);
 | 
			
		||||
            var edgeD = myGraph.AddEdge('b', 'e', 1, run);
 | 
			
		||||
            var edgeE = myGraph.AddEdge('d', 'f', 2, run);
 | 
			
		||||
            var edgeF = myGraph.AddEdge('c', 'g', 2, run);
 | 
			
		||||
            var edgeG = myGraph.AddEdge('d', 'h', 3, wallJump);
 | 
			
		||||
 | 
			
		||||
            myGraph.AddEdges(
 | 
			
		||||
                ('a', 'c', 3, run),
 | 
			
		||||
                ('a', 'e', 4, wallJump),
 | 
			
		||||
                ('b', 'd', 5, run),
 | 
			
		||||
                ('c', 'g', 4, jump),
 | 
			
		||||
                ('c', 'h', 11, run),
 | 
			
		||||
                ('d', 'c', 3, jump),
 | 
			
		||||
                ('e', 'f', 5, run),
 | 
			
		||||
                ('f', 'd', 2, run),
 | 
			
		||||
                ('f', 'h', 6, wallJump),
 | 
			
		||||
                ('g', 'h', 7, run),
 | 
			
		||||
                ('h', 'f', 1, jump),
 | 
			
		||||
                ('a', 'a', 3, jump) // cheeky lil self-edge
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            myGraph
 | 
			
		||||
                .BellmanFordSingleSourceShortestPath('a')
 | 
			
		||||
                .Should()
 | 
			
		||||
                .Contain(('b', edgeA, 2)).And
 | 
			
		||||
                .Contain(('c', edgeB, 1)).And
 | 
			
		||||
                .Contain(('d', edgeC, 4)).And
 | 
			
		||||
                .Contain(('e', edgeD, 3)).And
 | 
			
		||||
                .Contain(('f', edgeE, 6)).And
 | 
			
		||||
                .Contain(('g', edgeF, 3)).And
 | 
			
		||||
                .Contain(('h', edgeG, 7)).And
 | 
			
		||||
                .HaveCount(7);
 | 
			
		||||
 | 
			
		||||
            // have to call Count() because otherwise the lazy evaluation wont trigger
 | 
			
		||||
            myGraph.Invoking(x => x.BellmanFordSingleSourceShortestPath('z').Count()).Should().Throw<System.ArgumentException>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Test]
 | 
			
		||||
        public void BellmanFordSingleSourceShortestPathNegative()
 | 
			
		||||
        {
 | 
			
		||||
            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');
 | 
			
		||||
 | 
			
		||||
            var edgeA = myGraph.AddEdge('a', 'b', 2, run);
 | 
			
		||||
            var edgeB = myGraph.AddEdge('a', 'c', 1, jump);
 | 
			
		||||
            var edgeC = myGraph.AddEdge('b', 'd', -1, jump);
 | 
			
		||||
            var edgeD = myGraph.AddEdge('b', 'e', 1, run);
 | 
			
		||||
            var edgeE = myGraph.AddEdge('d', 'f', 2, run);
 | 
			
		||||
            var edgeF = myGraph.AddEdge('c', 'g', 2, run);
 | 
			
		||||
            var edgeG = myGraph.AddEdge('d', 'h', 3, wallJump);
 | 
			
		||||
 | 
			
		||||
            myGraph.AddEdges(
 | 
			
		||||
                ('a', 'c', 3, run),
 | 
			
		||||
                ('a', 'e', 4, wallJump),
 | 
			
		||||
                ('b', 'd', 5, run),
 | 
			
		||||
                ('c', 'g', 4, jump),
 | 
			
		||||
                ('c', 'h', 11, run),
 | 
			
		||||
                ('d', 'c', 3, jump),
 | 
			
		||||
                ('e', 'f', 5, run),
 | 
			
		||||
                ('f', 'd', 2, run),
 | 
			
		||||
                ('f', 'h', 6, wallJump),
 | 
			
		||||
                ('g', 'h', 7, run),
 | 
			
		||||
                ('h', 'f', 1, jump),
 | 
			
		||||
                ('a', 'a', 3, jump) // cheeky lil self-edge
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            myGraph
 | 
			
		||||
                .BellmanFordSingleSourceShortestPath('a')
 | 
			
		||||
                .Should()
 | 
			
		||||
                .Contain(('b', edgeA, 2)).And
 | 
			
		||||
                .Contain(('c', edgeB, 1)).And
 | 
			
		||||
                .Contain(('d', edgeC, 1)).And
 | 
			
		||||
                .Contain(('e', edgeD, 3)).And
 | 
			
		||||
                .Contain(('f', edgeE, 3)).And
 | 
			
		||||
                .Contain(('g', edgeF, 3)).And
 | 
			
		||||
                .Contain(('h', edgeG, 4)).And
 | 
			
		||||
                .HaveCount(7);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Test]
 | 
			
		||||
        public void BellmanFordShortestPath()
 | 
			
		||||
        {
 | 
			
		||||
            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');
 | 
			
		||||
 | 
			
		||||
            var edgeA = myGraph.AddEdge('a', 'b', 2, run);
 | 
			
		||||
            var edgeB = myGraph.AddEdge('a', 'c', 1, jump);
 | 
			
		||||
            var edgeC = myGraph.AddEdge('b', 'd', 2, jump);
 | 
			
		||||
            var edgeD = myGraph.AddEdge('b', 'e', 1, run);
 | 
			
		||||
            var edgeE = myGraph.AddEdge('d', 'f', 2, run);
 | 
			
		||||
            var edgeF = myGraph.AddEdge('c', 'g', 2, run);
 | 
			
		||||
            var edgeG = myGraph.AddEdge('d', 'h', 3, wallJump);
 | 
			
		||||
 | 
			
		||||
            myGraph.AddEdges(
 | 
			
		||||
                ('a', 'c', 3, run),
 | 
			
		||||
                ('a', 'e', 4, wallJump),
 | 
			
		||||
                ('b', 'd', 5, run),
 | 
			
		||||
                ('c', 'g', 4, jump),
 | 
			
		||||
                ('c', 'h', 11, run),
 | 
			
		||||
                ('d', 'c', 3, jump),
 | 
			
		||||
                ('e', 'f', 5, run),
 | 
			
		||||
                ('f', 'd', 2, run),
 | 
			
		||||
                ('f', 'h', 6, wallJump),
 | 
			
		||||
                ('g', 'h', 7, run),
 | 
			
		||||
                ('h', 'f', 1, jump),
 | 
			
		||||
                ('a', 'a', 3, jump) // cheeky lil self-edge
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            myGraph
 | 
			
		||||
                .BellmanFordShortestPath('a', 'h')
 | 
			
		||||
                .Select(edgeID => myGraph.EdgeData(edgeID))
 | 
			
		||||
                .Should()
 | 
			
		||||
                .ContainInOrder(
 | 
			
		||||
                    run, jump, wallJump
 | 
			
		||||
                )
 | 
			
		||||
                .And
 | 
			
		||||
                .HaveCount(3);
 | 
			
		||||
 | 
			
		||||
            myGraph.Invoking(x => x.BellmanFordShortestPath('a', 'z').Count()).Should().Throw<System.ArgumentException>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue