Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (477)
Games in Android Showcase (107)
games submitted by our members
Games in WIP (535)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Finding a path to nearest walkable tile, of a nonwalkable tile  (Read 4929 times)
0 Members and 1 Guest are viewing this topic.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Posted 2012-03-20 03:53:09 »

I have my AStar pathfinding set up, and it works great! However, I can only find paths to tiles I define.
If theres not a path to this location, it will return null.

The scenario is this: I want to go to a tree at x3 y6, but I can't feed those coordinates to the pathfinder, because I cant walk on the tree.
I also just can't grab the coordinate next to it, because it might not be the nearest to the tree. That's all the functionality it needs, so it's not quite as wide as the title may suggest.

How would you go about this?

Offline loom_weaver

JGO Coder


Medals: 17



« Reply #1 - Posted 2012-03-20 04:12:43 »

One possibility is to make all tiles walkable but some just have a really high weight.

As a result, you'll get a path and your character will try to walk it but will be blocked once they reach the obstacle.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #2 - Posted 2012-03-20 04:34:37 »

One possibility is to make all tiles walkable but some just have a really high weight.

As a result, you'll get a path and your character will try to walk it but will be blocked once they reach the obstacle.

That does seem a little hacky, and it'll still walk there if there is not other choice :/

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline UprightPath
« Reply #3 - Posted 2012-03-20 04:57:09 »

Closest tile of a non-walkable tile?


Have you tried doing a BFS from the nonwalkable tile, selecting the closest one(s). Then performing your A* to those and picking from there? This will render the closest tile.

If you want to find the closest-walking distance walkable tile, it takes a bit of finesse. You'd have two different pathfinders.

The Non-walkable one's hueristic would add in the distance from the walkable tile to the non-walkable goal. Then you'd keep expanding nodes like a regular A* until the current-cost-to-node > (best-cost-to-node + distance to hueristic tile).

Offline gbeebe

Senior Member


Medals: 5
Projects: 1



« Reply #4 - Posted 2012-03-20 05:00:40 »

Examine the tiles in the closed list, calculate the distance to the destination and pick the one that has the least.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #5 - Posted 2012-03-20 05:22:18 »

Closest tile of a non-walkable tile?


Have you tried doing a BFS from the nonwalkable tile, selecting the closest one(s). Then performing your A* to those and picking from there? This will render the closest tile.

If you want to find the closest-walking distance walkable tile, it takes a bit of finesse. You'd have two different pathfinders.

The Non-walkable one's hueristic would add in the distance from the walkable tile to the non-walkable goal. Then you'd keep expanding nodes like a regular A* until the current-cost-to-node > (best-cost-to-node + distance to hueristic tile).

Can you elaborate on what a BFS is? I didn't quite get the rest of your post Sad
EDIT: I just did some more research, and I found out not only what a BFS is, but also that it could solve my problem.
I suppose I can use the same heuristic as the pathfinder to determine the tile?

Examine the tiles in the closed list, calculate the distance to the destination and pick the one that has the least.

The tile I want could be in the open-list though. If I could make a path to the tree (the tile I want to end next to), it would be the next-last one in the entire path. However, I can't make a paths goal a walkable tile.

Did I mention my movers can't travel diagonals?


Offline Damocles
« Reply #6 - Posted 2012-03-20 05:39:10 »

You should make 2 searches then.

One from the target position to a closeby "A* Node" to check if its valid.
(You can also test several nodes, and pick the shortest as Targetnode)

Then when the mover reaches the End A*  Node, make a local search to the target.


So you have 2 "resolutions" in your pathfinding.

Offline gbeebe

Senior Member


Medals: 5
Projects: 1



« Reply #7 - Posted 2012-03-20 05:40:17 »

If you can't reach the tree, it shouldn't be in your open list.
Offline UprightPath
« Reply #8 - Posted 2012-03-20 05:52:25 »

Hmm, when I stated the second half I had an idea. However, now I don't know for sure if it'd work or not. I'd have to work on it a bit before I actually repeat it.

Offline gbeebe

Senior Member


Medals: 5
Projects: 1



« Reply #9 - Posted 2012-03-20 06:16:11 »

Here's how I would do it, in somewhat pseudo code:

A Node has an x, y, cost, and parentNode.  parentNode is the Node that got you to this Node.
Create a 2d array Node nodeMap[][]; equal to the width and height of the map (in tiles).
Create an ArrayList<Node> openList = new ArrayList<Node>();
Create another ArrayList<Node> path = new ArrayList<Node>();
Add your starting tile to the nodeMap in the X and Y coordinate, set the x and y, cost = 0 and parentNode = null;
Add it to the openList, openList.add(nodeMap[ x ][ y ]);
Now your loop while(!openList.isEmpty()){
   for(int n = 0; n < openList.size(); n++){
      examine the possible reachable neighbors of openList.get(n)
      if this node already has data (check the parent value), see if your cost is better and update it.
      add them to the nodeMap and fill in the values
      Then add that entry to the openList.
      remove the current tile from the openList, openList.remove(n);
   }
}
When you've gotten to this point you've mapped out all reachable tiles.
If the nodeMap[destinationX][destinationY].parent is null, you can't reach the tile
   so scan through your nodeMap and find the tile that is closest to the destination and assign it as the destination.
Otherwise we'll start at the destination.
Work your way back, from destination parent (in nodeMap) to parent to parent until you've reached the start node.
Using 0 as the index will ensure it gets placed at the beginning of the list, path.add(0, thisNode.parent);
Now you have an arraylist that is traceable from start to the destination (or close as you can get to the destination).
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline loom_weaver

JGO Coder


Medals: 17



« Reply #10 - Posted 2012-03-20 07:03:25 »

One possibility is to make all tiles walkable but some just have a really high weight.

As a result, you'll get a path and your character will try to walk it but will be blocked once they reach the obstacle.

That does seem a little hacky, and it'll still walk there if there is not other choice :/

It won't walk through the tile because it can't.  I made an error in typing my original advice.  What I actually meant to say is give non-walkable tiles a high value from the cost function when calculated during A*.  I.e. to your game logic the tile is still unwalkable but to A* it is.
Offline DrZoidberg

Senior Member


Medals: 15



« Reply #11 - Posted 2012-03-20 11:16:36 »

Why don't you use Dijkstra's algorithm? It's very similar to A* exept it uses only one list and it doesn't need you to give it a destination. Just give it the start node and it finds the distance of every node to the start node. So after running that algorithm you just have to look at all the tiles near your tree and check which one has the lowest distance. You could also use something in between the two algorithms. By adding a second list to Dijkstra's algorithm you can get something that's basically an A* without the heuristics and may run faster then Dijkstra.
http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #12 - Posted 2012-03-21 02:38:17 »

Here's how I would do it, in somewhat pseudo code:

A Node has an x, y, cost, and parentNode.  parentNode is the Node that got you to this Node.
Create a 2d array Node nodeMap[][]; equal to the width and height of the map (in tiles).
Create an ArrayList<Node> openList = new ArrayList<Node>();
Create another ArrayList<Node> path = new ArrayList<Node>();
Add your starting tile to the nodeMap in the X and Y coordinate, set the x and y, cost = 0 and parentNode = null;
Add it to the openList, openList.add(nodeMap[ x ][ y ]);
Now your loop while(!openList.isEmpty()){
   for(int n = 0; n < openList.size(); n++){
      examine the possible reachable neighbors of openList.get(n)
      if this node already has data (check the parent value), see if your cost is better and update it.
      add them to the nodeMap and fill in the values
      Then add that entry to the openList.
      remove the current tile from the openList, openList.remove(n);
   }
}
When you've gotten to this point you've mapped out all reachable tiles.
If the nodeMap[destinationX][destinationY].parent is null, you can't reach the tile
   so scan through your nodeMap and find the tile that is closest to the destination and assign it as the destination.
Otherwise we'll start at the destination.
Work your way back, from destination parent (in nodeMap) to parent to parent until you've reached the start node.
Using 0 as the index will ensure it gets placed at the beginning of the list, path.add(0, thisNode.parent);
Now you have an arraylist that is traceable from start to the destination (or close as you can get to the destination).

I have A* and pathfinding, but I can't make the destination the tile I want to end standing next to, because it's unreachable.
This seems like A* from the bottom up.

