Unity A* Pathfinding Crashes - c#

So I was trying to implement the A* algorithm. The Basic Logic is almost complete but there is this one problem I just cant fix.
When request a path Unity stops responding and my PC keeps getting slower until it hangs and I have to force shut down.
Here is the simplified code:
public static List<Node> ReturnPath(Vector3 pointA, Vector3 pointB)
{
/* A Bunch of initializations */
while (!pathFound)
{
//add walkable neighbours to openlist
foreach (Node n in GetNeighbours(currentNode))
{
if (!n.walkable)
continue;
n.gCost = currentNode.gCost + GetManDist (currentNode, n);
n.hCost = GetManDist (n, B);
openList.Add (n);
n.parent = currentNode;
}
//check openList for lowest fcost or lower hCost
for (int i = 0; i < openList.Count; i++)
{
if ((openList [i].fCost < currentNode.fCost) || (openList [i].fCost == currentNode.fCost && openList [i].hCost < currentNode.hCost))
{
//make the currentNode = node with lowest fcost
currentNode = openList [i];
}
openList.Remove (currentNode);
if(!closedList.Contains(currentNode))
closedList.Add (currentNode);
}
//Check if the currentNode it destination
if (currentNode == B)
{
Debug.Log ("Path Detected");
path = RetracePath (A, B);
pathFound = true;
}
}
return path;
}
This works fine as long as the destination is two nodes away. If it's any more than that, the problem mentioned above occurs. Why is that? My first guess that i was putting too much into the openList.
NOTES: Im breaking a 30 x 30 unit platform(floor) into 1x1 squares called "nodes"
GetManDist() Gets the Manhattan Distance between 2 nodes.
UPDATE: Here's the working code. Was too long for a comment
public List<Node> ReturnPath(Vector3 pointA, Vector3 pointB)
{
List<Node> openList = new List<Node>();
List<Node> closedList = new List<Node>();
List<Node> path = new List<Node> ();
Node A = ToNode (pointA);
Node B = ToNode (pointB);
Node currentNode;
bool pathFound = false;
A.hCost = GetManDist(A, B);
A.gCost = 0;
openList.Add (A);
while (!pathFound) // while(openList.Count > 0) might be better because it handles the possibility of a non existant path
{
//Set to arbitrary element
currentNode = openList [0];
//Check openList for lowest fcost or lower hCost
for (int i = 0; i < openList.Count; i++)
{
if ((openList [i].fCost < currentNode.fCost) || ((openList [i].fCost == currentNode.fCost && openList [i].hCost < currentNode.hCost)))
{
//Make the currentNode = node with lowest fcost
currentNode = openList [i];
}
}
//Check if the currentNode is destination
if (currentNode.hCost == 0) //Better than if(currentNode == B)
{
path = RetracePath (A, B);
pathFound = true;
}
//Add walkable neighbours to openlist
foreach (Node n in GetNeighbours(currentNode))
{
//Avoid wasting time checking unwalkable and those already checked
if (!n.walkable || closedList.Contains(n))
continue;
//Movement Cost to neighbour
int newGCost = currentNode.gCost + GetManDist (currentNode, n);
//Calculate g_Cost, Update if new g_cost to neighbour is less than an already calculated one
if (n.gCost > newGCost || !openList.Contains (n))
{
n.gCost = newGCost;
n.hCost = GetManDist (n, B);
n.parent = currentNode; //So we can retrace
openList.Add (n);
}
}
//We don't need you no more...
openList.Remove (currentNode);
//Avoid redundancy of nodes in closedList
if(!closedList.Contains(currentNode))
closedList.Add (currentNode);
}
return path;
}

The problem was with the value of currentNode. Since we are checking for the node with the lowest f_Cost or lower h_Cost in the openlist against currentNode, in some cases when the pathfinding encounters a wall, it has to go back or take a turn which results in increasing f_Cost and h_Cost (both greater than that of the currentNode). Therefore there is no longer any node in the openlist with a lower f_Cost/h_Cost resulting in an infinite loop. The simple solution was setting the currentNode to an arbitrary element in the openList everytime.
Adding
currentNode = openlist[0];
at the beginning of the loop.

Related

Jump Point Search result are not correct

