175 lines
4 KiB
Java
175 lines
4 KiB
Java
package universalelectricity.core.path;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
|
|
import net.minecraftforge.common.ForgeDirection;
|
|
import universalelectricity.core.vector.Vector3;
|
|
|
|
/**
|
|
* An advanced version of pathfinding to find the shortest path between two points. Uses the A*
|
|
* Pathfinding algorithm.
|
|
*
|
|
* @author Calclavia
|
|
*
|
|
*/
|
|
public class PathfinderAStar extends Pathfinder
|
|
{
|
|
/**
|
|
* A pathfinding call back interface used to call back on paths.
|
|
*/
|
|
public IPathCallBack callBackCheck;
|
|
|
|
/**
|
|
* The set of tentative nodes to be evaluated, initially containing the start node
|
|
*/
|
|
public Set<Vector3> openSet;
|
|
|
|
/**
|
|
* The map of navigated nodes storing the data of which position came from which in the format
|
|
* of: X came from Y.
|
|
*/
|
|
public HashMap<Vector3, Vector3> navigationMap;
|
|
|
|
/**
|
|
* Score values, used to determine the score for a path to evaluate how optimal the path is.
|
|
* G-Score is the cost along the best known path while F-Score is the total cost.
|
|
*/
|
|
public HashMap<Vector3, Double> gScore, fScore;
|
|
|
|
/**
|
|
* The node in which the pathfinder is trying to reach.
|
|
*/
|
|
public Vector3 goal;
|
|
|
|
public PathfinderAStar(IPathCallBack callBack, Vector3 goal)
|
|
{
|
|
super(callBack);
|
|
this.goal = goal;
|
|
}
|
|
|
|
@Override
|
|
public boolean findNodes(Vector3 start)
|
|
{
|
|
this.openSet.add(start);
|
|
this.gScore.put(start, 0d);
|
|
this.fScore.put(start, this.gScore.get(start) + getHeuristicEstimatedCost(start, this.goal));
|
|
|
|
while (!this.openSet.isEmpty())
|
|
{
|
|
// Current is the node in openset having the lowest f_score[] value
|
|
Vector3 currentNode = null;
|
|
|
|
double lowestFScore = 0;
|
|
|
|
for (Vector3 node : this.openSet)
|
|
{
|
|
if (currentNode == null || this.fScore.get(node) < lowestFScore)
|
|
{
|
|
currentNode = node;
|
|
lowestFScore = this.fScore.get(node);
|
|
}
|
|
}
|
|
|
|
if (currentNode == null)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (this.callBackCheck.onSearch(this, currentNode))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (currentNode.equals(this.goal))
|
|
{
|
|
this.results = reconstructPath(this.navigationMap, goal);
|
|
return true;
|
|
}
|
|
|
|
this.openSet.remove(currentNode);
|
|
this.closedSet.add(currentNode);
|
|
|
|
for (Vector3 neighbor : getNeighborNodes(currentNode))
|
|
{
|
|
double tentativeGScore = this.gScore.get(currentNode) + currentNode.distanceTo(neighbor);
|
|
|
|
if (this.closedSet.contains(neighbor))
|
|
{
|
|
if (tentativeGScore >= this.gScore.get(neighbor))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!this.openSet.contains(neighbor) || tentativeGScore < this.gScore.get(neighbor))
|
|
{
|
|
this.navigationMap.put(neighbor, currentNode);
|
|
this.gScore.put(neighbor, tentativeGScore);
|
|
this.fScore.put(neighbor, gScore.get(neighbor) + getHeuristicEstimatedCost(neighbor, goal));
|
|
this.openSet.add(neighbor);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Pathfinder reset()
|
|
{
|
|
this.openSet = new HashSet<Vector3>();
|
|
this.navigationMap = new HashMap<Vector3, Vector3>();
|
|
return super.reset();
|
|
}
|
|
|
|
/**
|
|
* A recursive function to back track and find the path in which we have analyzed.
|
|
*/
|
|
public Set<Vector3> reconstructPath(HashMap<Vector3, Vector3> nagivationMap, Vector3 current_node)
|
|
{
|
|
Set<Vector3> path = new HashSet<Vector3>();
|
|
path.add(current_node);
|
|
|
|
if (nagivationMap.containsKey(current_node))
|
|
{
|
|
path.addAll(reconstructPath(nagivationMap, nagivationMap.get(current_node)));
|
|
return path;
|
|
}
|
|
else
|
|
{
|
|
return path;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return An estimated cost between two points.
|
|
*/
|
|
public double getHeuristicEstimatedCost(Vector3 start, Vector3 goal)
|
|
{
|
|
return start.distanceTo(goal);
|
|
}
|
|
|
|
/**
|
|
* @return A Set of neighboring Vector3 positions.
|
|
*/
|
|
public Set<Vector3> getNeighborNodes(Vector3 vector)
|
|
{
|
|
if (this.callBackCheck != null)
|
|
{
|
|
return this.callBackCheck.getConnectedNodes(this, vector);
|
|
}
|
|
else
|
|
{
|
|
Set<Vector3> neighbors = new HashSet<Vector3>();
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
neighbors.add(vector.clone().modifyPositionFromSide(ForgeDirection.getOrientation(i)));
|
|
}
|
|
|
|
return neighbors;
|
|
}
|
|
}
|
|
}
|