Why don't you use Dijkstra's algorithm? It's very similar to A* exept it uses only one list and it doesn't need you to give it a destination. Just give it the start node and it finds the distance of every node to the start node. So after running that algorithm you just have to look at all the tiles near your tree and check which one has the lowest distance. You could also use something in between the two algorithms. By adding a second list to Dijkstra's algorithm you can get something that's basically an A* without the heuristics and may run faster then Dijkstra.
http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm

I really don't want to search the entire map, because it's not nessecary, and it doesn't solve the problem.
What I'm looking for, is a Path that leads to anything neighboring x4, y7 for instance.

Offline gbeebe

Senior Member


Medals: 5
Projects: 1



« Reply #13 - Posted 2012-03-21 03:51:30 »

After the A* calculations are done you have a closed list.  This is the list of all the tiles that you can reach.  cycle through those and find the one that is closest to the destination (aka: your tree).  You can use a simple calculation to figure out how close it is to the destination like Math.abs(nX - dX) + Math.abs(nY - dY);
Offline loom_weaver

JGO Coder


Medals: 17



« Reply #14 - Posted 2012-03-21 05:09:07 »

Mads,

I would encourage you to take another look at my solution.  It's not "hacky" in any way and you won't be walking on tiles that are impassible.

In a roguelike or basically any kind of tile game you will have many tiles that will be permanently unwalkable or temporarily unwalkable.  A tree is a good example of the former and simply having a mobile occupy a tile would be an example of the latter.

Take your initial problem that you posed:

1  
2  
3  
.....
.a.T.
.....


a = the starting point and T is a tree.

The most elegant way to determine what path 'a' should take is to do A* from the start 'a' to the destination 'T' even though the tree is unwalkable.

The way to do this is to make it appear to A* that all paths are possible but tiles with obstacles just have a very high cost function.  For example walking on the grass (.) costs 1 pt and the tree is 999.  If you have a monster occupying a tile it would still have a high cost e.g. 25 but not as high as the permanently unwalkable tiles.  The reasoning is that if the mobile encounters a log-jam of other mobiles it should walk up to it and wait until it clears rather than giving up because there is no empty path "right now".

With this approach A* will give you a solution and you only need to execute the algorithm once.  You make your mobile follow the given path as long as its possible and it will do so until it gets to the tree where it will be blocked because in reality, the tree is unwalkable.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #15 - Posted 2012-03-21 05:56:02 »

Mads,

I would encourage you to take another look at my solution.  It's not "hacky" in any way and you won't be walking on tiles that are impassible.

In a roguelike or basically any kind of tile game you will have many tiles that will be permanently unwalkable or temporarily unwalkable.  A tree is a good example of the former and simply having a mobile occupy a tile would be an example of the latter.

Take your initial problem that you posed:

1  
2  
3  
.....
.a.T.
.....


a = the starting point and T is a tree.

The most elegant way to determine what path 'a' should take is to do A* from the start 'a' to the destination 'T' even though the tree is unwalkable.

The way to do this is to make it appear to A* that all paths are possible but tiles with obstacles just have a very high cost function.  For example walking on the grass (.) costs 1 pt and the tree is 999.  If you have a monster occupying a tile it would still have a high cost e.g. 25 but not as high as the permanently unwalkable tiles.  The reasoning is that if the mobile encounters a log-jam of other mobiles it should walk up to it and wait until it clears rather than giving up because there is no empty path "right now".

With this approach A* will give you a solution and you only need to execute the algorithm once.  You make your mobile follow the given path as long as its possible and it will do so until it gets to the tree where it will be blocked because in reality, the tree is unwalkable.

Very good point! This would be much faster (potentially four a*-findPath calls) than the thing I finally did (see below).
However, what would happen in this situation:
1  
2  
3  
..TTT
.a.TG
..TTT

If the user selects the G, the pathfinder will be forced to return a path that is expensive, but it will return it as being possible.


