Skip to content

Commit

Permalink
Add an initial location.find_path
Browse files Browse the repository at this point in the history
A basic pathfinder with minimal knowledge of Minecraft or sane movement
patterns.
  • Loading branch information
mcmonkey4eva committed Dec 4, 2014
1 parent edc1adc commit fe10161
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/main/java/net/aufdemrand/denizen/Settings.java
Expand Up @@ -351,4 +351,9 @@ public static int blockTagsMaxBlocks() {
return DenizenAPI.getCurrentInstance().getConfig()
.getInt("Tags.Block tags.Max blocks", 1000000);
}

public static int pathfindingMaxDistance() {
return DenizenAPI.getCurrentInstance().getConfig()
.getInt("Tags.Path fiding.Max distance", 100);

This comment has been minimized.

Copy link
@fullwall

fullwall Feb 26, 2015

Contributor

typo

This comment has been minimized.

Copy link
@mcmonkey4eva

mcmonkey4eva Feb 26, 2015

Author Member

0.0 how did you even -- thanks.

}
}
50 changes: 47 additions & 3 deletions src/main/java/net/aufdemrand/denizen/objects/dLocation.java
Expand Up @@ -9,6 +9,7 @@
import net.aufdemrand.denizen.tags.Attribute;
import net.aufdemrand.denizen.tags.core.EscapeTags;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.aufdemrand.denizen.utilities.PathFinder;
import net.aufdemrand.denizen.utilities.Utilities;
import net.aufdemrand.denizen.utilities.debugging.dB;
import net.aufdemrand.denizen.utilities.entity.Rotation;
Expand Down Expand Up @@ -268,14 +269,20 @@ public int compare(Location loc1, Location loc2) {

@Override
public int hashCode() {
return getBlockX() + getBlockY() + getBlockZ();
return (int)(Math.floor(getX()) + Math.floor(getY()) + Math.floor(getZ()));
}

@Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof dLocation)) return false;
dLocation other = (dLocation) o;
if ((other.getWorld() == null && getWorld() != null)
|| (getWorld() == null && other.getWorld() != null)
|| (getWorld() != null && other.getWorld() != null
&& !getWorld().getName().equalsIgnoreCase(other.getWorld().getName()))) {
return false;
}
return Math.floor(getX()) == Math.floor(other.getX())
&& Math.floor(getY()) == Math.floor(other.getY())
&& Math.floor(getZ()) == Math.floor(other.getZ());
Expand Down Expand Up @@ -699,7 +706,7 @@ else if (yaw < 315)
// ENTITY AND BLOCK LIST ATTRIBUTES
/////////////////