After I completed the how to make a A* pathfinding I wanted to learn more about Jump Point Search.
I did some reasearch how to make one by reading the
orginal paper about it.
So after reading it a couple of times I try it for myself so I made one and it works short of.
so the problem is that he can find a path but not in the most best way see images what I mean.
So my first thought was that I did something wrong with the lowest Fcost so try to change that first but that was not answer. then I looked into the Sucessor function if I did something wrong try to change the order of the script but the result stay the same.
after that I looked into the jump code. But to be honest I don't see it what I did wrong either.
Then I try to find the answer here but, most of the answers resluted in the same result.
Maybe it can be that I have forgot something that this algoritme needs.
sucessor function.
List<Node> IDSucessor(Node currentNode,Node start,Node goal)
{
List<Node> sucessors = new List<Node>();
List<Node> neighbors = new List<Node>();
neighbors = Neigbors(currentNode);
foreach (Node n in neighbors)
{
Node node = n;
node = Jump(currentNode, n.x - currentNode.x, n.y - currentNode.y, start, goal);
if (node != null)
{
if (!closedList.Contains(node))
{
int newMovementCost = (int)currentNode.GCost + GetDistance(currentNode, n);
if (newMovementCost < node.GCost || !openList.Contains(node))
{
node.GCost = newMovementCost;
node.HCost = GetDistance(n, goal);
node.FCost = n.GCost + n.HCost;
node.parent = currentNode;
if (!openList.Contains(node))
{
sucessors.Add(node);
}
}
}
}
}
return sucessors;
}
Jump Function
Node Jump(Node currentNode,int x,int y,Node start,Node goal)
{
Node n = RetunNodeDir(currentNode, x, y);
if (n==null)
{
//if n is null return null
return null;
}
if (n.Col)
{
//if n collider return null.
return null;
}
if (n.x == goal.x && n.y==goal.y)
{
//if it's a goal then the n will be a sucessor
goalFounded = true;
return n;
}
//if A neigbor is forced than return n as it's sucessor.
if (IsForced(n, x, y, start, goal))
{
return n;
}
//if diangle
if(x!=0 && y!=0)
{
if (IsForecedDiangle(n, x, y, start, goal))
{
return n;
}
if (Jump(n, x, 0, start, goal) != null || Jump(n, 0, y, start, goal) != null)
{
return n;
}
}
//jump again
return Jump(n, x, y, start, goal);
}
Force node horizontal. the vertical is the same code but has a diffrent coordination direction. the diangle use also the same code but it needed a extra agrument in the function.
bool IsForcedHor(Node currentNode,int dirX)
{
Node up = RetunNodeDir(currentNode, 0, 1);
Node down = RetunNodeDir(currentNode, 0, - 1);
if(up!=null)
{
if(up.Col)
{
//check next to him.
Node upNext = RetunNodeDir(currentNode, dirX, 1);
if(upNext!=null)
{
if(!upNext.Col)
{
return true;
}
}
}
}
if(down!=null)
{
if(down.Col)
{
Node downNext = RetunNodeDir(currentNode, dirX, - 1);
if (downNext != null)
{
if (!downNext.Col)
{
return true;
}
}
}
}
return false;
}
I hope I gave you enough information.
kind regards,
codinG

A* Path-finding is just barely not working

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!

Monte Carlo tree search

