Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: [1] 2

Author Topic: Pathfinding by rectangles.  (Read 6059 times)

Soralin

  • Bay Watcher
    • View Profile
Pathfinding by rectangles.
« on: September 30, 2009, 05:53:47 pm »

I've seen something similar to this proposed before, but not with much detail.

The basic idea is to break down the map into smaller pieces, so that pathfinding from one location to another becomes much simpler, because it only has to look at the connections between large chunks of the map, rather than considering every tile.

Filling out the map with these could be done by a relatively simple method to initialize thing with, which can be modified after as the world is altered:

1. Pick a random unincorporated tile with a floor under it.
2. Expand a square out from the tile until you hit an obstacle or the border of another rectangle.
3. Expand out in the remaining directions until you hit another obstacle or rectangle border, keeping the area rectangular as you do so.
4. Repeat from #1 until map is covered.

Then you store connections between each rectangle to the rectangles adjacent to it, and when you need to pathfind, you can just do it from your starting rectangle to the rectangle the goal is in.  Which would be much faster than searching across every single tile.  You could store the borders as simply a range of tiles as well to save space.  Since these are rectangles, then they will only ever meet at a contiguous section of one of their faces.  A single rectangle could have multiple different rectangles along it's border, but never the same one at multiple places.  Rectangle A might have connections to B on the north face from 1-3, and from C on the north face from 5-7, but never B on the north face from 1-3 and 5-7.  Not to mention, a connection to a single rectangle could only ever be on a single face, all that's needed to be stored would be to provide a list of connections, and a single start and end tile for the range for each.  A dwarf could move to one of those tiles, and then just step into the next rectangle on it's path.

Being always open rectangles, free of obstacles, has other advantages as well, for example the distance of a path between two rectangles can be determined just by the manhattan distance, or something similar, like taking the greater of the distances of the x axis and the y axis, rather than the addition of them, if you want to treat diagonals as being the same length.  It only needs to look at the starting and ending coordinates, since the path between will always just be a straight line.  Pathfinding within these rectangles is also trivial, it's basically nothing but a straight line.  Say for example you enter at the west side of a rectangle, and want to leave by some point on the north end, you can always get there just by some combination of north, northeast, and east.  If you compare where the starting and ending points are, you can even always do it with just 2 possible directions to move in, although you might want 3 to be able to sidestep dwarves.  And at worst, you might need to look a square or two ahead to avoid running into another dwarf or such.  This also applies to any two points within a rectangle, all of them can be reached in a straight line.

Now, all of that could be fairly easily be done for a static map, but what happens when the map changes?  There are some possible resolutions to that:

If an obstacle is created in a rectangle:
1. If it is possible to shrink the size of the rectangle by 1 in one dimension or the other to remove the obstacle, then do so, preferring the side which leaves the fewest tiles unincorporated if it's at a corner.
2. If it's not possible to do the above, it can be broken into two smaller rectangles, with some tiles left out between them.

As for the unincorporated tiles created above, you could make them into 1 or 2 long rectangles in either of the above cases.  And filling in the information for the new rectangles should be easy, on one side, it's the parent rectangle, on one side, it's removed, and on all the other sides it's the same, although with the specified ranges for the edges reduced.  Although you would have to then go to each of those remaining connections and modify the connections accordingly

On the other hand, when an obstacle is removed, you could do the opposite:
1. If it is possible to expand an adjacent rectangle into the open tile, then do so.
2. If two rectangles completely share adjacent faces, then merge them to create a new rectangle.
3. Recheck #2 for the new merged rectangle.

Such that, any rectangle which has been broken by the previous method, would be reconnected if those obstacles are removed.  You might also want to have the merging step at the end of the first method also, but then the removal of obstacles doesn't always return things to normal, but that may be an acceptable tradeoff.  And merging is easy too, all you have to do is remove the connection data for the adjoining faces, combine the connection data, different connections just being both present, same connections having their ranges extended, and then make the necessary modifications to the connections of the rectangles it connects to.