if (attribute.startsWith("find") || attribute.startsWith("nearest")) {
if (attribute.matches("find") || attribute.startsWith("nearest")) {
attribute.fulfill(1);

// <--[tag]
Expand Down Expand Up @@ -949,8 +956,45 @@ public int compare(dEntity ent1, dEntity ent2) {

return new dList(found).getAttribute(attribute);
}
}

return new Element("null").getAttribute(attribute);
// <--[tag]
// @attribute <l@location.find_path[<location>]>
// @returns dList(dLocation)
// @description
// Returns a full list of points along the path from this location to the given location.
// The default radius, if unspecified, is 2.
// -->
if (attribute.startsWith("find_path")
&& attribute.hasContext(1)) {
dLocation two = dLocation.valueOf(attribute.getContext(1));
if (two == null) {
return null;
}
attribute = attribute.fulfill(1);
int radius = 2;
// <--[tag]
// @attribute <l@location.find_path[<location>].radius[<#>]>
// @returns dList(dLocation)
// @description
// Returns a full list of points along the path from this location to the given location.
// The default radius, if unspecified, is 2.
// -->
if (attribute.startsWith("radius")
&& attribute.hasContext(1)) {
radius = new Element(attribute.getContext(1)).asInt();
attribute = attribute.fulfill(1);
}
PathFinder.Node node = PathFinder.findPath(this, two, radius);
if (node == null) {
return null;
}
dList list = new dList();
while (node != null) {
list.add(node.position.identify());
node = node.next;
}
return list.getAttribute(attribute);
}


Expand Down
111 changes: 111 additions & 0 deletions src/main/java/net/aufdemrand/denizen/utilities/PathFinder.java
@@ -0,0 +1,111 @@
package net.aufdemrand.denizen.utilities;

import net.aufdemrand.denizen.Settings;
import net.aufdemrand.denizen.objects.dLocation;

import java.util.HashSet;

public class PathFinder {

public static class Node {
public dLocation position;
public double cost;
public double pathCost;
public Node next;
public Node nextListElement;
}

static class MinHeap {
public Node head;
public boolean hasNext() {
return head != null;
}
public void add(Node node) {
if (head == null) {
head = node;
}
else if (head.next == null && node.cost < head.cost) {
node.nextListElement = head;
head = node;
}
else {
Node cur = head;
while (cur.nextListElement != null && cur.nextListElement.cost < node.cost)
{
cur = cur.nextListElement;
}
node.nextListElement = cur.nextListElement;
cur.nextListElement = node;
}
}
public Node extractFirst()
{
Node res = head;
head = head.nextListElement;
return res;
}
}
public static dLocation[] surroundings = new dLocation[] {
new dLocation(null, 1, 0, 0),
new dLocation(null, -1, 0, 0),
new dLocation(null, 0, 0, 1),
new dLocation(null, 0, 0, -1),
new dLocation(null, 0, 1, 1),
new dLocation(null, 0, 1, -1),
new dLocation(null, 1, 1, 0),
new dLocation(null, -1, 1, 0),
new dLocation(null, 0, -1, 1),
new dLocation(null, 0, -1, -1),
new dLocation(null, 1, -1, 0),
new dLocation(null, -1, -1, 0)
};

public static Node findPath(dLocation start, dLocation end, int radius) {
int maxRadius = Settings.pathfindingMaxDistance();
Node snode = new Node();
snode.position = start;
MinHeap heaplist = new MinHeap();
heaplist.add(snode);
HashSet<dLocation> tried = new HashSet<dLocation>(100);
tried.add(start);
while (heaplist.hasNext()) {
Node curr = heaplist.extractFirst();
if (curr.position.distanceSquared(end) <= radius * radius) {
Node n = new Node();
n.position = start;
n.cost = curr.pathCost + 1;
n.pathCost = curr.cost + 1;
n.next = curr;
return n;
}
for (int i = 0; i < surroundings.length; i++) {
dLocation surr = surroundings[i];
dLocation point = new dLocation(start.getWorld(), curr.position.getX() + surr.getX(), curr.position.getY() + surr.getY(),
curr.position.getZ() + surr.getZ());
if (pointIsFree(point, maxRadius, start) && !tried.contains(point)) {
tried.add(point);
Node node = new Node();
node.position = point;
double surrCost = lengthSquared(surr);
node.cost = curr.pathCost + surrCost + point.distanceSquared(end);
node.pathCost = curr.pathCost + surrCost;
node.next = curr;
heaplist.add(node);
}
}
}
return null; // Give up
}

public static double lengthSquared(dLocation loc) {
return loc.getX() * loc.getX() + loc.getY() * loc.getY() + loc.getZ() * loc.getZ();
}

public static boolean pointIsFree(dLocation point, int maxRadius, dLocation start) {
return point.getWorld() != null
&& !point.getBlock().getType().isSolid()
&& !point.clone().add(0, 1, 0).getBlock().getType().isSolid()
&& point.clone().add(0, -1, 0).getBlock().getType().isSolid()
&& point.distanceSquared(start) < maxRadius * maxRadius;
}
}
3 changes: 3 additions & 0 deletions src/main/resources/config.yml
Expand Up @@ -102,6 +102,9 @@ Tags:
Block tags:
# How many blocks can be read, max, before stopping the tag in place
Max blocks: 1000000
Path finding:
# How far away the path finder can search before giving up
Max distance: 100


# The version of this configuration file, used to check if your
Expand Down

0 comments on commit fe10161

Please sign in to comment.