I'm currently working on a MCTS implementation for a simple board game.
I think I almost got it right, but whenever I want to iterate more than 1 time
(time or number(i) as factor for iterations) the game freezes. I would be glad if someone has a solid idea about what the issue could be.
// method to find best turn
Node findBest( )
{
Node startingPoint = new Node();
startingPoint.CurrentField_Boxes = GameBoard.boxs;
startingPoint.CurrentField_H = GameBoard.horizontal_line;
startingPoint.CurrentField_V = GameBoard.vertical_line;
Node rootNode = startingPoint;
for( int i = 0; i < 2; i++)
{
//Selection
Node promisingNode = selectPromisingNode(rootNode);
Debug.Log("POST SELECTION CHECK VON X/Y " + promisingNode.x + "/" + promisingNode.y);
//Expansion
if (!checkField())
{
expandNode(promisingNode);
}
//Simulation
Node nodeToExplore = promisingNode;
Debug.Log("POST 2 SELECTION CHECK VON X/Y " + nodeToExplore.x + "/" + nodeToExplore.y);
if (promisingNode.getChildren().Count > 0)
{
//get random child node
nodeToExplore = promisingNode.getRandomChild();
Debug.Log("POST 3 SELECTION CHECK VON X/Y " + nodeToExplore.x + "/" + nodeToExplore.y);
}
int playoutResult = simulation(nodeToExplore);
Debug.Log("SIM RESULT :" + playoutResult);
//backpropagation
backPropogation(nodeToExplore);
}
Debug.Log("*********************************");
Node bestNode = new Node();
Debug.Log("POST 4 SELECTION CHECK VON X/Y " + rootNode.getBestChild().x + "/" + rootNode.getBestChild().y);
bestNode = rootNode.getBestChild();
return bestNode;
}
private Node selectPromisingNode(Node rootNode)
{
Debug.Log("SELECTING");
Node node = new Node();
node = rootNode;
while (node.getChildren().Count != 0) // checking if terminal
{
findBestNodeWithUCT(node);
}
return node;
}
Node findBestNodeWithUCT( Node root)
{
double j = 0;
int bestIndex = 0; ;
for (int i = 0; i < root.getChildren().Count; i++)
{
if (root.getChildren()[i].getVisitTimes() / root.getChildren()[i].getResult() > j)
{
j = root.getChildren()[i].getVisitTimes() / root.getChildren()[i].getResult();
bestIndex = i;
Debug.Log("BEST INDEX : " + bestIndex);
}
}
Node bestOption = root.getChildren()[bestIndex];
Debug.Log("BEST OPTION RETURN X/Y : " + bestOption.x + "/" + bestOption.y);
return bestOption;
}
private void expandNode(Node node)
{
Debug.Log("Start EXPANSION");
bool vert = false;
bool hori = false;
//Phase I : creating new Node
//create a new node wit certain action A
Node newNode = new Node();
//init and setting parent
newNode.parent[0] = node;
//init gamefield into node
newNode.CurrentField_V = node.CurrentField_V;
newNode.CurrentField_H = node.CurrentField_H;
//init and setting child parent relationship
newNode.visitTimes++;
Debug.Log("ERSTELLE NEUEN KNOTEN");
//Phase II : Validating Action
//create a action a which leads to the expansion
int x = UnityEngine.Random.Range(0,4);
int y = UnityEngine.Random.Range(0, 4);
//checking variables for valid coordinates
Debug.Log("AKTION A IN EXPANSION :" + x + y);
//Prechecking of generated coordinates to set corresponding obj
if (y < 3)
{
vert = true;
}
if (x < 3 )
{
hori = true;
}
else if (x == 3 && y == 3)
{
Debug.Log(" OUT OF BOUNDS :NEW RANDOM");
expandNode(node);
}
//Phase III : Doing Action
if(vert && newNode.CurrentField_V[x, y].tag == "is play" && hori && newNode.CurrentField_H[x, y].tag != "is play")
{
expandNode(node);
}
//setting choice into sim gamefield with green color
if (vert && newNode.CurrentField_V[x, y].tag != "is play")// && GameBoard.sim_vertical_line[x, y].tag != "is play")
{
Debug.Log("SETZEN DER VERTIKALEN AKTION IN KNOTENSTATE");
GameBoard.sim_vertical_line[x, y].tag = "is play";
GameBoard.sim_vertical_line[x, y].GetComponent<SpriteRenderer>().sprite = greenV;
// newNode.CurrentField_V[x, y].tag = "is play";
newNode.x = x;
newNode.y = y;
node.nodeChildren.Add(newNode);
}
if (hori && newNode.CurrentField_H[x, y].tag != "is play" )//&& GameBoard.sim_horizontal_line[x, y].tag != "is play")
{
Debug.Log("SETZEN DER HORIZONTALEN AKTION IN KNOTENSTATE");
GameBoard.sim_horizontal_line[x, y].tag = "is play";
GameBoard.sim_horizontal_line[x, y].GetComponent<SpriteRenderer>().sprite = greenH;
// newNode.CurrentField_H[x, y].tag = "is play";
newNode.x = x;
newNode.y = y;
node.nodeChildren.Add(newNode);
}
else { expandNode(node); }
}
private void backPropogation(Node nodeToExplore)
{
Node tempNode = nodeToExplore;
Debug.Log("BACKPROPAGATION");
while (tempNode != null)
{
int i = 0;
if (i % 2 == 0)
{
tempNode.visitTimes++;
int j = tempNode.result;
tempNode.setResult(j);
tempNode = tempNode.getParent();
}
else if (i % 2 != 0)
{
tempNode.visitTimes++;
int j = tempNode.result;
tempNode.setResult(j * -1);
tempNode = tempNode.getParent();
}
i++;
}
}
One iteration works fine and I already got a better behaviour than on a random bot, but with only about 5-6 nodes max can get created in one iteration.
Thanks a lot.
In the selectPromisingNode() function, you have an infinite loop starting from your second iteration. You start out plugging the root node into that function. Starting from the second iteration, the root node has more than 0 children, so the condition of the while-loop is satisfied. Inside the loop, you do not change the value of the node variable, so you'll infinitely often keep calling findBestNodeWithUCT() with the root node as argument.
You'll probably want to change that line of code inside the loop to: node = findBestNodeWithUCT(node), so that you actually start traversing the tree a bit instead of staying stuck at the root.
I didn't check the rest of the code, so I do not know if there are more errors, but this at least seems to precisely explain the problem you're experiencing

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;