What I finally implemented looks like this. I hope it'll get better, because there is potentially four findPath calls on each of these.
I'm also kind of unhappy with the MAX_VALUE - I think it could've been done nicer. Does anyone have suggestions or comments? Smiley
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
public Path nearestNeighbor(Mover mover, int sx, int sy, int tx, int ty) {   
     
      Path closest = null;
      int stepAmount = Integer.MAX_VALUE;
     
      for (int x=-1;x<2;x++) {
         for (int y=-1;y<2;y++) {
            if ((x == 0) && (y == 0)) { // it's not a neighbour
              continue;
            }

            if ((x != 0) && (y != 0)) { // no diagonal movement
                 continue;
            }  
           
            // determine the location of the neighbor and evaluate it
           int xp = x + tx;
            int yp = y + ty;
            if (isValidLocation(mover, sx, sy, xp, yp)) {
               Path path = findPath(mover, sx, sy, xp, yp);
               if (path != null) { // If there was not a path, continue on, adventurer!
                 int steps = path.getLength();
                  if (steps < stepAmount) { // If it has less steps there than anything before
                    closest = findPath(mover, sx, sy, xp, yp);
                     stepAmount = steps;
                  }
               }
            }  
         }
      }
      // We've run out of search folks!
     if (closest != null) { // Something was found!
        return closest;
      } else { // Nothing was found!
        return null;
      }
   }

Offline loom_weaver

JGO Coder


Medals: 17



« Reply #16 - Posted 2012-03-21 06:21:03 »

If the user selects the G, the pathfinder will be forced to return a path that is expensive, but it will return it as being possible.

Yes, A* will return a path that will take you into the pocket surrounded by the trees 2 tiles away from G.  That is the tile you're seeking right?

If you're merely trying to determine if any path exists I would use a different algorithm such as flood-fill.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #17 - Posted 2012-03-21 06:31:29 »

If the user selects the G, the pathfinder will be forced to return a path that is expensive, but it will return it as being possible.
Yes, A* will return a path that will take you into the pocket surrounded by the trees 2 tiles away from G.  That is the tile you're seeking right?

How does this work? I thought a* avoids the ones with high costs but if there is no other route, it'll take the ones with high cost.
To word it differently, because I did a bad job: I thought the high cost ones was low priority, but not completely out of question.


Offline loom_weaver

JGO Coder


Medals: 17



« Reply #18 - Posted 2012-03-21 06:41:10 »

If the user selects the G, the pathfinder will be forced to return a path that is expensive, but it will return it as being possible.
Yes, A* will return a path that will take you into the pocket surrounded by the trees 2 tiles away from G.  That is the tile you're seeking right?

How does this work? I thought a* avoids the ones with high costs but if there is no other route, it'll take the ones with high cost.
To word it differently, because I did a bad job: I thought the high cost ones was low priority, but not completely out of question.

A* doesn't completely avoid the high cost paths.  It just evaluates the cheaper ones first.

I.e., it will avoid the tiles with high cost because it's cheaper to start exploring the grass in the opposite direction than you're trying to reach.

Imagine you're hiking and want to get to the other side of a body of water.  Depending on the terrain, at some point in time it's cheaper to swim than to walk X number of miles to go around.

In the same manner A* will reach a point where biting the bullet and evaluating the path through the "impassible" trees is the cheapest path there is (note that the trees aren't impassible to A*, they're just really expensive to go through).  In your example, the cost of going directly east through the '.' and 'T' to the 'G' would be 1 + 999 + 1 = 1001.  That's the cheapest path and is what will be returned by A*.

[edit] Now given this path, you move your mobile along it until it can't go any farther (because of the trees).  Your mobile will move into the grass tile surrounded on three sides by trees which is where you wanted it to go all along.
Offline UprightPath
« Reply #19 - Posted 2012-03-21 07:07:33 »

A* is a modified Best-First search. Where the best is the node that has the lowest (traveled-to + remaining) cost. This means that for the most part expensive nodes will not be exploded, the same with paths that go backwards from your goal.

However, the amount of exploration depends on your heuristic. That is the basis of what makes it good. If your Heuristic isn't goo, then you will explore more nodes than necessary. A lot more than necessary or a lot less.

This gets into the idea of Admissibility and Optimal.

If your heuristic does indeed render a cost that is <= the best path, it's admissible. This means that it's likely to only explore nodes that will lead it to the goal, instead of nodes that won't. If it's over, it will explore more of the more expensive nodes. Further, the closer it is to being the exact value, the more likely you are to find the optimal path. If it's lower, you're likely to find a path, but not always the best path. If it's above you're always going to find the best path.

