Package squidpony.squidgrid
Class Adjacency
java.lang.Object
squidpony.squidgrid.Adjacency
- All Implemented Interfaces:
Serializable
- Direct Known Subclasses:
Adjacency.BasicAdjacency
,Adjacency.RotationAdjacency
public abstract class Adjacency extends Object implements Serializable
Some classes need detailed information about what cells are considered adjacent to other cells, and may
need to construct a customized mapping of cells to their neighbors. Implementations of this abstract
class provide information about all sorts of things, including the distance metric (from DijkstraMap),
but also the maximum number of states that can be moved to in one step (including rotations at the same
point in space, in some cases), and whether the type of map uses a "two-step" rule that needs two
sequential moves in the same direction to be viable and unobstructed to allow movement (which is
important in thin-wall maps).
When CustomDijkstraMap and similar classes need to store more information about a point than just its (x,y) position, they also use implementations of this class to cram more information in a single int. This abstract class provides methods to obtain four different numbers from a single int, though not all implementations may provide all four as viable options. It also provides a utility to get a Coord from an int. X and Y are exactly what they always mean in 2D Coords, R is typically used for rotation, and N is typically used for anything else when it is present. The convention is to use N for the Z-axis when elevation/depth should be tracked, or for any more specialized extensions to the information carried at a point. The composite() method produces a compressed int from X, Y, R, and N values, and the validate() method allows code to quickly check if an int is valid data this class can use. Other information is tracked by fields, such as height, width, rotations, and depths, where the maximum number of possible states is given by height * width * rotations * depths, and the minimum for any of these int fields is 1.
Lastly, the neighborMaps() method produces very important information about what neighbors each cell has, and by modifying the returned int[][], you can produce "portal" effects, wraparound, and other useful concepts. The value it returns consists of an array (with length == maxAdjacent) of arrays (each with the same size, length == width * height * rotations * depth). The values in the inner arrays can be any int between 0 and (width * height * rotations * depth), which refers to the index in any of the inner arrays of a neighboring cell, or can be -1 if there is no neighbor possible here (typically at edges or corners of the map, some of the neighbors are not valid and so use -1). In normal usage, a for loop is used from 0 to maxAdjacent, and in each iteration the same index is looked up (the current cell, encoded as by composite() or obtained as an already-composited neighbor earlier), and this normally gets a different neighbor every time. In methods that do a full-map search or act in a way that can possibly loop back over an existing cell in the presence of wrapping (toroidal or "modulus" maps) or portals, you may want to consider tracking a count of how many cells have been processed and terminate any processing of further cells if the count significantly exceeds the number of cells on the map (terminating when 4 times the cell count is reached may be the most extreme case for very-portal-heavy maps). Created by Tommy Ettinger on 8/12/2016.
When CustomDijkstraMap and similar classes need to store more information about a point than just its (x,y) position, they also use implementations of this class to cram more information in a single int. This abstract class provides methods to obtain four different numbers from a single int, though not all implementations may provide all four as viable options. It also provides a utility to get a Coord from an int. X and Y are exactly what they always mean in 2D Coords, R is typically used for rotation, and N is typically used for anything else when it is present. The convention is to use N for the Z-axis when elevation/depth should be tracked, or for any more specialized extensions to the information carried at a point. The composite() method produces a compressed int from X, Y, R, and N values, and the validate() method allows code to quickly check if an int is valid data this class can use. Other information is tracked by fields, such as height, width, rotations, and depths, where the maximum number of possible states is given by height * width * rotations * depths, and the minimum for any of these int fields is 1.
Lastly, the neighborMaps() method produces very important information about what neighbors each cell has, and by modifying the returned int[][], you can produce "portal" effects, wraparound, and other useful concepts. The value it returns consists of an array (with length == maxAdjacent) of arrays (each with the same size, length == width * height * rotations * depth). The values in the inner arrays can be any int between 0 and (width * height * rotations * depth), which refers to the index in any of the inner arrays of a neighboring cell, or can be -1 if there is no neighbor possible here (typically at edges or corners of the map, some of the neighbors are not valid and so use -1). In normal usage, a for loop is used from 0 to maxAdjacent, and in each iteration the same index is looked up (the current cell, encoded as by composite() or obtained as an already-composited neighbor earlier), and this normally gets a different neighbor every time. In methods that do a full-map search or act in a way that can possibly loop back over an existing cell in the presence of wrapping (toroidal or "modulus" maps) or portals, you may want to consider tracking a count of how many cells have been processed and terminate any processing of further cells if the count significantly exceeds the number of cells on the map (terminating when 4 times the cell count is reached may be the most extreme case for very-portal-heavy maps). Created by Tommy Ettinger on 8/12/2016.
- See Also:
- Serialized Form
-
Nested Class Summary
Nested Classes Modifier and Type Class Description static class
Adjacency.BasicAdjacency
static class
Adjacency.RotationAdjacency
static class
Adjacency.ThinWallAdjacency
-
Field Summary
Fields Modifier and Type Field Description int
blockingRule
If you want obstacles present in orthogonal cells to prevent pathfinding along the diagonal between them, this can be used to make single-cell diagonal walls non-viable to move through, or even to prevent diagonal movement if any one obstacle is orthogonally adjacent to both the start and target cell of a diagonal move.IntDoubleOrderedMap
costRules
Used in place of a double[][] of costs in CustomDijkstraMap; allows you to set the costs to enter tiles (viaaddCostRule(char, double)
oraddCostRule(char, double, boolean)
if the map has rotations).int
depths
Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this.Direction[]
directions
The array of all possible directions this allows, regardless of cost.int
height
Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this.int[]
invertAdjacent
int
maxAdjacent
The maximum number of states that can be considered adjacent; when rotations are present and have a cost this is almost always 3 (move forward, turn left, turn right), and in most other cases this is 4 (when using Manhattan distance) or 8 (for other distance metrics).Measurement
measurement
This affects how distance is measured on diagonal directions vs.int
rotations
Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this.protected boolean
standardCost
boolean
twoStepRule
Only needed for thin-wall maps; this requires two steps in the same direction to both be valid moves for that direction to be considered, and always moves the pathfinder two steps, typically to cells with even numbers for both x and y (where odd-number-position cells are used for edges or corners between cells, and can still be obstacles or possible to pass through, but not stay on).int
width
Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this. -
Constructor Summary
Constructors Constructor Description Adjacency()
-
Method Summary
Modifier and Type Method Description IntDoubleOrderedMap
addCostRule(char tile, double cost)
abstract IntDoubleOrderedMap
addCostRule(char tile, double cost, boolean isRotation)
abstract int
composite(int x, int y, int r, int n)
Encodes up to four components used by this Adjacency, putting them into one int.Coord
extractCoord(int data)
abstract int
extractN(int data)
abstract int
extractR(int data)
abstract int
extractX(int data)
abstract int
extractY(int data)
boolean
hasStandardCost()
abstract boolean
isBlocked(int start, int direction, int[][][] neighbors, double[] map, double wall)
int
move(int start, int x, int y)
int
move(int start, int x, int y, int r, int n)
abstract int[][][]
neighborMaps()
abstract void
portal(int[][][] neighbors, int inputPortal, int outputPortal, boolean twoWay)
IntDoubleOrderedMap
putAllVariants(IntDoubleOrderedMap map, int key, double value)
abstract IntDoubleOrderedMap
putAllVariants(IntDoubleOrderedMap map, int key, double value, int size)
void
putAllVariants(IntVLA list, double[] map, int key, double value)
abstract void
putAllVariants(IntVLA list, double[] map, int key, double value, int size)
void
resetAllVariants(double[] map, int[] keys, double[] values)
void
resetAllVariants(double[] map, int[] keys, double[] values, int size)
abstract void
resetAllVariants(double[] map, int[] keys, int usable, double[] values, int size)
String
show(int data)
String
showMap(int[] map, int r)
abstract boolean
validate(int data)
-
Field Details
-
directions
The array of all possible directions this allows, regardless of cost. -
maxAdjacent
The maximum number of states that can be considered adjacent; when rotations are present and have a cost this is almost always 3 (move forward, turn left, turn right), and in most other cases this is 4 (when using Manhattan distance) or 8 (for other distance metrics). -
twoStepRule
Only needed for thin-wall maps; this requires two steps in the same direction to both be valid moves for that direction to be considered, and always moves the pathfinder two steps, typically to cells with even numbers for both x and y (where odd-number-position cells are used for edges or corners between cells, and can still be obstacles or possible to pass through, but not stay on). -
blockingRule
If you want obstacles present in orthogonal cells to prevent pathfinding along the diagonal between them, this can be used to make single-cell diagonal walls non-viable to move through, or even to prevent diagonal movement if any one obstacle is orthogonally adjacent to both the start and target cell of a diagonal move.
If this is 0, as a special case no orthogonal obstacles will block diagonal moves.
If this is 1, having one orthogonal obstacle adjacent to both the current cell and the cell the pathfinder is trying to diagonally enter will block diagonal moves. This generally blocks movement around corners, the "hard corner" rule used in some games.
If this is 2, having two orthogonal obstacles adjacent to both the current cell and the cell the pathfinder is trying to diagonally enter will block diagonal moves. As an example, if there is a wall to the north and a wall to the east, then the pathfinder won't be able to move northeast even if there is a floor there.
A similar effect can be achieved with a little more control by using thin walls, where the presence of a "thin corner" can block diagonal movement through that corner, or the absence of a blocking wall in a corner space allows movement through it. -
measurement
This affects how distance is measured on diagonal directions vs. orthogonal directions. MANHATTAN should form a diamond shape on a featureless map, while CHEBYSHEV and EUCLIDEAN will form a square. EUCLIDEAN does not affect the length of paths, though it will change the DijkstraMap's gradientMap to have many non-integer values, and that in turn will make paths this finds much more realistic and smooth (favoring orthogonal directions unless a diagonal one is a better option). -
width
Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this. -
height
Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this. -
rotations
Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this. -
depths
Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this. -
standardCost
-
costRules
Used in place of a double[][] of costs in CustomDijkstraMap; allows you to set the costs to enter tiles (viaaddCostRule(char, double)
oraddCostRule(char, double, boolean)
if the map has rotations). A cost of 1.0 is normal for most implementations; higher costs make a movement harder to perform and take more time if the game uses that mechanic, while lower costs (which should always be greater than 0.0) make a move easier to perform. Most games can do perfectly well with just 1.0 and 2.0, if they use this at all, plus possibly a very high value for impossible moves (say, 9999.0 for something like a submarine trying to enter suburbia).
You should not alter costRules in most cases except through the Adjacency's addCostRule method; most Adjacency implementations will set a flag if any cost is set through addCostRule that is different from the default, and this flag determines early-stop behavior in pathfinding (it can be checked withhasStandardCost()
, but cannot be set directly).
Adjacency implementations are expected to set a reasonable default value for when missing keys are queried, usingIntDoubleOrderedMap.defaultReturnValue(double)
; there may be a reason for user code to call this as well. -
invertAdjacent
-
-
Constructor Details
-
Method Details
-
hasStandardCost
-
extractX
-
extractY
-
extractR
-
extractN
-
composite
Encodes up to four components used by this Adjacency, putting them into one int. Returns -1 if the encoded position is out of bounds or otherwise invalid, otherwise any int is possible. You can get the individual values withextractX(int)
,extractY(int)
,extractR(int)
, andextractN(int)
, though not all implementations use R and N.- Parameters:
x
- the x component to encodey
- the y component to encoder
- the rotation component to encode; not all implementations use rotation and the max value variesn
- the bonus component to encode; this can be used for height or other extra data in some implementations- Returns:
- the encoded position as an int; -1 if invalid, non-negative for valid positions
-
validate
-
extractCoord
-
move
-
move
-
neighborMaps
-
portal
public abstract void portal(int[][][] neighbors, int inputPortal, int outputPortal, boolean twoWay) -
isBlocked
public abstract boolean isBlocked(int start, int direction, int[][][] neighbors, double[] map, double wall) -
addCostRule
-
addCostRule
-
putAllVariants
-
putAllVariants
public abstract IntDoubleOrderedMap putAllVariants(IntDoubleOrderedMap map, int key, double value, int size) -
putAllVariants
-
putAllVariants
-
resetAllVariants
-
resetAllVariants
-
resetAllVariants
public abstract void resetAllVariants(double[] map, int[] keys, int usable, double[] values, int size) -
show
-
showMap
-