RuneHive-Game
Loading...
Searching...
No Matches
AStarPathFinder.java
Go to the documentation of this file.
1package com.runehive.game.world.pathfinding.path.impl;
2
3import com.runehive.game.world.Interactable;
4import com.runehive.game.world.entity.mob.Direction;
5import com.runehive.game.world.entity.mob.Mob;
6import com.runehive.game.world.pathfinding.distance.Distance;
7import com.runehive.game.world.pathfinding.path.Path;
8import com.runehive.game.world.pathfinding.path.PathFinder;
9import com.runehive.game.world.position.Position;
10import com.runehive.game.world.region.Region;
11
12import java.util.*;
13
14/**
15 * Represents a {@code PathFinder} which uses the A* search algorithm(by passing
16 * obstacles).
17 *
18 * @author Graham
19 * @author Major | Suggestions, discussion
20 * @author Artem Batutin <artembatutin@gmail.com>
21 * @author Ryley Kimmel <ryley.kimmel@live.com>
22 */
23public final class AStarPathFinder extends PathFinder {
24
25 /** The Heuristic used by this {@code PathFinder}. */
26 private final Distance heuristic;
27 private final Mob character;
28 private final Map<Position, Node> nodes = new HashMap<>(Region.SIZE * Region.SIZE);
29 private final Set<Node> open = new HashSet<>(Region.SIZE * Region.SIZE);
30 private final Queue<Node> sorted = new PriorityQueue<>(Region.SIZE * Region.SIZE);
31 private final Deque<Position> shortest = new ArrayDeque<>(100);
32
33 /**
34 * Constructs a new {@link AStarPathFinder} with the specified traversal
35 * tool.mapviewer.
36 */
38 this.character = character;
39 this.heuristic = heuristic;
40 }
41
42 /**
43 * A default method to find a path for the specified {@link Mob}.
44 *
45 * @param destination The destination of the path.
46 * @return A {@link Deque} of {@link Position steps} to the specified
47 * destination.
48 */
49 public Path find(Position destination) {
50 Position origin = character.getPosition();
51 if (origin.getHeight() != destination.getHeight())
52 return new Path(null);
53 return find(character, destination);
54 }
55
56 /**
57 * A default method to find a path for the specified {@link Mob}.
58 *
59 * @param destination The destination of the path.
60 * @return A {@link Deque} of {@link Position steps} to the specified
61 * destination.
62 */
63 public Path find(Interactable destination) {
64 Position origin = character.getPosition();
65 if (origin.getHeight() != destination.getHeight())
66 return new Path(null);
67 return find(character, destination);
68 }
69
70 /**
71 * Performs the path finding calculations to find the path using the A*
72 * algorithm.
73 *
74 *
75 * @param source
76 * @param target The target Position.
77 * @return The path to pursue to reach the destination.
78 */
79 @Override
80 public Path find(Mob source, Position target, int targetWidth, int targetLength) {
81 if (source.getHeight() != target.getHeight())
82 return new Path(null);
83
84 Node start = new Node(source.getPosition()), end = new Node(target);
85
86 open.clear();
87 nodes.clear();
88 sorted.clear();
89 shortest.clear();
90
91 nodes.put(source.getPosition(), start);
92 nodes.put(target, end);
93
94 open.add(start);
95 sorted.add(start);
96
97 Node active;
98 Position next;
99 boolean found = false;
100 int distance = (int) (source.getPosition().getDistance(target) * Region.SIZE);
101
102 do {
103 active = getCheapest(sorted);
104 Position position = active.getPosition();
105
106 if (position.equals(target)) {
107 found = true;
108 break;
109 }
110
111 open.remove(active);
112 active.close();
113
114 for (Direction direction : Direction.valid()) {
115 next = position.transform(direction.getFaceLocation());
116 if (traversable(position, source.width(), direction) && traversable(next, source.width(), Direction.getOppositeDirection(direction))) {
117 Node node = nodes.computeIfAbsent(next, Node::new);
118 compare(active, node);
119 }
120 }
121 } while (!open.isEmpty() && sorted.size() < distance);
122
123 if (found) {
124
125 if (end.hasParent()) {
126 active = end;
127 }
128
129 if (active.hasParent()) {
130 Position position = active.getPosition();
131
132 while (!source.getPosition().equals(position)) {
133 shortest.addFirst(position);
134 active = active.getParent();
135 position = active.getPosition();
136
137 if (shortest.size() >= 100) {
138 break;
139 }
140 }
141 }
142
143 return new Path(shortest);
144 }
145 return new Path(null);
146 }
147
148 private void compare(Node active, Node other) {
149 int cost = active.getCost() + heuristic.calculate(active.getPosition(), other.getPosition());
150 if (other.getCost() > cost) {
151 open.remove(other);
152 other.close();
153 } else if (other.isOpen() && !open.contains(other)) {
154 other.setCost(cost);
155 other.setParent(active);
156 open.add(other);
157 sorted.add(other);
158 }
159 }
160
161 /**
162 * Gets the cheapest open {@link Node} from the {@link Queue}.
163 *
164 * @param nodes The queue of nodes.
165 * @return The cheapest node.
166 */
167 private Node getCheapest(Queue<Node> nodes) {
168 Node node = nodes.peek();
169 while (!node.isOpen()) {
170 nodes.poll();
171 node = nodes.peek();
172 }
173 return node;
174 }
175
176}
177
178/**
179 * A {@code Entity} representing a weighted {@link Position}.
180 *
181 * @author Graham
182 * @author Major
183 * @author Artem Batutin <artembatutin@gmail.com>
184 * @author Ryley Kimmel <ryley.kimmel@live.com>
185 */
186final class Node implements Comparable<Node> {
187
188 /** The cost of this {@code Entity}. */
189 private int cost;
190
191 /** Whether or not this {@code Entity} is openShop. */
192 private boolean open = true;
193
194 /** The parent {@code Entity} of this Entity. */
195 private Node parent;
196
197 /** The Position of this {@code Entity}. */
198 private final Position position;
199
200 /**
201 * Creates the {@code Entity} with the specified {@link Position} and cost.
202 *
203 * @param position The Position.
204 */
206 this(position, 0);
207 }
208
209 /**
210 * Creates the {@code Entity} with the specified {@link Position} and cost.
211 *
212 * @param position The Position.
213 * @param cost The cost of the Entity.
214 */
215 private Node(Position position, int cost) {
216 this.position = position;
217 this.cost = cost;
218 }
219
220 /**
221 * Sets the cost of this Entity.
222 *
223 * @param cost The cost.
224 */
225 void setCost(int cost) {
226 this.cost = cost;
227 }
228
229 /**
230 * Gets the cost of this Entity.
231 *
232 * @return The cost.
233 */
234 int getCost() {
235 return cost;
236 }
237
238 /**
239 * Closes this Entity.
240 */
241 public void close() {
242 open = false;
243 }
244
245 /**
246 * Returns whether or not this {@link Node} is openShop.
247 *
248 * @return {@code true} if this Entity is openShop, otherwise {@code false}.
249 */
250 public boolean isOpen() {
251 return open;
252 }
253
254 /**
255 * Sets the parent Entity of this Entity.
256 *
257 * @param parent The parent Entity. May be {@code null}.
258 */
260 this.parent = parent;
261 }
262
263 /**
264 * Gets the parent Entity of this Entity.
265 *
266 * @return The parent Entity.
267 * @throws NoSuchElementException If this Entity does not have a parent.
268 */
270 return parent;
271 }
272
273 /**
274 * Returns whether or not this Entity has a parent Entity.
275 *
276 * @return {@code true} if this Entity has a parent Entity, otherwise {@code
277 * false}.
278 */
279 boolean hasParent() {
280 return parent != null;
281 }
282
283 /**
284 * Gets the {@link Position} this Entity represents.
285 *
286 * @return The position.
287 */
289 return position;
290 }
291
292 /**
293 * Compares the {@code Entity}'s cost with another.
294 *
295 * @param other The other Entity to check.
296 * @return The differential Integer.
297 */
298 @Override
299 public int compareTo(Node other) {
300 return Integer.compare(cost, other.cost);
301 }
302
303 /**
304 * Gets the condition if the Entity equals another object.
305 *
306 * @param obj The object to be checked.
307 * @return {@code true} if it's the same as the object, {@code false}
308 * otherwise.
309 */
310 @Override
311 public final boolean equals(Object obj) {
312 if (this == obj) return true;
313 if (!(obj instanceof Node)) return false;
314 Node other = (Node) obj;
315 return getPosition().getX() == other.getPosition().getX()
316 && getPosition().getY() == other.getPosition().getY()
317 && getPosition().getHeight() == other.getPosition().getHeight()
318 && getCost() == other.getCost()
319 && getParent().getCost() == other.getParent().getCost()
320 && getParent().getPosition().getX() == other.getParent().getPosition().getX()
321 && getParent().getPosition().getY() == other.getParent().getPosition().getY()
323 }
324
325 /**
326 * Gets the node's hash code.
327 *
328 * @return hash code.
329 */
330 @Override
331 public final int hashCode() {
332 final int prime = 31;
333 int result = 1;
334 result = prime * result + position.getX();
335 result = prime * result + position.getY();
336 result = prime * result + position.getHeight();
337 if (hasParent()) {
338 Position p = parent.getPosition();
339 result = prime * result + p.getX();
340 result = prime * result + p.getY();
341 result = prime * result + p.getHeight();
342 }
343 result = prime * result + cost;
344 return result;
345 }
346
347}
348
Handles the mob class.
Definition Mob.java:66
An algorithm used to find a path between two Positions.
boolean traversable(Position current, int size, Direction... directions)
Returns whether or not a Position walking one step in any of the specified Directions would lead to i...
Represents a single path in the path finding system.
Definition Path.java:13
Path find(Interactable destination)
A default method to find a path for the specified Mob.
Node getCheapest(Queue< Node > nodes)
Gets the cheapest open Node from the Queue.
Path find(Mob source, Position target, int targetWidth, int targetLength)
Performs the path finding calculations to find the path using the A* algorithm.
final Distance heuristic
The Heuristic used by this PathFinder.
Path find(Position destination)
A default method to find a path for the specified Mob.
AStarPathFinder(Mob character, Distance heuristic)
Constructs a new AStarPathFinder with the specified traversal tool.mapviewer.
A Entity representing a weighted Position.
Node getParent()
Gets the parent Entity of this Entity.
Node(Position position)
Creates the Entity with the specified Position and cost.
Node(Position position, int cost)
Creates the Entity with the specified Position and cost.
boolean isOpen()
Returns whether or not this Node is openShop.
int compareTo(Node other)
Compares the Entity's cost with another.
Position getPosition()
Gets the Position this Entity represents.
final Position position
The Position of this Entity.
boolean open
Whether or not this Entity is openShop.
final boolean equals(Object obj)
Gets the condition if the Entity equals another object.
final int hashCode()
Gets the node's hash code.
void setCost(int cost)
Sets the cost of this Entity.
Node parent
The parent Entity of this Entity.
void setParent(Node parent)
Sets the parent Entity of this Entity.
boolean hasParent()
Returns whether or not this Entity has a parent Entity.
Represents a single tile on the game world.
Definition Position.java:14
int getHeight()
Gets the height coordinate, or height.
Definition Position.java:51
int getY()
Gets the absolute y coordinate.
Definition Position.java:46
int getX()
Gets the absolute x coordinate.
Definition Position.java:41
double getDistance(Position other)
Gets the distance between this location and another location.
Represents a single region.
Definition Region.java:21
Represents the enumerated directions an entity can walk or face.
static Direction getOppositeDirection(Direction direction)
An object implementing Interactable has uses.
An interface to calculate the distance between two nodes in a Position.
Definition Distance.java:11