General cleanups to path calculation
This commit is contained in:
parent
5537954180
commit
77f6e1c6c4
@ -12,6 +12,7 @@ import java.util.List;
|
|||||||
* @author leijurv
|
* @author leijurv
|
||||||
*/
|
*/
|
||||||
public interface IPath {
|
public interface IPath {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ordered list of movements to carry out.
|
* Ordered list of movements to carry out.
|
||||||
* movements.get(i).getSrc() should equal positions.get(i)
|
* movements.get(i).getSrc() should equal positions.get(i)
|
||||||
@ -44,11 +45,13 @@ public interface IPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param currentPosition
|
* Determines whether or not a position is within this path.
|
||||||
* @return
|
*
|
||||||
|
* @param pos The position to check
|
||||||
|
* @return Whether or not the specified position is in this class
|
||||||
*/
|
*/
|
||||||
default boolean isInPath(BlockPos currentPosition) {
|
default boolean isInPath(BlockPos pos) {
|
||||||
return positions().contains(currentPosition);
|
return positions().contains(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
default Tuple<Double, BlockPos> closestPathPos(double x, double y, double z) {
|
default Tuple<Double, BlockPos> closestPathPos(double x, double y, double z) {
|
||||||
|
@ -14,65 +14,73 @@ import java.util.stream.Collectors;
|
|||||||
* @author leijurv
|
* @author leijurv
|
||||||
*/
|
*/
|
||||||
class Path implements IPath {
|
class Path implements IPath {
|
||||||
|
|
||||||
public final BlockPos start;
|
public final BlockPos start;
|
||||||
|
|
||||||
public final BlockPos end;
|
public final BlockPos end;
|
||||||
|
|
||||||
public final Goal goal;
|
public final Goal goal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The blocks on the path. Guaranteed that path.get(0) equals start and
|
* The blocks on the path. Guaranteed that path.get(0) equals start and
|
||||||
* path.get(path.size()-1) equals end
|
* path.get(path.size()-1) equals end
|
||||||
*/
|
*/
|
||||||
public final ArrayList<BlockPos> path;
|
public final List<BlockPos> path;
|
||||||
final ArrayList<Movement> movements;
|
|
||||||
|
final List<Movement> movements;
|
||||||
|
|
||||||
Path(PathNode start, PathNode end, Goal goal) {
|
Path(PathNode start, PathNode end, Goal goal) {
|
||||||
this.start = start.pos;
|
this.start = start.pos;
|
||||||
this.end = end.pos;
|
this.end = end.pos;
|
||||||
this.goal = goal;
|
this.goal = goal;
|
||||||
this.path = new ArrayList<>();
|
this.path = new LinkedList<>();
|
||||||
this.movements = new ArrayList<>();
|
this.movements = new LinkedList<>();
|
||||||
assemblePath(start, end);
|
assemblePath(start, end);
|
||||||
sanityCheck();
|
sanityCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final void assemblePath(PathNode start, PathNode end) {
|
private void assemblePath(PathNode start, PathNode end) {
|
||||||
if (!path.isEmpty() || !movements.isEmpty()) {
|
if (!path.isEmpty() || !movements.isEmpty()) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
PathNode current = end;
|
PathNode current = end;
|
||||||
LinkedList<BlockPos> tempPath = new LinkedList<>();//repeatedly inserting to the beginning of an arraylist is O(n^2)
|
LinkedList<BlockPos> tempPath = new LinkedList<>(); // Repeatedly inserting to the beginning of an arraylist is O(n^2)
|
||||||
LinkedList<Movement> tempMovements = new LinkedList<>();//instead, do it into a linked list, then convert at the end
|
LinkedList<Movement> tempMovements = new LinkedList<>(); // Instead, do it into a linked list, then convert at the end
|
||||||
while (!current.equals(start)) {
|
while (!current.equals(start)) {
|
||||||
tempPath.addFirst(current.pos);
|
tempPath.addFirst(current.pos);
|
||||||
tempMovements.addFirst(current.previousMovement);
|
tempMovements.addFirst(current.previousMovement);
|
||||||
current = current.previous;
|
current = current.previous;
|
||||||
}
|
}
|
||||||
tempPath.addFirst(start.pos);
|
tempPath.addFirst(start.pos);
|
||||||
//can't directly convert from the PathNode pseudo linked list to an array because we don't know how long it is
|
// Can't directly convert from the PathNode pseudo linked list to an array because we don't know how long it is
|
||||||
//inserting into a LinkedList<E> keeps track of length, then when we addall (which calls .toArray) it's able
|
// inserting into a LinkedList<E> keeps track of length, then when we addall (which calls .toArray) it's able
|
||||||
//to performantly do that conversion since it knows the length.
|
// to performantly do that conversion since it knows the length.
|
||||||
path.addAll(tempPath);
|
path.addAll(tempPath);
|
||||||
movements.addAll(tempMovements);
|
movements.addAll(tempMovements);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sanityCheck() {
|
/**
|
||||||
|
* Performs a series of checks to ensure that the assembly of the path went as expected.
|
||||||
|
*/
|
||||||
|
private void sanityCheck() {
|
||||||
if (!start.equals(path.get(0))) {
|
if (!start.equals(path.get(0))) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException("Start node does not equal first path element");
|
||||||
}
|
}
|
||||||
if (!end.equals(path.get(path.size() - 1))) {
|
if (!end.equals(path.get(path.size() - 1))) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException("End node does not equal last path element");
|
||||||
}
|
}
|
||||||
if (path.size() != movements.size() + 1) {
|
if (path.size() != movements.size() + 1) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException("Size of path array is unexpected");
|
||||||
}
|
}
|
||||||
for (int i = 0; i < path.size(); i++) {
|
for (int i = 0; i < path.size(); i++) {
|
||||||
BlockPos src = path.get(i);
|
BlockPos src = path.get(i);
|
||||||
BlockPos dest = path.get(i + 1);
|
BlockPos dest = path.get(i + 1);
|
||||||
Movement movement = movements.get(i);
|
Movement movement = movements.get(i);
|
||||||
if (!src.equals(movement.getSrc())) {
|
if (!src.equals(movement.getSrc())) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException("Path source is not equal to the movement source");
|
||||||
}
|
}
|
||||||
if (!dest.equals(movement.getDest())) {
|
if (!dest.equals(movement.getDest())) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException("Path destination is not equal to the movement destination");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,19 +12,25 @@ import java.util.Objects;
|
|||||||
* @author leijurv
|
* @author leijurv
|
||||||
*/
|
*/
|
||||||
class PathNode {
|
class PathNode {
|
||||||
|
|
||||||
final BlockPos pos;
|
final BlockPos pos;
|
||||||
|
|
||||||
final Goal goal;
|
final Goal goal;
|
||||||
|
|
||||||
final double estimatedCostToGoal;
|
final double estimatedCostToGoal;
|
||||||
|
|
||||||
// These three fields are mutable and are changed by PathFinder
|
// These three fields are mutable and are changed by PathFinder
|
||||||
double cost;
|
double cost;
|
||||||
|
|
||||||
PathNode previous;
|
PathNode previous;
|
||||||
|
|
||||||
Movement previousMovement;
|
Movement previousMovement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this a member of the open set in A*? (only used during pathfinding)
|
* Is this a member of the open set in A*? (only used during pathfinding)
|
||||||
*/
|
*/
|
||||||
boolean isOpen;
|
boolean isOpen;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In the linked list of open nodes, which one is next? (only used during pathfinding)
|
* In the linked list of open nodes, which one is next? (only used during pathfinding)
|
||||||
*/
|
*/
|
||||||
@ -40,30 +46,28 @@ class PathNode {
|
|||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
// TODO possibly reimplement hashCode and equals. They are necessary for this class to function but they could be done better
|
* TODO: Possibly reimplement hashCode and equals. They are necessary for this class to function but they could be done better
|
||||||
|
*
|
||||||
|
* @return The hash code value for this {@link PathNode}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {//this is some OG code right here
|
public int hashCode() {
|
||||||
int hash = 3241;
|
int hash = 3241;
|
||||||
hash = 3457689 * hash + this.pos.getX();
|
hash = 3457689 * hash + this.pos.getX();
|
||||||
hash = 8734625 * hash + this.pos.getY();
|
hash = 8734625 * hash + this.pos.getY();
|
||||||
hash = 2873465 * hash + this.pos.getZ();
|
hash = 2873465 * hash + this.pos.getZ();
|
||||||
hash = 3241543 * hash + Objects.hashCode(this.goal);//don't call goal.hashcode. this calls objects hashcode to verify that the actual goal objects are == identical, which is important for node caching
|
// Don't call goal.hashCode(). this calls objects hashcode to verify that the actual goal objects are == identical, which is important for node caching
|
||||||
|
hash = 3241543 * hash + Objects.hashCode(this.goal);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {//autogenerated by netbeans. that's why it looks disgusting.
|
public boolean equals(Object obj) {
|
||||||
if (obj == null) {
|
if (obj == null || !(obj instanceof PathNode))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
if (getClass() != obj.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final PathNode other = (PathNode) obj;
|
final PathNode other = (PathNode) obj;
|
||||||
if (!Objects.equals(this.pos, other.pos)) {
|
return Objects.equals(this.pos, other.pos) && Objects.equals(this.goal, other.goal);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return Objects.equals(this.goal, other.goal);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user