On top of this, there are situations where there may be smaller rectangles left laying around, or where the initial rectangle building wasn't ideal.  Like say there's a tree along the faces of 4 rectangles, next to the corner, so that when it's removed, it leaves a 1x1 space, which would have to be it's own rectangle, since none of the others can expand.  Or if a new rectangle gets itself formed in a doorway and across a tiny section of hallway.  You could occasionally pick a random tile laying around, and attempt to generate a new rectangle from that point, ignoring present rectangles, but not obstacles.  If the new rectangle covers a larger area then all the rectangles it would destroy, then it could be created, and new ones made to fill the empty spaces.  Although I'm not sure how that would work in practice, it would lead to larger rectangles, but not necessarily fewer.

Other additions:

The above method only covers normal walkable tiles.  Once you've covered all the walkable tiles, you could then use the same sort of method to make rectangles/rectangular prisms, that consist entirely of open space, or entirely of water, or entirely of magma, etc.  The connections between these wouldn't need to be much, since they'd still only ever connect on a single face.  A piece of ground, open to the sky, and connecting to a single block above it, would only need to specify the connection, and it's starting and ending coordinates, which would just be opposite corners of the rectangle.  Even between two cubes, each would only need to specify the connection to the other, and the corner ends of the connecting face.  What's more, each area would have it's own specific movement type inherent to it.  All of the land tiles already have their rectangles set up, and connection to a rectangular prism of air would only have to be considered for flying creatures for example.  You could have connections marked by movement type, and only creatures that can move in that way would even consider that connection as a possibility.  And movement from an entrance or point within it to any of the connecting faces, or any other point within it, is still a trivial solution.

Stairs might benefit from being covered by rectangles separately as well, you could make a rectangular prism out of them, and then they would be the only land-based cells that would have to consider connections above and below them.  Not to mention you could then more easily exclude them as a movement type (like for caravans).  Ramps wouldn't really need them, since they replace a direction like east, with east-up or east-down, but never both at the same time.  In fact, you could have flat rectangles of land that ignored ramps, and just treated the side that's a level higher or lower as being on the same level, since that's largely how dwarves treat it, and it would still all work out the same.  Well, you might have some issues in the specification of connections to surrounding rectangles or cubes of air.

Things like water flowing out into a rectangle could act like an obstacle, splitting rectangles into smaller pieces, and then, if you don't do any merges on breaks, or if they don't get merged in the meantime by any optimizing process, then when the water dries, it should all merge back together into a single rectangle, most of the time at least, you could end up with some oddities if it dries from the inside first.  Maybe put damp tiles as higher priority to check for optimization.  You could also cover water on the ground as a separate set of rectangles for movement purposes, either group them with ocean rectangular prisms, or have their own water/walkable set.  But all of that could be a bit processor intensive whenever water flows across an area.

Another way would simply be to keep the rectangle intact, but put a marker on it or something, that there's a temporary obstacle like water or magma (or enemy units nearby?)  And then perhaps an A* search rather than just a trivial search could be done to see if the destination could be reached, or upon reaching the rectangle, the dwarf could check to see if it could get to it's destination on the other side of the rectangle, and if not, re-check for another path.  That might be less processor intensive then breaking it apart, at least for large amounts of water, or small numbers of dwarves attempting to cross.  Or the same sort of connection map or whatever exists currently, applied to rectangles like this that are of mixed movement types.

One issue that isn't covered is the current pathfinding cost user modifications.  In order to make that work, it would either have to only apply to whole rectangles, or make it so rectangles can only be formed along constant pathfinding costs, or the pathinding cost is averaged out over the whole rectangle, or have it ignore pathfinding costs, effectively removing them.  Otherwise, it would actually have to check the cost to cross each rectangle itself while it's looking for a path, which would be ugly.  #2 supplies all the added functionality of pathfinding costs, just with a somewhat reduced improvement, since it has to make more rectangles than normal, although not much if your pathfinding change fills a whole corridor or room.  All of the others reduce functionality in some way.

Overall:

This system should provide a large advantage to pathfinding, by significantly cutting down the number of nodes that have to be searched to reach a destination.  In fact, A* would only have to work on the higher level mapping of things, all the movement within rectangles could be handled trivially, by a system that doesn't even need to search.  And it should be able to produce the same results as far as pathfinding choices go, or quite close to them.

