66
77package com .almasb .fxgl .pathfinding .astar ;
88
9+ import com .almasb .fxgl .core .collection .grid .Cell ;
10+ import com .almasb .fxgl .core .collection .grid .NeighborFilteringOption ;
11+ import static com .almasb .fxgl .core .collection .grid .NeighborFilteringOption .*;
912import com .almasb .fxgl .pathfinding .CellState ;
1013import com .almasb .fxgl .pathfinding .Pathfinder ;
14+ import com .almasb .fxgl .pathfinding .heuristic .Heuristic ;
15+ import com .almasb .fxgl .pathfinding .heuristic .ManhattanDistance ;
16+ import com .almasb .fxgl .pathfinding .heuristic .OctileDistance ;
1117
1218import java .util .*;
1319
@@ -18,11 +24,20 @@ public final class AStarPathfinder implements Pathfinder<AStarCell> {
1824
1925 private final AStarGrid grid ;
2026
27+ private final Heuristic <AStarCell > defaultHeuristic ;
28+ private final Heuristic <AStarCell > diagonalHeuristic ;
29+
2130 private boolean isCachingPaths = false ;
2231 private Map <CacheKey , List <AStarCell >> cache = new HashMap <>();
2332
2433 public AStarPathfinder (AStarGrid grid ) {
34+ this (grid , new ManhattanDistance <>(10 ), new OctileDistance <>());
35+ }
36+
37+ public AStarPathfinder (AStarGrid grid , Heuristic <AStarCell > defaultHeuristic , Heuristic <AStarCell > diagonalHeuristic ) {
2538 this .grid = grid ;
39+ this .defaultHeuristic = defaultHeuristic ;
40+ this .diagonalHeuristic = diagonalHeuristic ;
2641 }
2742
2843 public AStarGrid getGrid () {
@@ -46,11 +61,21 @@ public List<AStarCell> findPath(int sourceX, int sourceY, int targetX, int targe
4661 return findPath (grid .getData (), grid .get (sourceX , sourceY ), grid .get (targetX , targetY ));
4762 }
4863
64+ @ Override
65+ public List <AStarCell > findPath (int sourceX , int sourceY , int targetX , int targetY , NeighborFilteringOption neighborFilteringOption ) {
66+ return findPath (grid .getData (), grid .get (sourceX , sourceY ), grid .get (targetX , targetY ), neighborFilteringOption );
67+ }
68+
4969 @ Override
5070 public List <AStarCell > findPath (int sourceX , int sourceY , int targetX , int targetY , List <AStarCell > busyCells ) {
5171 return findPath (grid .getData (), grid .get (sourceX , sourceY ), grid .get (targetX , targetY ), busyCells .toArray (new AStarCell [0 ]));
5272 }
5373
74+ @ Override
75+ public List <AStarCell > findPath (int sourceX , int sourceY , int targetX , int targetY , NeighborFilteringOption neighborFilteringOption , List <AStarCell > busyCells ) {
76+ return findPath (grid .getData (), grid .get (sourceX , sourceY ), grid .get (targetX , targetY ), neighborFilteringOption , busyCells .toArray (new AStarCell [0 ]));
77+ }
78+
5479 /**
5580 * Since the equality check is based on references,
5681 * start and target must be elements of the array.
@@ -62,6 +87,20 @@ public List<AStarCell> findPath(int sourceX, int sourceY, int targetX, int targe
6287 * @return path as list of nodes from start (excl) to target (incl) or empty list if no path found
6388 */
6489 public List <AStarCell > findPath (AStarCell [][] grid , AStarCell start , AStarCell target , AStarCell ... busyNodes ) {
90+ return findPath (grid , start , target , NeighborFilteringOption .FOUR_DIRECTIONS , busyNodes );
91+ }
92+
93+ /**
94+ * Since the equality check is based on references,
95+ * start and target must be elements of the array.
96+ *
97+ * @param grid the grid of nodes
98+ * @param start starting node
99+ * @param target target node
100+ * @param busyNodes busy "unwalkable" nodes
101+ * @return path as list of nodes from start (excl) to target (incl) or empty list if no path found
102+ */
103+ public List <AStarCell > findPath (AStarCell [][] grid , AStarCell start , AStarCell target , NeighborFilteringOption neighborFilteringOption , AStarCell ... busyNodes ) {
65104 if (start == target || target .getState () == CellState .NOT_WALKABLE )
66105 return Collections .emptyList ();
67106
@@ -75,10 +114,12 @@ public List<AStarCell> findPath(AStarCell[][] grid, AStarCell start, AStarCell t
75114 }
76115 }
77116
117+ Heuristic <AStarCell > heuristic = (neighborFilteringOption .is (FOUR_DIRECTIONS )) ? defaultHeuristic : diagonalHeuristic ;
118+
78119 // reset grid cells data
79120 for (int y = 0 ; y < grid [0 ].length ; y ++) {
80121 for (int x = 0 ; x < grid .length ; x ++) {
81- grid [x ][y ].setHCost (Math . abs ( target . getX () - x ) + Math . abs ( target . getY () - y ));
122+ grid [x ][y ].setHCost (heuristic . getCost ( x , y , target ));
82123 grid [x ][y ].setParent (null );
83124 grid [x ][y ].setGCost (0 );
84125 }
@@ -92,7 +133,7 @@ public List<AStarCell> findPath(AStarCell[][] grid, AStarCell start, AStarCell t
92133 boolean found = false ;
93134
94135 while (!found && !closed .contains (target )) {
95- for (AStarCell neighbor : getValidNeighbors (current , busyNodes )) {
136+ for (AStarCell neighbor : getValidNeighbors (current , neighborFilteringOption , busyNodes )) {
96137 if (neighbor == target ) {
97138 target .setParent (current );
98139 found = true ;
@@ -101,16 +142,18 @@ public List<AStarCell> findPath(AStarCell[][] grid, AStarCell start, AStarCell t
101142 }
102143
103144 if (!closed .contains (neighbor )) {
145+ int gCost = isDiagonal (current , neighbor ) ? diagonalHeuristic .getWeight () : defaultHeuristic .getWeight ();
146+ int newGCost = current .getGCost () + gCost ;
147+
104148 if (open .contains (neighbor )) {
105- int newG = current .getGCost () + 10 ;
149+ if ( newGCost < neighbor .getGCost ()) {
106150
107- if (newG < neighbor .getGCost ()) {
108151 neighbor .setParent (current );
109- neighbor .setGCost (newG );
152+ neighbor .setGCost (newGCost );
110153 }
111154 } else {
112155 neighbor .setParent (current );
113- neighbor .setGCost (current . getGCost () + 10 );
156+ neighbor .setGCost (newGCost );
114157 open .add (neighbor );
115158 }
116159 }
@@ -165,10 +208,15 @@ private List<AStarCell> buildPath(AStarCell start, AStarCell target) {
165208 * @param busyNodes nodes which are busy, i.e. walkable but have a temporary obstacle
166209 * @return neighbors of the node
167210 */
168- private List <AStarCell > getValidNeighbors (AStarCell node , AStarCell ... busyNodes ) {
169- var result = grid .getNeighbors (node .getX (), node .getY ());
211+ private List <AStarCell > getValidNeighbors (AStarCell node , NeighborFilteringOption neighborFilteringOption , AStarCell ... busyNodes ) {
212+ var result = grid .getNeighbors (node .getX (), node .getY (), neighborFilteringOption );
170213 result .removeAll (Arrays .asList (busyNodes ));
171214 result .removeIf (cell -> !cell .isWalkable ());
172215 return result ;
173216 }
217+
218+ private boolean isDiagonal (Cell current , Cell neighbor ) {
219+ return neighbor .getX () - current .getX () != 0 && neighbor .getY () - current .getY () != 0 ;
220+ }
221+
174222}
0 commit comments