A* Path-finding is just barely not working - c#

I have my NPC and I have a target location for him. I have placed my A* path-finding algorithm into the code. This created a path, but it is in no way an ideal path, nor is it cohesive. I have written this piece a few times and have found myself facing the same problem.
This picture should show the result of my problem: the final path is all over the place.
To me, it appears that algorithm is finding a connection between every tile on my map instead of locating the best path. I really can't nail down where this is happening, though.
For some clarity, I am defining the tiles (squares) and their neighbors when I load my game scene. When the program reaches GetAdjacentSquares is is using those results to find valid tiles. You will notice that they NPC's path is not centering directly onto any of the green tiles.
List<Path> GetAdjacentSquares(Path p)
{
List<Path> ret = new List<Path>();
TileData tile = tManager.GetTileByCoords(new Vector2(p.x, p.y));
foreach (Vector2 t in tile.neighborLocs)
{
TileData n = tManager.GetTileByCoords(t);
if (n && n.tType == TileTypes.Houses || n.tType == TileTypes.Road)
{
ret.Add(new Path(p, n.tTileLoc.x, n.tTileLoc.y));
}
}
return ret;
}
int BlocksToTarget(Vector2 tileLoc, Vector2 targetLoc)
{
int final = (int)Mathf.Abs((tileLoc.x - targetLoc.x) * (tileLoc.x - targetLoc.x) + (tileLoc.y - targetLoc.y) * (tileLoc.y - targetLoc.y));
return final;`
}
bool DoesPathContain(List<Path> paths, Vector2 target)
{
foreach(Path p in paths)
{
if (p.x == target.x && p.y == target.y)
return true;
}
return false;
}
int LowestFScore(List<Path> path)
{
int lowest = int.MaxValue;
foreach(Path p in path)
{
if (p.f <= lowest)
lowest = p.f;
}
return lowest;
}
void GoHome()
{
Path current = null;
//target
Path destination = new Path(null, cData.houseLoc.x, cData.houseLoc.y);
//start
Path start = new Path(destination, transform.localPosition.x, transform.localPosition.y);
//start by adding the original position to the open list
List<Path> open = new List<Path>() { start };
List<Path> close = new List<Path>();
int g = 0;
while(open.Count > 0)
{
//get the square with the lowest F score
current = open.Last(p => p.f == LowestFScore(open));
//add the current square to the closed list
close.Add(current);
//remove it from the open list
open.Remove(current);
//if we added the destination to the closed list, we've found a path
if(DoesPathContain(close, cData.houseLoc))
break;
//The rest of the algorithm evaluates adjacent tiles
List<Path> adjacentTiles = GetAdjacentSquares(current);
g++;
foreach(Path tile in adjacentTiles)
{
Vector2 tileLoc = new Vector2(tile.x, tile.y);
//if this adjacent square is already in the closed list, ignore it
if (DoesPathContain(close, tileLoc))
continue;
if(!DoesPathContain(open, tileLoc))
{
//if this adjacent square is already in the closed list, ignore it
tile.g = g;
tile.h = BlocksToTarget(tileLoc, cData.houseLoc);
tile.parent = current;
//add it to the open list
open.Add(tile);
}
else
{
//test if using the current G score makes the adjacent square's F score
//lower, if yes update the parent because it means it's a better path
if (g+tile.h < tile.f)
{
tile.g = g;
tile.parent = current;
}
}
}
}
//foreach (Path p in close)
// Debug.Log(p.f + " ("+p.x+", "+p.y+")");
walkTo = close;
cData.isWalking = true;
}
`

I have learned that I was getting my final results from the closed list. Once I accessed the correct list, the path was drawn!

Related

Mesh find separate component (bodies) within 1 mesh (stackoverflow exception)

What I am trying to do is group separated components (geometries) within my 1 mesh. We have an half-edge data structure. My idea is to grap 1 halfedge and loop through the edges, find paired (twin) half edges and loop through that one to find the faces, if I already visited that face we skip that one and go to the next.
This will result in stackoverflow, because my mesh is too big.
public Mesh[] SplitDisjointMeshes()
{
var output = new List<Mesh>();
var visited = new bool[_faces.Count];
for(var i = 0; i < _halfedges.Count; i++)
{
if (visited[_halfedges[i].AdjacentFace]) continue;
var faces = new List<int>();
GetDisjointMeshPart(i, ref visited, faces);
// Todo: Build new mesh from all connected faces
}
return null;
}
private void GetDisjointMeshPart(int i, ref bool[] visited, List<int> faces)
{
int he_first, he_current;
he_first = i;
he_current = he_first;
do
{
he_current = _halfedges[he_current].NextHalfedge;
var tmp = _halfedges.GetPairHalfedge(he_current);
var face = _halfedges[tmp].AdjacentFace;
if (face == -1 || visited[face]) continue; // Not found or aready visited
faces.Add(face);
visited[face] = true;
GetDisjointMeshPart(tmp, ref visited, faces);
}
while (he_current != he_first);
}
Any ideas on how to resolve the stackoverflow from happening? Or if it is possible to get an O(N) loop.

System.StackOverflowException error

I am trying to create a 2D cave generation system. When I run the program I get "System.StackOverflowException" Exception, after I try to create new object from its own class.
My cave generator works like this:
I create a map that contains ID’s (integers) of the different types of cells(like wall, water or empty Space).
First off all my "Map" class creates a map filled with walls and after that in the centre of the map, it creates a "Miner" object. The Miner digs the map and makes caves. The problem is I want to create more miners. So, my Miner that is digging the map creates another Miner. However, when I do this, I get a "System.StackOverflowException" Exception.
How do I go about tracking down the cause of the StackOverflow in my program.
Here is my miner code:
Miner.cs
public class Miner
{
Random rand = new Random();
public string state { get; set; }
public int x { get; set; }
public int y { get; set; }
public Map map { get; set; }
public int minersCount;
public Miner(Map map, string state, int x, int y)
{
this.map = map;
this.state = state;
this.x = x;
this.y = y;
minersCount++;
if (state == "Active")
{
StartDigging();
}
}
bool IsOutOfBounds(int x, int y)
{
if (x == 0 || y == 0)
{
return true;
}
else if (x > map.mapWidth - 2 || y > map.mapHeight - 2)
{
return true;
}
return false;
}
bool IsLastMiner()
{
if (minersCount == 1)
{
return true;
}
else
{
return false;
}
}
public void StartDigging()
{
if (state == "Active")
{
int dir = 0;
bool needStop = false;
int ID = -1;
while (!needStop && !IsOutOfBounds(x, y))
{
while (dir == 0)
{
dir = ChooseDirection();
}
if (!AroundIsNothing())
{
while (ID == -1)
{
ID = GetIDFromDirection(dir);
}
}
else
{
if (!IsLastMiner())
{
needStop = true;
}
}
if (ID == 1)
{
DigToDirection(dir);
dir = 0;
}
if (ID == 0 && IsLastMiner())
{
MoveToDirection(dir);
dir = 0;
}
TryToCreateNewMiner();
}
if (needStop)
{
state = "Deactive";
}
}
}
public void TryToCreateNewMiner()
{
if (RandomPercent(8))
{
Miner newMiner = new Miner(map, "Active", x, y);
}
else
{
return;
}
}
bool AroundIsNothing()
{
if (map.map[x + 1, y] == 0 && map.map[x, y + 1] == 0 &&
map.map[x - 1, y] == 0 && map.map[x, y - 1] == 0)
{
return true;
}
else
{
return false;
}
}
void MoveToDirection(int dir)
{
if (dir == 1)
{
x = x + 1;
}
else if (dir == 2)
{
y = y + 1;
}
else if (dir == 3)
{
x = x - 1;
}
else if (dir == 4)
{
y = y - 1;
}
}
void DigToDirection(int dir)
{
if (dir == 1)
{
map.map[x + 1, y] = 0;
x = x + 1;
}
else if (dir == 2)
{
map.map[x, y + 1] = 0;
y = y + 1;
}
else if (dir == 3)
{
map.map[x - 1, y] = 0;
x = x - 1;
}
else if (dir == 4)
{
map.map[x, y - 1] = 0;
y = y - 1;
}
}
int GetIDFromDirection(int dir)
{
if (dir == 1)
{
return map.map[x + 1, y];
}
else if (dir == 2)
{
return map.map[x, y + 1];
}
else if (dir == 3)
{
return map.map[x - 1, y];
}
else if (dir == 4)
{
return map.map[x, y - 1];
}
else
{
return -1;
}
}
int ChooseDirection()
{
return rand.Next(1, 5);
}
bool RandomPercent(int percent)
{
if (percent >= rand.Next(1, 101))
{
return true;
}
return false;
}
}
Whilst you can get StackOverflowExceptions by creating too many really large objects on the stack, it usually happens because your code has got into a state where it is calling the same chain of functions over and over again. So, to track down the cause in your code, the best starting point is to determine where your code calls itself.
Your code consists of several functions that are called by the Miner class itself, most of which are trivial
Trivial functions that don't call anything else in the class. Whilst these functions may contribute to the state that triggers the problem, they aren't part of the terminal function loop:
IsOutOfBounds(int x, int y)
bool IsLastMiner()
bool AroundIsNothing()
void MoveToDirection(int dir)
void DigToDirection(int dir)
int GetIDFromDirection(int dir)
int ChooseDirection()
bool RandomPercent(int percent)
This leaves your remaining three functions
public Miner(Map map, string state, int x, int y) // Called by TryToCreateNewMiner
public void StartDigging() // Called by constructor
// Contains main digging loop
public void TryToCreateNewMiner() // Called by StartDigging
These three functions form a calling loop, so if the branching logic in the functions is incorrect it could cause a non-terminating loop and hence a stack overflow.
So, looking at the branching logic in the functions
Miner
The constructor only has one branch, based on if the state is "Active". It is always active, since that's the way the object is always being created, so the constructor will always call StartDigging. This feels like the state isn't being handled correctly, although it's possible that you're going to use it for something else in the future...
As an aside, it's generally considered to be bad practice to do a lot of processing, not required to create the object in an objects constructor. All of your processing happens in the constructor which feels wrong.
TryToCreateNewMiner
This has one branch, 8% of the time, it will create a new miner and call the constructor. So for every 10 times TryToCreateNewMiner is called, we stand a good chance that it will have succeeded at least once. The new miner is initially started in the same position as the parent object (x and y aren't changed).
StartDigging
There's a fair bit of branching in this method. The main bit we are interested in are the conditions around calls to TryToCreateNewMiner. Lets look at the branches:
if(state=="Active")
This is currently a redundant check (it's always active).
while (!needStop && !IsOutOfBounds(x, y)) {
The first part of this termination clause is never triggered. needStop is only ever set to true if(!IsLastMiner). Since minersCount is always 1, it's always the last miner, so needStop is never triggered. The way you are using minersCount suggests that you think it is shared between instances of Miner, which it isn't. If that is your intention you may want to read up on static variables.
The second part of the termination clause is the only way out of the loop and that is triggered if either x or y reaches the edge of the map.
while(dir==0)
This is a pointless check, dir can only be a number between 1 and 5, since that's what is returned by ChooseDirection.
if(!AroundIsNothing())
This is checking if the positions that the Miner can move into are all set to 0. If they are not, then GetIDFromDirection is called. This is key. If the Miner is currently surrounded by 0, ID will not be set, it will remain at it's previous value. In a situation where a Miner has just been created, this will be -1 (we know this could happen because all Miners are created at the location of the Miner creating it).
The last two checksif(ID==1) and if(ID==0 && IsLastMiner()) guard the code that moves the Miner (either by calling dig, or move). So, if ID is not 0, or 1 at this point the Miner will not move. This could cause a problem because it is immediately before the call to TryToCreateNewMiner, so if the program ever gets into this situation it will be stuck in a loop where the Miner isn't moving and it's constantly trying to create new Miners in the same position. 8% of the time this will work, creating a new miner in the same position, which will perform the same checks and get into the same loop, again not moving and trying to create a new miner and so it goes until the stack runs out of space and the program crashes.
You need to take a look at your termination clauses and the way you're handling ID, you probably don't want the Miner to just stop doing anything if it gets completely surround by 0.

Implementing A* pathfinding in a 2D array

I'm in the process of making a 2D tile map and i'm now trying to implement A* pathfinding. I'm following the Wikipedia pseudocode for A*.
Things are going quite well except for some weird behaviour in the decisions taken by the algorithm.
My code so far:
void Pathfinding(Point from, Point destination) {
goalNode = new Node(destination, 0, 0);
startNode = new Node(from, 0, ManhattanDistance(from, destination));
open = new List<Node>(); //list of nodes
closed = new List<Node>();
open.Add(startNode); //Add starting point
while(open.Count > 0) {
node = getBestNode(); //Get node with lowest F value
if(node.position == goalNode.position) {
Debug.Log("Goal reached");
getPath(node);
break;
}
removeNode(node, open);
closed.Add(node);
List<Node> neighbors = getNeighbors(node);
foreach(Node n in neighbors) {
float g_score = node.G + 1;
float h_score = ManhattanDistance(n.position, goalNode.position);
float f_score = g_score + h_score;
if(isValueInList(n, closed) && f_score >= n.F)
continue;
if(!isValueInList(n, open) || f_score < n.F) {
n.parent = node;
n.G = g_score;
n.G = h_score;
if(!isValueInList(n, open)) {
map_data[n.position.x, n.position.y] = 4;
open.Add(n);
}
}
}
}
}
The result of running this code:
Blue is the nodes from the open list and green is the path chosen to the goal node.
SOLUTION:
void Pathfinding(Point from, Point destination) {
goalNode = new Node(destination, 0, 0);
startNode = new Node(from, 0, ManhattanDistance(from, destination));
open = new List<Node>(); //list of nodes
closed = new List<Node>();
open.Add(startNode); //Add starting point
while(open.Count > 0) {
node = getBestNode(); //Get node with lowest F value
if(node.position == goalNode.position) {
Debug.Log("Goal reached");
getPath(node);
break;
}
removeNode(node, open);
closed.Add(node);
List<Node> neighbors = getNeighbors(node);
foreach(Node n in neighbors) {
float g_score = node.G + 1;
float h_score = ManhattanDistance(n.position, goalNode.position);
float f_score = g_score + h_score;
if(isValueInList(n, closed) && f_score >= n.F)
continue;
if(!isValueInList(n, open) || f_score < n.F) {
n.parent = node;
n.G = g_score;
n.H = h_score;
if(!isValueInList(n, open)) {
map_data[n.position.x, n.position.y] = 4;
open.Add(n);
}
}
}
}
}
First, your open Nodes should be sorted in descending order, while in your code - there is no ordering. You compute the distance (g) and the heuristics (h) but never actually use it. You should consider using ordered container instead of lists (as sorting lists in each iteration won't be efficient)
Second, you do not store the heuristic value in the node as
n.G = h_score;
should be
n.H = h_score;

Multithreading and passing data to new threads

I have some issues with an application I'm coding. It's job is to solve a maze using threads. One thread begins, and for each bifurcation it calls a static method in another class, passing parameters that the other thread needs, and then starts threads for each path. My outputs are all messed up, and I'm not sure if it's a Multithreading problem or maybe a problem with the references. Here's some of the code (Each thread is given a new instance of an Explorer class):
This is the Run() method for each thread:
public void Explore()
{
while (ImDone == false)
{
Move();
if (ActualPosition[0] != Labyrinth.ExitPoint[0] ||
ActualPosition[1] != Labyrinth.ExitPoint[1]) //I'm not at the end..
continue;
PrintMyStatus(); //Print in the console my parents and my complete route..
break;
}
This is the Move() method:
List<int[]> validPaths = CheckSurroundings(); //returns a list of paths
switch (validPaths.Count)
{
case 0:
ImDone = true; //No more routes available
break;
case 1:
MarkMyPosition(validPaths); //Change internal variables to the only new path
break;
default:
lock(this) //As this involves thread creating I locked it..
{
//Creating deep copies of my "History" so my childs can have them..
List<int[]> DCValidPaths = DeepCopy(validPaths);
List<string> DCMyParents = DeepCopy(MyParents);
string[,] DCMyExplorationMap = DeepCopy(MyExplorationMap);
List<int[]> DCMyPositions = DeepCopy(MyPositions);
foreach (var path in validPaths)
{
DCMyParents.Add(Thread.CurrentThread.Name); //Adding myself as a parent
ExplorationManager.ExplorerMaker(
DCValidPaths,
DCMyParents,
DCMyExplorationMap,
DCMyPositions,
ID); //Threads are created in the static class
}
}
break;
}
My DeepCopy() method is using is the one in the accepted answer of this question. Any other info needed I'll be more than happy to provide in order to solve the problem. Thanks in advance for your help.
EDITS: My problem consists basically in: I'm having an OutOfMemoryException in the Thread.Start() clause and the Path displayed by the threads contain corrupted data (incorrect positions). I tested the CheckSurroundings() method and by far only returns correct positions (The labyrinth is contained in a two-dimensional string array).
This is the constructor of the Explorer class:
public Explorer(List<string> myParents, int[] actualPosition, string[,] myExplorationMap,
List<int[]> myPositions, int ID)
{
//Here I pass the data specified in Move();
MyParents = myParents;
ActualPosition = actualPosition;
MyExplorationMap = myExplorationMap;
MyPositions = myPositions;
this.ID = ID + 1; //An ID for reference
//Marking position in my map with "1" so I know I've been here already,
//and adding the position given to my list of positions
MyExplorationMap[ActualPosition[0], ActualPosition[1]] = "1";
MyPositions.Add(DeepCopy(ActualPosition));
}
And this is the class creating the threads:
public static class ExplorationManager
{
public static void ExplorerMaker(List<int[]> validPaths, List<string> myParents, string[,] myExplorationMap, List<int[]> myPositions, int ID)
{
foreach (var thread in validPaths.Select
(path => new Explorer(myParents, path, myExplorationMap, myPositions,ID)).
Select(explorer => new Thread(explorer.Explore)))
{
thread.Name = "Thread of " + ID + " generation";
thread.Start(); //For each Path in Valid paths, create a new instance of Explorer and assign a thread to it.
}
}
}
And the method returning the ValidPaths
private List<int[]> CheckSurroundings()
{
var validPaths = new List<int[]>();
var posX = ActualPosition[0];
var posY = ActualPosition[1];
for (var dx = -1; dx <= 1; dx++)
{
if (dx == 0 || (posX + dx) < 0 || (posX + dx) >= Labyrinth.Size ||
MyExplorationMap[posX + dx, posY] == "1") continue;
var tempPos = new int[2];
tempPos[0] = posX + dx;
tempPos[1] = posY;
validPaths.Add(tempPos);
}
for (var dy = -1; dy <= 1; dy++)
{
if (dy == 0 || (posY + dy) < 0 || (posY + dy) >= Labyrinth.Size ||
MyExplorationMap[posX, posY + dy] == "1") continue;
var tempPos = new int[2];
tempPos[0] = posX;
tempPos[1] = posY + dy;
validPaths.Add(tempPos);
}
//This method checks up, down, left, right and returns the posible routes as `int[]` for each one
return validPaths;
}
CheckSurroundings uses the deep copy passed to the child (via the constructor) to validate the routes he can take. It is NOT intended to alterate the parent's copy, because he is now in ANOTHER path of the labyrinth. The child only needs the information updated (passed via the constructor) UNTIL the point they "separate". And also EACH child must be independent from the other childs. That's what I'm trying to do. But I'm not sure what's wrong, maybe concurrency problem? Please help. If you need anything else let me know. –
EDIT for your updates:
myExplorationMap is a deep copy of the original exploration map. You set the position to 1 in the Explorer constructor, which updates the copy that is shared by all of the child threads, but it does not update the original MyExplorationMap property in the parent thread. Only the child threads will know this position was visited. I assume this is used somewhere in the CheckSurroundings method?

Shortest path in Directed Acyclic Graph

I am given a string of characters, in which every consequent pair of characters comprises an edge. What I mean by that is this is the string: ABBCAD. Edges of the string are:
A->B
B->C
A->D
Shortest path distance is A->D
The task at hand is to build up a Directed Acyclic Graph in memory from the string using the above rule and find the shortest path staring at the root node(in the example given it's A label) ending at terminal node.
NJKUUGHBNNJHYAPOYJHNRMNIKAIILFGJSNAICZQRNM
I gather one of the approaches that suites the task is to use the Depth First Search algo.
This is not homework...
This is a job for Djikstra's Algorithm. Once you build a representation of your graph it should be easy enough to produce the lowest cost traversal ... since in your case it seems that all paths have an equal cost (1).
You can look here on CodeProject for a reasonably decent implementation of Djikstra in C#.
could you present me with a pseudo code of your version of the graph representation for this case?
There are multiple ways to represent a graph in such a problem. If the number of vertices in your graph are likely to be small - it would be sufficient to use an adjacency matrix for the representation of edges. In which case, you could just use a .NET multidimensional array. The trick here is that you need to convert vertices labelled with characters to ordinal values. One approach would be to write a wrapper class that maps character codes to indices into the adjacency matrix:
class AdjacencyMatrix
{
// representation of the adjacency matrix (AM)
private readonly int[,] m_Matrix;
// mapping of character codes to indices in the AM
private readonly Dictionary<char,int> m_Mapping;
public AdjacencyMatrix( string edgeVector )
{
// using LINQ we can identify and order the distinct characters
char[] distinctChars = edgeVector.Distinct().OrderBy(x => x);
m_Mapping = distinctChars.Select((c,i)=>new { Vertex = c, Index = i })
.ToDictionary(x=>x.Vertex, x=>x.Index);
// build the adjacency cost matrix here...
// TODO: I leave this up to the reader to complete ... :-D
}
// retrieves an entry from within the adjacency matrix given two characters
public int this[char v1, char v2]
{
get { return m_Matrix[m_Mapping[v1], m_Mapping[v2]];
private set { m_Matrix[m_Mapping[v1], m_Mapping[v2]] = value; }
}
}
For this particular case, Dijkstra's could be greatly simplified. I imagine something like this would do the trick.
class Node {
public object Value;
// Connected nodes (directed)
public Node[] Connections;
// Path back to the start
public Node Route;
}
Node BreadthFirstRoute(Node[] theStarts, Node[] theEnds) {
Set<Node> visited = new Set<Node>();
Set<Node> frontier = new Set<Node>();
frontier.AddRange(theStarts);
Set<Node> ends = new Set<Node>();
ends.AddRange(theEnds);
// Visit nodes one frontier at a time - Breadth first.
while (frontier.Count > 0) {
// Move frontier into visiting, reset frontier.
Set<Node> visiting = frontier;
frontier = new Set<Node>();
// Prevent adding nodes being visited to frontier
visited.AddRange(visiting);
// Add all connected nodes to frontier.
foreach (Node node in visiting) {
foreach (Node other in node.Connections) {
if (!visited.Contains(other)) {
other.Route = other;
if (ends.Contains(other)) {
return other;
}
frontier.Add(other);
}
}
}
}
return null;
}
Just for the record. This is your graph example:
Where the shortest path from A to M is marked in blue.
It is a very short program in Mathematica:
a = StringSplit["NJKUUGHBNNJHYAPOYJHNRMNIKAIILFGJSNAICZQRNM", ""]
b = Table[a[[i]] -> a[[i + 1]], {i, Length#a - 1}]
vertexNumber[g_, vName_] := Position[ Vertices[g, All], vName][[1, 1]];
Needs["Combinatorica`"]
c = ToCombinatoricaGraph[b]
sp = ShortestPath[c, vertexNumber[c, "A"], vertexNumber[c, "M"]]
vlsp = GetVertexLabels[c, sp]
vlspt = Table[{vlsp[[i]], vlsp[[i + 1]]}, {i, Length#vlsp - 1}]
GraphPlot[b, VertexLabeling -> True, ImageSize -> 250,
DirectedEdges -> True, Method -> {"SpringEmbedding"},
EdgeRenderingFunction ->
(If[Cases[vlspt, {First[#2], Last[#2]}] == {},
{Red, Arrowheads[Medium], Arrow[#1, .1]},
{Blue, Arrowheads[Medium], Arrow[#1, .1]}] &)]
LBushkin's answer is correct. Eric Lippert has a series about implementing the A* algorithm in C#. A* is a more general case of Dijkstra's algorithm : if your cost estimation function always returns 0, it is exactly equivalent to Dijkstra.
Another option would be to use a graph library that has various shortest path algorithms implemented. One I have used in the past and found to be good is QuickGraph. The documentation is quite good. For example, here is a page in the documentation that explains how to use Dijkstra's algorithm.
Because your graph is acyclic the Viterbi algorithm can be used, visit the states in topological order and update the costs in preceding vertices (states).
The below code implements the search algorithm and data structure. I wasn't 100% sure of the definition of the valid based on the original question. But it should be straight forward to modify the code to construct other topologies and solve various dynamic programming task using a DAG.
Changing the outer for loop when computing the state potentials to while loop with a queue will allow for different shortestpath algorithms to used easily changed by changing the queue discipline. For example a binary heap based queue will give Dijkstra algorithm or a FIFO queue will Bellman-Ford algorithm.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DAGShortestPath
{
class Arc
{
public Arc(int nextstate, float cost)
{
Nextstate = nextstate;
Cost = cost;
}
public int Nextstate { get; set; }
public float Cost { get; set; }
};
class State
{
public bool Final { get; set; }
public List<Arc> Arcs { get; set; }
public void AddArc(int nextstate, float cost)
{
if (Arcs == null)
Arcs = new List<Arc>();
Arcs.Add(new Arc(nextstate, cost));
}
}
class Graph
{
List< State > _states = new List< State >();
int _start = -1;
public void AddArc(int state, int nextstate, float cost)
{
while (_states.Count <= state)
AddState();
while (_states.Count <= nextstate)
AddState();
_states[state].AddArc(nextstate, cost);
}
public List<Arc> Arcs(int state)
{
return _states[state].Arcs;
}
public int AddState()
{
_states.Add(new State());
return _states.Count - 1;
}
public bool IsFinal(int state)
{
return _states[state].Final;
}
public void SetFinal(int state)
{
_states[state].Final = true;
}
public void SetStart(int start)
{
_start = -1;
}
public int NumStates { get { return _states.Count; } }
public void Print()
{
for (int i = 0; i < _states.Count; i++)
{
var arcs = _states[i].Arcs;
for (int j = 0; j < arcs.Count; j++)
{
var arc = arcs[j];
Console.WriteLine("{0}\t{1}\t{2}", i, j, arc.Cost);
}
}
}
}
class Program
{
static List<int> ShortertPath(Graph graph)
{
float[] d = new float[graph.NumStates];
int[] tb = new int[graph.NumStates];
for (int i = 0; i < d.Length; i++)
{
d[i] = float.PositiveInfinity;
tb[i] = -1;
}
d[0] = 0.0f;
float bestscore = float.PositiveInfinity;
int beststate = -1;
for (int i = 0; i < graph.NumStates; i++)
{
if (graph.Arcs(i) != null)
{
foreach (var arc in graph.Arcs(i))
{
if (arc.Nextstate < i)
throw new Exception("Graph is not topologically sorted");
float e = d[i] + arc.Cost;
if (e < d[arc.Nextstate])
{
d[arc.Nextstate] = e;
tb[arc.Nextstate] = i;
if (graph.IsFinal(arc.Nextstate) && e < bestscore)
{
bestscore = e;
beststate = arc.Nextstate;
}
}
}
}
}
//Traceback and recover the best final sequence
if (bestscore == float.NegativeInfinity)
throw new Exception("No valid terminal state found");
Console.WriteLine("Best state {0} and score {1}", beststate, bestscore);
List<int> best = new List<int>();
while (beststate != -1)
{
best.Add(beststate);
beststate = tb[beststate];
}
return best;
}
static void Main(string[] args)
{
Graph g = new Graph();
String input = "ABBCAD";
for (int i = 0; i < input.Length - 1; i++)
for (int j = i + 1; j < input.Length; j++)
{
//Modify this of different constraints on-the arcs
//or graph topologies
//if (input[i] < input[j])
g.AddArc(i, j, 1.0f);
}
g.SetStart(0);
//Modify this to make all states final for example
//To compute longest sub-sequences and so on...
g.SetFinal(g.NumStates - 1);
var bestpath = ShortertPath(g);
//Print the best state sequence in reverse
foreach (var v in bestpath)
{
Console.WriteLine(v);
}
}
}
}

Categories