It does require a bit of memory for the mapping, pairs of coordinates for the corners of each rectangle, and pairs of coordinates and a pointer or index for each of it's connecting rectangles.  As well as perhaps some other useful information, like a consistent movement type for the whole rectangle, land, air, water, etc.  It could also get some of that memory back, by it's A* search being much shorter, and having to consider and store many fewer nodes, and each dwarf could just store a smaller set of coordinates for the path to their destinations, that they could trivially travel between in basically straight lines, while still allowing them to walk around other dwarves and such.

The adjusting, splitting, fusing, etc. of rectangles could take up a little bit of processor time, but that would only happen when a modification to the world is made.  And should be more than made up for by the greatly reduced time spent on pathfinding.  Especially since calculations relating to rectangles would be constant with the size of your fortress, taking the same amount of processor time no matter how many dwarves you had.
Logged

Bricks

  • Bay Watcher
  • Because you never need one brick.
    • View Profile
Re: Pathfinding by rectangles.
« Reply #1 on: September 30, 2009, 06:17:37 pm »

I think the game already does something like this.
Logged
EMPATHY - being able to feel other peoples' stuff.

BlackPhantom

  • Bay Watcher
    • View Profile
Re: Pathfinding by rectangles.
« Reply #2 on: September 30, 2009, 06:22:34 pm »

Not to mention that this game has a very dynamic environment.
The adjusting, splitting, fusing, etc. of rectangles could take up a little bit of processor time, but that would only happen when a modification to the world is made.
"only"? this would be the case every time you tell your dwarf to dig or build a wall, etc...
Logged

lucusLoC

  • Bay Watcher
    • View Profile
Re: Pathfinding by rectangles.
« Reply #3 on: September 30, 2009, 06:56:11 pm »

i dont know about that black. the game already has to recalc the connectivity map every time you change something. i think this may be a viable solution for cutting down A* processor time if it can be made to divide up the map quickly and efficiently.

it may cause some issues with multi tile creatures though. a dorf can only exsist in on retangle at a time, but a multi tile can path through 2 at a time. how does that work? do you flood fill with larger squares? but then what about fractal like enviroments like forrests? the only way i see it working is if you let the rectangles overlap.

i also like how it would help 3d path finding. that would be a pluss.


Logged
Quantum dumps are proof of "memory" being a perfectly normal dimension in DF. ~Gazz

Footkerchief

  • Bay Watcher
  • The Juffo-Wup is strong in this place.
    • View Profile
Re: Pathfinding by rectangles.
« Reply #4 on: September 30, 2009, 07:20:43 pm »

I think the game already does something like this.

It doesn't use hierarchical pathfinding, no.

OP, apologies for posting without carefully reading your entire post, but from a quick glance it looked a lot like a DF implementation of hierarchical pathfinding (the method in the article also allows for creatures that can fly, swim, take up more than one tile, etc.).
Logged

Soralin

  • Bay Watcher
    • View Profile
Re: Pathfinding by rectangles.
« Reply #5 on: September 30, 2009, 08:01:44 pm »

"only"? this would be the case every time you tell your dwarf to dig or build a wall, etc...
True, but it shouldn't be much of a calculation.  I mean, to dig out a tile, it either creates a new rectangle, would would mean creating it, and then finding which rectangles correspond to any adjacent tiles, and then setting those as connections, and setting the connected node's connections to the new rectangle.  Or it would mean expanding a rectangle, which would even be slightly less work, since it wouldn't have to look at tiles it already knows about.

In comparison, A* search looks at all the surrounding tiles from the starting point, adds them to an ordered list or tree or such in order of (distance traveled + minimum possible remaining distance to destination), and then takes the lowest valued node, and repeats the process, adding all of it's adjacent tiles, that haven't already been counted, into their proper place in the ordered tree, and then takes the lowest of them off the top, and repeats that process for every tile/node it checks, until it reaches it's goal.

So, in short, the calculations it has to do to modify the rectangles from digging out a tile, or even a whole bunch of tiles, should be quite a lot less than the calculations it takes for the miner to find their way to the tiles to be dug out in the first place, even with using this system.