How to find height of BST iteratively?

public void HeightIterative()
{
int counter = 0;
int counter2 = 0;
TreeNode current=root;
if(current != null)
{
while(current.LeftNode!=null)
{
counter++;
current = current.LeftNode;
}
while(current.RightNode!=null)
{
counter2++;
current = current.RightNode;
}
}
int res = 1+Math.Max(counter, counter2);
Console.WriteLine("The Height Of Tree Is: "+res);
}
I wrote iterative method, to calculate height of tree. but in some cases its not working properly. As in case:
10
1
2
3
4
5
18
17
16
15
14
13
what's the problem. according to this sequence height of tree is 6 where as my code is showing 5.
You are using two loops, but each loop investigated only oneside of node, but each node in tree has two sides you should investigate it all. You can do it through recursion call.
private int GetLen(TreeNode node)
{
var result = 0;
if(node != null)
{
result = Math.Max(GetLen(node.LeftNode), GetLen(node.RightNode)) + 1;
}
return result;
}
public void HeightIterative()
{
int res = GetLen(root);
Console.WriteLine("The Height Of Tree Is: "+res);
}
Iterative version:
private class NodeInfo
{
public NodeInfo(TreeNode node, int len)
{
Node = node;
Len = len;
}
public TreeNode Node {get; private set;}
public int Len {get; private set;}
}
public void HeightIterative()
{
int maxLen = 0;
var queue = new Queue<NodeInfo>();
queue.Enqueue(new NodeInfo(root, 1));
while (queue.Count > 0)
{
var item = queue.Dequeue();
var current = item.Node;
var currentLen = item.Len;
if (current.LeftNode != null)
{
queue.Enqueue(new NodeInfo(current.LeftNode, currentLen + 1));
}
if (current.RightNode != null)
{
queue.Enqueue(new NodeInfo(current.RightNode, currentLen + 1));
}
if (currentLen > maxLen)
{
maxLen = currentLen;
}
}
Console.WriteLine("The Height Of Tree Is: " + maxLen);
}
There is a way that does not require any extra space except the queue for storing the nodes.
Add child nodes of a current element and remember size of the queue.
Let each dequeue call decrement the counter
When counter reaches zero that means we are done with current level.
Repeat and count number of times counter reaches zero - this is the depth/height of the tree
Code goes like this :
public int treeDepth(Node root){
int height = 0;
int counterNodesInLevel = 1;
if(root!=null)
{
Queue<Node> queue=new Queue<Node>()
queue.enqueue(root);
while (!queue.isEmpty()){
Node current = queue.dequeue();
counterNodesInLevel -= 1;
if(current.left!=null){
queue.enqueue(current.left)
}
if(current.right!=null){
queue.enqueue(current.right)
}
if (counterNodesInLevel == 0){
height += 1;
counterNodesInLevel = queue.Size();
}
}
}
return height;
}
Time complexity is O(N), space complexity is O(N)
The problem:
You are finding the depth of the left-most node in the first loop, and the right-most in the second, and never interrogating any node that involves going down to the left AND the right.
A solution:
Have a single loop that drills down the left nodes, but adds each right node that it 'skips' into a queue. When you run out of left nodes, pop-off a node form your queue and continue on until the queue becomes empty. You'll need to store the height of each node you put in the queue with that node.
public int Height()
{
int result = GetMaxHeight(this.Root,0);
return result;
}
private int GetMaxHeight(Node<T> node,int count)
{
int leftMax = 0, rightMax = 0;
if (node.Left != null)
{
leftMax = GetMaxHeight(node.Left, count+1);
}
if (node.Right != null)
{
rightMax = GetMaxHeight(node.Right, count + 1);
}
if(node.Left==null && node.Right == null)
{
return count;
}
return Math.Max(leftMax,rightMax);
}

Categories