com.bladecoder.engine.pathfinder.AStarPathFinder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of blade-engine Show documentation
Show all versions of blade-engine Show documentation
Classic point and click adventure game engine
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.bladecoder.engine.pathfinder;
import com.badlogic.gdx.utils.BinaryHeap;
/** A path finder that uses the AStar heuristic based algorithm to determine a path.
*
* Original implementation by Kevin Glass from Slick2D.
*
* @author hneuer */
public class AStarPathFinder> implements NavContext, PathFinder {
/** The set of nodes that we do not yet consider fully searched */
private final BinaryHeap openList = new BinaryHeap();
/** The graph being searched */
private final NavGraph graph;
/** The maximum depth of search we're willing to accept before giving up */
private final int maxSearchDistance;
/** The heuristic we're applying to determine which nodes to search first */
private final AStarHeuristicCalculator heuristicCalculator;
/** The mover going through the path */
private Object mover;
/** The distance searched so far */
private int distance;
/** Unique ID for each search run. Used to mark nodes. */
private int checkedID;
/** The current source node in the context (part of the NavContext implementation) */
private N sourceNodeInContext;
/** Create a path finder with a specific heuristic. */
public AStarPathFinder (NavGraph graph, int maxSearchDistance, AStarHeuristicCalculator heuristic) {
this.heuristicCalculator = heuristic;
this.graph = graph;
this.maxSearchDistance = maxSearchDistance;
}
@Override
public boolean findPath (Object mover, N startNode, N targetNode, NavPath out) {
this.mover = mover;
distance = 0;
if (isBlocked(targetNode, targetNode)) return false;
checkedID++;
if (checkedID < 0) checkedID = 1;
BinaryHeap openList = this.openList;
AStarHeuristicCalculator heuristicCalculator = this.heuristicCalculator;
int maxSearchDistance = this.maxSearchDistance;
openList.clear();
addToOpenList(getAlgoData(startNode));
getAlgoData(targetNode);
AStarAlgoData currentData = null;
int maxDepth = 0;
while (maxDepth < maxSearchDistance && openList.size != 0) {
AStarAlgoData lastData = currentData;
currentData = openList.pop();
currentData.open = false;
distance = currentData.depth;
currentData.closed = true;
if (currentData.node == targetNode && lastData != null && !isBlocked(lastData.node, targetNode)) break;
float currentCost = currentData.cost;
for (N neighborNode : currentData.node.neighbors) {
AStarAlgoData neighborData = getAlgoData(neighborNode);
if (!isBlocked(currentData.node, neighborNode)) {
sourceNodeInContext = startNode;
float nextStepCost = currentCost + graph.getCost(this, neighborNode);
if (nextStepCost < neighborData.cost) {
if (neighborData.open) {
openList.remove(neighborData);
neighborData.open = false;
}
neighborData.closed = false;
}
if (!neighborData.open && !neighborData.closed) {
neighborData.cost = nextStepCost;
neighborData.heuristic = heuristicCalculator.getCost(this, mover, neighborNode, targetNode);
neighborData.depth = currentData.depth + 1;
neighborNode.parent = currentData.node;
maxDepth = Math.max(maxDepth, neighborData.depth);
addToOpenList(neighborData);
}
}
}
}
boolean pathFound = targetNode.parent != null;
if (pathFound) out.fill(startNode, targetNode);
return pathFound;
}
/** Get the AStar data from the given node and reset it if it has not been used in this run. If it does not exist at all (node
* is reached the first time in the very first run) create a new one. */
private AStarAlgoData getAlgoData (N node) {
@SuppressWarnings("unchecked")
AStarAlgoData ad = (AStarAlgoData)node.algoData;
if (node.algoData == null) {
ad = new AStarAlgoData(node);
node.algoData = ad;
}
if (ad.checkedID != checkedID) {
ad.reset();
ad.checkedID = checkedID;
}
return ad;
}
/** Ask the graph if the way from start to target node is blocked. */
private boolean isBlocked (N startNode, N targetNode) {
sourceNodeInContext = startNode;
return graph.blocked(this, targetNode);
}
private void addToOpenList (AStarAlgoData node) {
openList.add(node, node.cost + node.heuristic);
node.open = true;
}
@Override
public Object getMover () {
return mover;
}
@Override
public float getSearchDistance () {
return distance;
}
@Override
public N getSourceNode () {
return sourceNodeInContext;
}
/** The description of a class providing a cost for a given tile based on a target location and entity being moved. This
* heuristic controls what priority is placed on different tiles during the search for a path */
public interface AStarHeuristicCalculator> {
public float getCost (NavContext map, Object mover, N startNode, N targetNode);
}
class AStarAlgoData extends BinaryHeap.Node {
/** Backlink to the node. */
final N node;
/** Heuristic from this node to the target. */
float heuristic;
/** Search depth to reach this node. */
int depth;
/** ID of the current search. */
int checkedID;
/** This node's cost. */
float cost;
/** In the open list */
boolean open;
/** In the closed list */
boolean closed;
public AStarAlgoData (N node) {
super(0);
this.node = node;
}
void reset () {
closed = false;
open = false;
cost = 0;
depth = 0;
node.parent = null;
}
}
}