it may cause some issues with multi tile creatures though. a dorf can only exsist in on retangle at a time, but a multi tile can path through 2 at a time. how does that work? do you flood fill with larger squares? but then what about fractal like enviroments like forrests? the only way i see it working is if you let the rectangles overlap.
Heck, multi-tile creatures are already rare enough (just caravans that I can think of currently), that you could just use the normal pathfinding system to handle them instead if nothing else.

OP, apologies for posting without carefully reading your entire post, but from a quick glance it looked a lot like a DF implementation of hierarchical pathfinding (the method in the article also allows for creatures that can fly, swim, take up more than one tile, etc.).
Mmm, no, not quite, that looks like quite a bit of a different system.  They have uniform blocks with different features in them, and calculate paths between edge points on them.  What I had above was using rectangles of arbitrary and different sizes, but of uniform composition, without obstacles or anything in them, so that the pathfinding within them was trivial, just a straight line, and the only A* pathfinding needed would be between them.
Logged

lucusLoC

  • Bay Watcher
    • View Profile
Re: Pathfinding by rectangles.
« Reply #6 on: September 30, 2009, 08:34:13 pm »

but multi tile creatures may not always be rare. any upgrade to the pathing algorithm should consider them.
Logged
Quantum dumps are proof of "memory" being a perfectly normal dimension in DF. ~Gazz

Soralin

  • Bay Watcher
    • View Profile
Re: Pathfinding by rectangles.
« Reply #7 on: October 01, 2009, 11:23:06 am »

but multi tile creatures may not always be rare. any upgrade to the pathing algorithm should consider them.
Well, even if there's a lot of different types of multi-tile units, I don't think there will be huge numbers of them on a map at a time compared to single-tile units.  For example if you have 200 dwarves and other single-tile units, and there's an attacking force with 10 multi-tile siege weapons, then any improvement you can make to single-tile unit movement, gets you 20x more speed than the same improvement to multi-tile unit movement.

Something like this could be somewhat done by the system above, checking the size of the path between adjacent rectangles before including them would handle it.  The problem is, rectangles that are smaller than the multi-tile unit in one direction or another may be impassable or exhibit odd behavior.  As well as diagonals, or areas straddling multiple rectangles.

You could restrict rectangles to not being adjacent to walls or obstacles, and then just treat a 3-tiled creature as a 1-tiled one, as it's center, and the system should work fine.  But you'd need a completely new separate set of rectangles overlayed on the map to store and maintain to do that.
Logged

lucusLoC

  • Bay Watcher
    • View Profile
Re: Pathfinding by rectangles.
« Reply #8 on: October 01, 2009, 11:43:35 am »

they could be rare, until someone mods in 2x2x2 woolly mammoths and 1x1x2 giraffes as barn animals. i am not sure if you could dynamically calculate Hierarchical Clearance-based Pathfinding (in the link) or incorporate some aspict of that in the calculation. (in this case the pathing tile is the top left tile, and all clearances are calculated from there. i think that makes more sense than calculating from the "center"). perhaps you could calculate the clearance of the rectangles boarder tiles and only recalc when that rectangle changes?
Logged
Quantum dumps are proof of "memory" being a perfectly normal dimension in DF. ~Gazz

Sunken

  • Bay Watcher
  • Wabewalker
    • View Profile
Re: Pathfinding by rectangles.
« Reply #9 on: October 01, 2009, 01:42:18 pm »

I wrote an implementation very (very!) similar to what you suggest this summer, except it doesn't use rectangles but what I call "trivially connected zones". I sent a description to Toady and from what I hear, I think something similar is in the works already.
Logged
Alpha version? More like elf aversion!

Slogo

  • Bay Watcher
    • View Profile
Re: Pathfinding by rectangles.
« Reply #10 on: October 01, 2009, 03:54:46 pm »

