Skip to content

Commit

Permalink
update pathfinding API to use any AStarCell derivative
Browse files Browse the repository at this point in the history
  • Loading branch information
AlmasB committed Mar 21, 2024
1 parent bc81316 commit 2e2ec77
Show file tree
Hide file tree
Showing 14 changed files with 301 additions and 168 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

package com.almasb.fxgl.pathfinding.astar;

import com.almasb.fxgl.core.collection.grid.Grid;
import com.almasb.fxgl.entity.Entity;
import com.almasb.fxgl.entity.GameWorld;
import com.almasb.fxgl.pathfinding.CellState;
Expand All @@ -17,9 +16,13 @@
import java.util.stream.Collectors;

/**
* Default implementation of the TraversableGrid<AStarCell> for pathfinding purposes.
* Domain-specific grids, or those that require extra functionality, should extend
* TraversableGrid directly.
*
* @author Almas Baimagambetov (AlmasB) ([email protected])
*/
public class AStarGrid extends Grid<AStarCell> {
public final class AStarGrid extends TraversableGrid<AStarCell> {

/**
* Constructs A* grid with A* cells using given width and height.
Expand All @@ -29,13 +32,6 @@ public AStarGrid(int width, int height) {
super(AStarCell.class, width, height, (x, y) -> new AStarCell(x, y, CellState.WALKABLE));
}

public List<AStarCell> getWalkableCells() {
return getCells()
.stream()
.filter(c -> c.getState().isWalkable())
.collect(Collectors.toList());
}

/**
* Generates an A* grid from the given world data.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
* @author Almas Baimagambetov ([email protected])
*/
@Required(CellMoveComponent.class)
public final class AStarMoveComponent extends Component {
public final class AStarMoveComponent<T extends AStarCell> extends Component {

private CellMoveComponent moveComponent;

private LazyValue<AStarPathfinder> pathfinder;
private LazyValue<AStarPathfinder<T>> pathfinder;

private List<AStarCell> path = new ArrayList<>();
private List<T> path = new ArrayList<>();

private Runnable delayedPathCalc = EmptyRunnable.INSTANCE;

Expand All @@ -44,21 +44,21 @@ public final class AStarMoveComponent extends Component {
}
};

public AStarMoveComponent(AStarGrid grid) {
public AStarMoveComponent(TraversableGrid<T> grid) {
this(new LazyValue<>(() -> grid));
}

/**
* This ctor is for cases when the grid has not been constructed yet.
*/
public AStarMoveComponent(LazyValue<AStarGrid> grid) {
pathfinder = new LazyValue<>(() -> new AStarPathfinder(grid.get()));
public AStarMoveComponent(LazyValue<TraversableGrid<T>> grid) {
pathfinder = new LazyValue<>(() -> new AStarPathfinder<>(grid.get()));
}

/**
* This ctor is for cases when using a pre-built pathfinder.
*/
public AStarMoveComponent(AStarPathfinder pathfinderValue) {
public AStarMoveComponent(AStarPathfinder<T> pathfinderValue) {
pathfinder = new LazyValue<>(() -> pathfinderValue);
}

Expand Down Expand Up @@ -93,7 +93,7 @@ public boolean isAtDestination() {
return isAtDestinationProp.get();
}

public AStarGrid getGrid() {
public TraversableGrid<T> getGrid() {
return pathfinder.get().getGrid();
}

Expand All @@ -104,7 +104,7 @@ public AStarGrid getGrid() {
*
* @return cell where this entity is located
*/
public Optional<AStarCell> getCurrentCell() {
public Optional<T> getCurrentCell() {
var cellX = moveComponent.getCellX();
var cellY = moveComponent.getCellY();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,27 @@
/**
* @author Almas Baimagambetov ([email protected])
*/
public final class AStarPathfinder implements Pathfinder<AStarCell> {
public final class AStarPathfinder<T extends AStarCell> implements Pathfinder<T> {

private final AStarGrid grid;
private final TraversableGrid<T> grid;

private final Heuristic<AStarCell> defaultHeuristic;
private final DiagonalHeuristic<AStarCell> diagonalHeuristic;
private final Heuristic<T> defaultHeuristic;
private final DiagonalHeuristic<T> diagonalHeuristic;

private boolean isCachingPaths = false;
private Map<CacheKey, List<AStarCell>> cache = new HashMap<>();
private Map<CacheKey, List<T>> cache = new HashMap<>();

public AStarPathfinder(AStarGrid grid) {
public AStarPathfinder(TraversableGrid<T> grid) {
this(grid, new ManhattanDistance<>(), new OctileDistance<>());
}

public AStarPathfinder(AStarGrid grid, Heuristic<AStarCell> defaultHeuristic, DiagonalHeuristic<AStarCell> diagonalHeuristic) {
public AStarPathfinder(TraversableGrid<T> grid, Heuristic<T> defaultHeuristic, DiagonalHeuristic<T> diagonalHeuristic) {
this.grid = grid;
this.defaultHeuristic = defaultHeuristic;
this.diagonalHeuristic = diagonalHeuristic;
}

public AStarGrid getGrid() {
public TraversableGrid<T> getGrid() {
return grid;
}

Expand All @@ -58,22 +58,22 @@ public boolean isCachingPaths() {
}

@Override
public List<AStarCell> findPath(int sourceX, int sourceY, int targetX, int targetY) {
public List<T> findPath(int sourceX, int sourceY, int targetX, int targetY) {
return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY));
}

@Override
public List<AStarCell> findPath(int sourceX, int sourceY, int targetX, int targetY, NeighborDirection neighborDirection) {
public List<T> findPath(int sourceX, int sourceY, int targetX, int targetY, NeighborDirection neighborDirection) {
return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY), neighborDirection);
}

@Override
public List<AStarCell> findPath(int sourceX, int sourceY, int targetX, int targetY, List<AStarCell> busyCells) {
return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY), busyCells.toArray(new AStarCell[0]));
public List<T> findPath(int sourceX, int sourceY, int targetX, int targetY, List<T> busyCells) {
return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY), NeighborDirection.FOUR_DIRECTIONS, busyCells.toArray(new AStarCell[0]));
}

@Override
public List<AStarCell> findPath(int sourceX, int sourceY, int targetX, int targetY, NeighborDirection neighborDirection, List<AStarCell> busyCells) {
public List<T> findPath(int sourceX, int sourceY, int targetX, int targetY, NeighborDirection neighborDirection, List<T> busyCells) {
return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY), neighborDirection, busyCells.toArray(new AStarCell[0]));
}

Expand All @@ -87,7 +87,7 @@ public List<AStarCell> findPath(int sourceX, int sourceY, int targetX, int targe
* @param busyNodes busy "unwalkable" nodes
* @return path as list of nodes from start (excl) to target (incl) or empty list if no path found
*/
public List<AStarCell> findPath(AStarCell[][] grid, AStarCell start, AStarCell target, AStarCell... busyNodes) {
public List<T> findPath(T[][] grid, T start, T target, T... busyNodes) {
return findPath(grid, start, target, NeighborDirection.FOUR_DIRECTIONS, busyNodes);
}

Expand All @@ -101,7 +101,7 @@ public List<AStarCell> findPath(AStarCell[][] grid, AStarCell start, AStarCell t
* @param busyNodes busy "unwalkable" nodes
* @return path as list of nodes from start (excl) to target (incl) or empty list if no path found
*/
public List<AStarCell> findPath(AStarCell[][] grid, AStarCell start, AStarCell target, NeighborDirection neighborDirection, AStarCell... busyNodes) {
public List<T> findPath(T[][] grid, T start, T target, NeighborDirection neighborDirection, AStarCell... busyNodes) {
if (start == target || target.getState() == CellState.NOT_WALKABLE)
return Collections.emptyList();

Expand All @@ -115,7 +115,7 @@ public List<AStarCell> findPath(AStarCell[][] grid, AStarCell start, AStarCell t
}
}

Heuristic<AStarCell> heuristic = (neighborDirection == FOUR_DIRECTIONS) ? defaultHeuristic : diagonalHeuristic;
Heuristic<T> heuristic = (neighborDirection == FOUR_DIRECTIONS) ? defaultHeuristic : diagonalHeuristic;

// reset grid cells data
for (int y = 0; y < grid[0].length; y++) {
Expand Down Expand Up @@ -195,13 +195,13 @@ public List<AStarCell> findPath(AStarCell[][] grid, AStarCell start, AStarCell t
return new ArrayList<>(path);
}

private List<AStarCell> buildPath(AStarCell start, AStarCell target) {
List<AStarCell> path = new ArrayList<>();
private List<T> buildPath(T start, T target) {
List<T> path = new ArrayList<>();

AStarCell tmp = target;
T tmp = target;
do {
path.add(tmp);
tmp = tmp.getParent();
tmp = (T) tmp.getParent();
} while (tmp != start);

Collections.reverse(path);
Expand All @@ -213,7 +213,7 @@ private List<AStarCell> buildPath(AStarCell start, AStarCell target) {
* @param busyNodes nodes which are busy, i.e. walkable but have a temporary obstacle
* @return neighbors of the node
*/
private List<AStarCell> getValidNeighbors(AStarCell node, NeighborDirection neighborDirection, AStarCell... busyNodes) {
private List<T> getValidNeighbors(AStarCell node, NeighborDirection neighborDirection, AStarCell... busyNodes) {
var result = grid.getNeighbors(node.getX(), node.getY(), neighborDirection);
result.removeAll(Arrays.asList(busyNodes));
result.removeIf(cell -> !cell.isWalkable());
Expand All @@ -223,5 +223,4 @@ private List<AStarCell> getValidNeighbors(AStarCell node, NeighborDirection neig
private boolean isDiagonal(Cell current, Cell neighbor) {
return neighbor.getX() - current.getX() != 0 && neighbor.getY() - current.getY() != 0;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* FXGL - JavaFX Game Library. The MIT License (MIT).
* Copyright (c) AlmasB ([email protected]).
* See LICENSE for details.
*/

package com.almasb.fxgl.pathfinding.astar;

import com.almasb.fxgl.core.collection.grid.CellGenerator;
import com.almasb.fxgl.core.collection.grid.Grid;

import java.util.List;
import java.util.stream.Collectors;

/**
* The supertype for any grid that can be traversed using A* pathfinding.
*
* @author Almas Baim (https://github.com/AlmasB)
*/
public abstract class TraversableGrid<T extends AStarCell> extends Grid<T> {

public TraversableGrid(Class<T> type, int width, int height) {
super(type, width, height);
}

public TraversableGrid(Class<T> type, int width, int height, CellGenerator<T> populateFunction) {
super(type, width, height, populateFunction);
}

public TraversableGrid(Class<T> type, int width, int height, int cellWidth, int cellHeight, CellGenerator<T> populateFunction) {
super(type, width, height, cellWidth, cellHeight, populateFunction);
}

/**
* @return all cells whose state is CellState.WALKABLE
*/
public List<T> getWalkableCells() {
return getCells()
.stream()
.filter(c -> c.getState().isWalkable())
.collect(Collectors.toList());
}
}
Loading

0 comments on commit 2e2ec77

Please sign in to comment.