General cleanups to path calculation
This commit is contained in:
		| @@ -12,6 +12,7 @@ import java.util.List; | ||||
|  * @author leijurv | ||||
|  */ | ||||
| public interface IPath { | ||||
|  | ||||
|     /** | ||||
|      * Ordered list of movements to carry out. | ||||
|      * movements.get(i).getSrc() should equal positions.get(i) | ||||
| @@ -44,11 +45,13 @@ public interface IPath { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param currentPosition | ||||
|      * @return | ||||
|      * Determines whether or not a position is within this path. | ||||
|      * | ||||
|      * @param pos The position to check | ||||
|      * @return Whether or not the specified position is in this class | ||||
|      */ | ||||
|     default boolean isInPath(BlockPos currentPosition) { | ||||
|         return positions().contains(currentPosition); | ||||
|     default boolean isInPath(BlockPos pos) { | ||||
|         return positions().contains(pos); | ||||
|     } | ||||
|  | ||||
|     default Tuple<Double, BlockPos> closestPathPos(double x, double y, double z) { | ||||
|   | ||||
| @@ -14,65 +14,73 @@ import java.util.stream.Collectors; | ||||
|  * @author leijurv | ||||
|  */ | ||||
| class Path implements IPath { | ||||
|  | ||||
|     public final BlockPos start; | ||||
|  | ||||
|     public final BlockPos end; | ||||
|  | ||||
|     public final Goal goal; | ||||
|  | ||||
|     /** | ||||
|      * The blocks on the path. Guaranteed that path.get(0) equals start and | ||||
|      * path.get(path.size()-1) equals end | ||||
|      */ | ||||
|     public final ArrayList<BlockPos> path; | ||||
|     final ArrayList<Movement> movements; | ||||
|     public final List<BlockPos> path; | ||||
|  | ||||
|     final List<Movement> movements; | ||||
|  | ||||
|     Path(PathNode start, PathNode end, Goal goal) { | ||||
|         this.start = start.pos; | ||||
|         this.end = end.pos; | ||||
|         this.goal = goal; | ||||
|         this.path = new ArrayList<>(); | ||||
|         this.movements = new ArrayList<>(); | ||||
|         this.path = new LinkedList<>(); | ||||
|         this.movements = new LinkedList<>(); | ||||
|         assemblePath(start, end); | ||||
|         sanityCheck(); | ||||
|     } | ||||
|  | ||||
|     private final void assemblePath(PathNode start, PathNode end) { | ||||
|     private void assemblePath(PathNode start, PathNode end) { | ||||
|         if (!path.isEmpty() || !movements.isEmpty()) { | ||||
|             throw new IllegalStateException(); | ||||
|         } | ||||
|         PathNode current = end; | ||||
|         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<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 | ||||
|         while (!current.equals(start)) { | ||||
|             tempPath.addFirst(current.pos); | ||||
|             tempMovements.addFirst(current.previousMovement); | ||||
|             current = current.previous; | ||||
|         } | ||||
|         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 | ||||
|         //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. | ||||
|         // 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 | ||||
|         // to performantly do that conversion since it knows the length. | ||||
|         path.addAll(tempPath); | ||||
|         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))) { | ||||
|             throw new IllegalStateException(); | ||||
|             throw new IllegalStateException("Start node does not equal first path element"); | ||||
|         } | ||||
|         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) { | ||||
|             throw new IllegalStateException(); | ||||
|             throw new IllegalStateException("Size of path array is unexpected"); | ||||
|         } | ||||
|         for (int i = 0; i < path.size(); i++) { | ||||
|             BlockPos src = path.get(i); | ||||
|             BlockPos dest = path.get(i + 1); | ||||
|             Movement movement = movements.get(i); | ||||
|             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())) { | ||||
|                 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 | ||||
|  */ | ||||
| class PathNode { | ||||
|  | ||||
|     final BlockPos pos; | ||||
|  | ||||
|     final Goal goal; | ||||
|  | ||||
|     final double estimatedCostToGoal; | ||||
|  | ||||
|     // These three fields are mutable and are changed by PathFinder | ||||
|     double cost; | ||||
|  | ||||
|     PathNode previous; | ||||
|  | ||||
|     Movement previousMovement; | ||||
|  | ||||
|     /** | ||||
|      * Is this a member of the open set in A*? (only used during pathfinding) | ||||
|      */ | ||||
|     boolean isOpen; | ||||
|  | ||||
|     /** | ||||
|      * In the linked list of open nodes, which one is next? (only used during pathfinding) | ||||
|      */ | ||||
| @@ -40,30 +46,28 @@ class PathNode { | ||||
|         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 | ||||
|     public int hashCode() {//this is some OG code right here | ||||
|     public int hashCode() { | ||||
|         int hash = 3241; | ||||
|         hash = 3457689 * hash + this.pos.getX(); | ||||
|         hash = 8734625 * hash + this.pos.getY(); | ||||
|         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; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean equals(Object obj) {//autogenerated by netbeans. that's why it looks disgusting. | ||||
|         if (obj == null) { | ||||
|     public boolean equals(Object obj) { | ||||
|         if (obj == null || !(obj instanceof PathNode)) | ||||
|             return false; | ||||
|         } | ||||
|         if (getClass() != obj.getClass()) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         final PathNode other = (PathNode) obj; | ||||
|         if (!Objects.equals(this.pos, other.pos)) { | ||||
|             return false; | ||||
|         } | ||||
|         return Objects.equals(this.goal, other.goal); | ||||
|         return Objects.equals(this.pos, other.pos) && Objects.equals(this.goal, other.goal); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user