If you want to use multi-tiled creatures it's 'easy', you group the nodes (tiles) based on the size creature that can occupy the rectangle. Each connection in the graph also contains the directions you can move from one zone to the next: i.e zone A is connected to zone B Vertically and Diagonally. When creatures are determining if they can move from one group to the next they consider the axis opposite the one direction they using to move between zones (or a little more complex calculation if moving diagonally). If the size of the group the are moving to for that axis is < the size of the creature's relevant dimension (depends on the way the creature will be facing when traveling) then they can move between the zones. Otherwise they can't. This will allow for a 1x3 creature to enter a group that's designated as being 1x2 for example. The one limitation (or bonus depending on desired functionality) of this is it will allow for a 1xX creature to snake through a 1x1 hallway with turns. This could be desirable as it'd setup a basis for one type of movement. You can then refine the algorithm for non-snaking creatures so that it checks to see if they can be fully contained within a zone. If they can they can use that zone to 'turn' otherwise they can only pass through that zone if they can continue moving the direction they entered it in. So if you had 2 3x3 rooms connected by a 1x1 room you could pass through as a 1x3 creature but if you had an S bend 1x1 hallway you couldn't because you wouldn't be able to turn at the corners of the hallway, otherwise

An example of how the world would have to be carved up is like this:
Code: [Select]
##.###.#
#...##.#
#......#
#...####

The Groupings would come out like this:
########
##A###E#
#BBB##E#
#BBBCCD#
#BBB####
########
A=1x1
B=3x3
C=1x2
D=1x1
E=1x2

Multi tile creature 1x2 could path from E to A only if it can snake around turns while a 2x2 creature wouldn't be able to move out of zone B.

It's all kinda gibberish sounding sorry, but maybe I'll make a little test implementation app using this type of pathfinding to show what I mean.

lucusLoC

  • Bay Watcher
    • View Profile
Re: Pathfinding by rectangles.
« Reply #11 on: October 01, 2009, 04:54:44 pm »

what about if the creature is in 3 or more zones at the same time?

take this example hallway:

Code: [Select]

o....o
o....o
o...oo
o....o
o....o
oo...o
o....o
o....o



this is broken down into

Code: [Select]

o1111o
o1111o
o222oo
o2223o
o2223o
oo443o
o5555o
o5555o


a 3x3 creature can go from area 1 to 2 just fine, but the diagonal move (alllowed in current df implementation, plus to the player it seems like there is "enough space.") from area 2 would bring the creature to occupie area 2, 3 and 4. neither area 3 or 4 are capable of occupieng the entire creature.

the clearance based calculatoins in the above linked article would work in this hall, as they would correctly identify the clearance needed (assuing diagonal movement) but it seems to only work for square creatures. it seems to me if we want to allow cretures with varing dimensions we will need 3 markers per tile, south clearance, east clearance and verticle clearance (assuming pathing tile is botom north-west)

a node based pathing algorythem would make it so that you would not have to calculate clearances on all tiles on the map, as you would realy only need to calculate clearances on the south east side of ever box, or south east and top in the case of a volumetric impementation (nessisary to bring creatures into the 3rd dimention, and for flyers/swimmers). of course this is only true if you have a max creature size. say we make the max creature size 5x5x5, then we only need to calculate the clearances on the south, east and top edge tiles 4 deep (per side). depending on the size of the space this could eliminate a lot of tiles to mark.

on the other hand, indoors and in cluttered spaces, the boxes will be small and every tile will be marked for clearance anyway. in situations like dense forrests (and fractal inspired fortress layouts) bounding boxes will very rarely contain more than a handfull of squares.

that brings us to another little problem with non square creature. how do you define "adaquate turing space" in a confined enviroment. remember, a lot of what is expected here is going to be based on player intuition. lest take an example of a 3x5 creature (back to 2d for simlicity). now i can go down a 3x whatever corridor just fine, and most player will understand if it cannot make that right angle turn at the end. but now let it go down a 4xX  corrioror with a right angle turn. it looks like it should fit. how do you let the computer now that the 4 squares in the first example block a turn, but the 1 square in the second does not? the real problem here is that rectangular creatures just cannot exsist at a diagonal. this seems to be a serious issue i cant seem to conceptualy solve on a grid.

take the next example hall:

Code: [Select]


o....ooooooo
oo....oooooo
ooo....ooooo
oooo....oooo
ooooo....ooo
oooooo....oo
ooooooo....o