Heuristic-  Result
> Actual    Best path, not Optimal
= Actual    Best path, and Optimal
< Actual    A Path (Can't figure the Optimality, however it'll be faster than the > Actual).

So A* isn't magical. It will not always find the best path, will often find a good path in a good amount of time.


Part of the issue that I have with the 'High' cost for impassable objects is that it if you make them cost too much, they will be the only part of path that matters in deciding your end point. If you make them cost too little, then your pathfinder will often kick out a path that will go through them.

Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #20 - Posted 2012-03-22 05:05:05 »

A* is a modified Best-First search. Where the best is the node that has the lowest (traveled-to + remaining) cost. This means that for the most part expensive nodes will not be exploded, the same with paths that go backwards from your goal.

However, the amount of exploration depends on your heuristic. That is the basis of what makes it good. If your Heuristic isn't goo, then you will explore more nodes than necessary. A lot more than necessary or a lot less.

This gets into the idea of Admissibility and Optimal.

If your heuristic does indeed render a cost that is <= the best path, it's admissible. This means that it's likely to only explore nodes that will lead it to the goal, instead of nodes that won't. If it's over, it will explore more of the more expensive nodes. Further, the closer it is to being the exact value, the more likely you are to find the optimal path. If it's lower, you're likely to find a path, but not always the best path. If it's above you're always going to find the best path.

Heuristic-  Result
> Actual    Best path, not Optimal
= Actual    Best path, and Optimal
< Actual    A Path (Can't figure the Optimality, however it'll be faster than the > Actual).

So A* isn't magical. It will not always find the best path, will often find a good path in a good amount of time.


Part of the issue that I have with the 'High' cost for impassable objects is that it if you make them cost too much, they will be the only part of path that matters in deciding your end point. If you make them cost too little, then your pathfinder will often kick out a path that will go through them.

That's a very good point. Maybe my former implementation is faster than searching a big map?

Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #21 - Posted 2012-03-22 05:05:31 »

A* is a modified Best-First search. Where the best is the node that has the lowest (traveled-to + remaining) cost. This means that for the most part expensive nodes will not be exploded, the same with paths that go backwards from your goal.

However, the amount of exploration depends on your heuristic. That is the basis of what makes it good. If your Heuristic isn't goo, then you will explore more nodes than necessary. A lot more than necessary or a lot less.

This gets into the idea of Admissibility and Optimal.

If your heuristic does indeed render a cost that is <= the best path, it's admissible. This means that it's likely to only explore nodes that will lead it to the goal, instead of nodes that won't. If it's over, it will explore more of the more expensive nodes. Further, the closer it is to being the exact value, the more likely you are to find the optimal path. If it's lower, you're likely to find a path, but not always the best path. If it's above you're always going to find the best path.

Heuristic-  Result
> Actual    Best path, not Optimal
= Actual    Best path, and Optimal
< Actual    A Path (Can't figure the Optimality, however it'll be faster than the > Actual).

So A* isn't magical. It will not always find the best path, will often find a good path in a good amount of time.


Part of the issue that I have with the 'High' cost for impassable objects is that it if you make them cost too much, they will be the only part of path that matters in deciding your end point. If you make them cost too little, then your pathfinder will often kick out a path that will go through them.

That's a very good point. Maybe my former implementation is faster than searching the entirety of a big map?

Offline Damocles
« Reply #22 - Posted 2012-03-22 07:37:25 »

here a nice A* applet to test around, also setting different costs for tiles

http://www.vision.ee.ethz.ch/~cvcourse/astar/AStar.html

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

pw (37 views)
2014-07-24 01:59:36

Riven (38 views)
2014-07-23 21:16:32

Riven (26 views)
2014-07-23 21:07:15

Riven (28 views)
2014-07-23 20:56:16

ctomni231 (58 views)
2014-07-18 06:55:21

Zero Volt (49 views)
2014-07-17 23:47:54

danieldean (41 views)
2014-07-17 23:41:23

MustardPeter (43 views)
2014-07-16 23:30:00

Cero (59 views)
2014-07-16 00:42:17

Riven (56 views)
2014-07-14 18:02:53
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!