a 3x5 creature should fit down that hall, but in a grid system i just dont see how.

anyhoo, enough rambling. i have caused enough trouble.
Logged
Quantum dumps are proof of "memory" being a perfectly normal dimension in DF. ~Gazz

Sunken

  • Bay Watcher
  • Wabewalker
    • View Profile
Re: Pathfinding by rectangles.
« Reply #12 on: October 02, 2009, 12:48:14 pm »

Multi-tile creatures will probably need to be deformable in some way, witness that last diagonal corridor. Divide up large creatures into body parts, forbid all body parts from being in the same square and enforce some other constraints on their relative positions.
Pathfinding for such a creature would make for some head-scratching, but you could probably do it by analyzing all possible body configurations (once and for all), and then compute a clearance-like value across the map, based on the least restrictive pose for each location.
Logged
Alpha version? More like elf aversion!

Soralin

  • Bay Watcher
    • View Profile
Re: Pathfinding by rectangles.
« Reply #13 on: October 03, 2009, 10:51:08 am »

I wrote an implementation very (very!) similar to what you suggest this summer, except it doesn't use rectangles but what I call "trivially connected zones". I sent a description to Toady and from what I hear, I think something similar is in the works already.
Nice, that's good to know. :)

Code: [Select]
########
##A###E#
#BBB##E#
#BBBCCD#
#BBB####
########
A couple of possible problems, if you're doing this by rectangles, that the rectangles may not end up so ideally placed.  Or that creatures might have problems crossing multiple rectangles, or ones smaller then them (how would a 1x4 creature handle the above?).  Say for example:
Code: [Select]
########
##D###E#
#BBB##E#
#AAAAAA#
#CCC####
########
What if the rectangles got set up like this?  Could a 2x2 creature move around in the room there?

Code: [Select]
Or say for example you have this group of trees outside:
..t....
.......
......t
...t...
t......
.......
....t..

And you end up with rectangles:

..tAAA.
BBBAAA.
BBBAAAt
BBBtDDD
tCCCDDD
.CCCDDD
.CCCt..

And say the tree at the center is removed, and a new rectangle created (since the others can't be expanded):
..tAAA.
BBBAAA.
BBBAAAt
BBBEDDD
tCCCDDD
.CCCDDD
.CCCt..

Now, could a 3x3 wagon path through the center there, or would the 1x1 rectangle act as a block to it's movement?
And how would you determine if it could move through in a way that would allow that?

Most of the ways I can think of handling that would have to simply go back to the default, ignoring the rectangles, and handling it by individual tiles, maybe with the annotated system in that hierarchy link or something similar to it.  But at least for single-tiled creatures this should work quite well. :)
Logged

Impaler[WrG]

  • Bay Watcher
  • Khazad Project Leader
    • View Profile
Re: Pathfinding by rectangles.
« Reply #14 on: October 04, 2009, 12:43:56 am »

Very interesting stuff, I recall thinking about the possibility of some kind of rectangle based system for pathing a ways back, I think I might have posted about it but can't remember.  In any case it could potentially be a BIG boost in processing because an open rectangular area is effortless to path over if you know the end point (the edge square that will transition you too the next rectangle).  A path would only need to consist of the rectangle edges with all the in between steps taken along strait line vectors.  That means you can basically skip the lowest level A* run that a Hierarchical path system would normally need, just the high level search would be adequate.  Combined with the frequently discussed frequently used path caching system I think two or more orders of magnitude reduction in pathing cost is possible.

I think most of you are familiar with the Khazad project, Peterix and I have done a lot of work on 3D display and map extraction and I'd like to branch out into better pathfinding as well.  Anyone who knows C++ is welcome to join, we use Subversion (SVN) on Sourceforge and the project is licensed under the GPL.  If one or more of the folks here wanted to build a path finding system Khazad would be a great place to do it, send me a PM and we can discuss implementation details and add you too the project.
« Last Edit: October 04, 2009, 12:55:03 am by Impaler[WrG] »
Logged
Khazad the Isometric Fortress Engine
Extract forts from DF, load and save them to file and view them in full 3D

Khazad Home Thread
Khazad v0.0.5 Download
Pages: [1] 2