Added a target coordinate finder.

Implemented DFS name changes.
This commit is contained in:
Harrison Deng 2020-04-20 23:24:12 -05:00
parent cf6a371428
commit f5e3435d8b
5 changed files with 231 additions and 93 deletions

View File

@ -0,0 +1,11 @@
package ca.recrown.islandsurvivalcraft.pathfinding;
public interface CoordinateTargetValidatable {
/**
* Is the current coordinate the objective?
* @param x the x of the coordinate.
* @param y the y of the coordinate.
* @return true if this is the objective coordinate.
*/
public boolean isCoordinateTarget(int x, int y);
}

View File

@ -1,35 +1,44 @@
package ca.recrown.islandsurvivalcraft.pathfinding;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
public class DepthFirstSearch {
private PriorityQueue<Node> queue;
private Queue<Node> queue;
private CoordinateValidatable validatable;
private Node startNode;
private Node endNode;
private ArrayList<Node> checkedNodes;
public DepthFirstSearch(CoordinateValidatable validatable) {
queue = new PriorityQueue<>();
this.validatable = validatable;
public DepthFirstSearch() {
checkedNodes = new ArrayList<>();
}
public void Setup(int xGoal, int yGoal, int x, int y) {
startNode = new Node(null, xGoal, yGoal, x, y);
public DepthFirstSearch(CoordinateValidatable validatable) {
this();
this.validatable = validatable;
}
public Node getTree() {
return startNode;
public void setValidatable(CoordinateValidatable validatable) {
this.validatable = validatable;
}
public void setStartPosition(int x, int y) {
startNode = new Node(null, x, y);
}
public Node getEndNode() {
return endNode;
public void setEndPosition(int x, int y) {
endNode = new Node(null, x, y);
}
public Node getParentOfNode(Node node) {
return node.parent;
public int getEndX() {
return endNode.x;
}
public int getEndY() {
return endNode.y;
}
public void deleteTree() {
@ -37,14 +46,49 @@ public class DepthFirstSearch {
endNode = null;
}
public boolean buildTree() {
public boolean buildPathToEndNode() {
queue = new PriorityQueue<>();
checkedNodes.clear();
queue.clear();
Node begin = startNode;
queue.add(begin);
while (!queue.isEmpty()) {
Node n = queue.poll();
if (!(n.x == n.xGoal && n.y == n.yGoal)) {
if (!n.equals(endNode)) {
n.child[0] = new Node(n, n.x + 1, n.y);
n.child[1] = new Node(n, n.x - 1, n.y);
n.child[2] = new Node(n, n.x, n.y + 1);
n.child[3] = new Node(n, n.x, n.y - 1);
} else {
n.shallowCopyTo(endNode);
return true;
}
if (!n.check()) {
for (int i = 0; i < n.child.length; i++) {
Node child = n.child[i];
if (validatable.validate(child.x, child.y)) {
queue.add(child);
}
}
}
}
return false;
}
/**
* Finds the end node.
* Will not set end node if one wasn't found,
* therefore, previous end node data is kept.
* @param targetValidator
* @return
*/
public boolean findEndNode(CoordinateTargetValidatable targetValidator) {
queue = new LinkedList<>();
checkedNodes.clear();
Node begin = startNode;
queue.add(begin);
while (!queue.isEmpty()) {
Node n = queue.poll();
if (!targetValidator.isCoordinateTarget(n.x, n.y)) {
n.child[0] = new Node(n, n.x + 1, n.y);
n.child[1] = new Node(n, n.x - 1, n.y);
n.child[2] = new Node(n, n.x, n.y + 1);
@ -55,8 +99,9 @@ public class DepthFirstSearch {
}
if (!n.check()) {
for (int i = 0; i < n.child.length; i++) {
if (validatable.validate(n.child[i].x, n.child[i].y)) {
queue.add(n.child[i]);
Node child = n.child[i];
if (validatable.validate(child.x, child.y)) {
queue.add(child);
}
}
}
@ -66,49 +111,35 @@ public class DepthFirstSearch {
private class Node implements Comparable<Node> {
public Node[] child;
public final Node parent;
public Node parent;
public int x;
public int y;
private boolean checked = false;
private final int xGoal;
private final int yGoal;
public Node(Node parent, int xGoal, int yGoal, int x, int y) {
public Node(Node parent, int x, int y) {
this.parent = parent;
child = new Node[4];
this.xGoal = xGoal;
this.yGoal = yGoal;
this.x = x;
this.y = y;
}
public Node(Node parent, int x, int y) {
this(parent, parent.xGoal, parent.yGoal, x, y);
}
@Override
public int compareTo(Node o) {
return Math.round(distanceToGoal() - o.distanceToGoal());
return Math.round(distanceToGoal(endNode) - o.distanceToGoal(endNode));
}
public float distanceToGoal() {
float distanceX = xGoal - this.x;
float distanceY = yGoal - this.y;
public float distanceToGoal(Node goal) {
float distanceX = goal.x - this.x;
float distanceY = goal.y - this.y;
float distance = (float) Math.sqrt(distanceX * distanceX + distanceY * distanceY);
return distance;
}
public boolean check() {
if (!checked) {
checked = true;
if (checkedNodes.contains(this)) {
return true;
}
checkedNodes.add(this);
return false;
if (checkedNodes.contains(this)) {
return true;
}
return true;
checkedNodes.add(this);
return false;
}
@Override
@ -121,5 +152,12 @@ public class DepthFirstSearch {
}
return false;
}
public void shallowCopyTo(Node node) {
node.child = this.child;
node.parent = this.parent;
node.x = this.x;
node.y = this.y;
}
}
}

View File

@ -1,5 +1,6 @@
package ca.recrown.islandsurvivalcraft.worldgeneration;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
@ -9,12 +10,13 @@ import ca.recrown.islandsurvivalcraft.islandbedrockmetadata.IslandOwnerMetadata;
import ca.recrown.islandsurvivalcraft.islandbedrockmetadata.IslandShoreBiomeMetadata;
import ca.recrown.islandsurvivalcraft.islandbedrockmetadata.IslandTUIDMetadata;
import ca.recrown.islandsurvivalcraft.islandbedrockmetadata.IslandTemperatureMetadata;
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable;
import ca.recrown.islandsurvivalcraft.IslandSurvivalCraft;
import ca.recrown.islandsurvivalcraft.islandbedrockmetadata.IslandBedrockMetadataHelper;
import ca.recrown.islandsurvivalcraft.islandbedrockmetadata.IslandMainBiomeMetadata;
import ca.recrown.islandsurvivalcraft.islandbedrockmetadata.IslandMetadataPack;
public class BiomePerIslandGenerator implements IslandBiomeGenerator {
public class BiomePerIslandGenerator implements IslandBiomeGenerator, CoordinateTargetValidatable {
private IslandMapGenerator mapGenerator;
private Biome currentIslandBiome;
private float currentIslandTemperature;
@ -42,7 +44,7 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
int worldZ = chunkZ * 16 + localZ;
if (mapGenerator.isIsland(worldX, worldZ)) {
IslandMetadataPack sameIslandPack = getSurroundingIslandData(world, worldX, worldZ);
IslandMetadataPack sameIslandPack = getSurroundingIslandData(worldX, worldZ);
if (sameIslandPack != null) {
//Old island
currentIslandBiome = sameIslandPack.mainBiomeMetadata.getMainBiome();
@ -50,6 +52,8 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
currentIslandTemperature = sameIslandPack.temperatureMetadata.getTemperature();
currentTUID = sameIslandPack.islandTIUDMetadata.getIUID();
} else {
//New island
currentIslandTemperature = temperatureMapGenerator.getTemperature(worldX, worldZ);
currentIslandBiome = biomeSelector.getLandBiome(currentIslandTemperature);
@ -67,16 +71,46 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
current.setTUID(currentTUID, plugin);
metadataHelper.setIslandBedrockMetadataPack(worldX, worldZ, current);
//Set the biome information for the chunk
if (mapGenerator.isShore(worldX, worldZ)) {
setBiome(localX, localZ, biome, currentIslandShoreBiome);
} else {
setBiome(localX, localZ, biome, currentIslandBiome);
}
//Check if island besides this one need to be generated.
if (localX == 0 && mapGenerator.isSameIsland(worldX, worldZ, worldX - 1, worldZ)) {
if (!world.isChunkGenerated(chunkX - 1, chunkZ)) {
Chunk chunk = world.getChunkAt(chunkX - 1, chunkZ);
chunk.load(true);
chunk.unload(true);
}
} else if (localX == 15 && mapGenerator.isSameIsland(worldX, worldZ, worldX + 1, worldZ)) {
if (!world.isChunkGenerated(chunkX + 1, chunkZ)) {
Chunk chunk = world.getChunkAt(chunkX + 1, chunkZ);
chunk.load(true);
chunk.unload(true);
}
}
if (localZ == 0 && mapGenerator.isSameIsland(worldX, worldZ, worldX, worldZ - 1)) {
if (!world.isChunkGenerated(chunkX, chunkZ - 1)) {
Chunk chunk = world.getChunkAt(chunkX, chunkZ - 1);
chunk.load(true);
chunk.unload(true);
}
} else if (localZ == 15 && mapGenerator.isSameIsland(worldX, worldZ, worldX, worldZ + 1)) {
if (!world.isChunkGenerated(chunkX, chunkZ + 1)) {
Chunk chunk = world.getChunkAt(chunkX, chunkZ + 1);
chunk.load(true);
chunk.unload(true);
}
}
}
}
private IslandMetadataPack getSurroundingIslandData(World world, int worldX, int worldZ) {
private IslandMetadataPack getSurroundingIslandData(int worldX, int worldZ) {
IslandMetadataPack data = null;
if (mapGenerator.isSameIsland(worldX, worldZ, worldX + 1, worldZ)) {
data = getMetadataPackAt(worldX + 1, worldZ, world);
@ -114,4 +148,10 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
biomeGrid.setBiome(localX, y, localZ, biome);
}
}
@Override
public boolean isCoordinateTarget(int x, int y) {
return false;
}
}

View File

@ -140,8 +140,9 @@ public class IslandMapGenerator implements CoordinateValidatable {
public boolean isSameIsland(int firstBlockX, int firstBlockZ, int secondBlockX, int secondBlockZ) {
if (!isIsland(firstBlockX, firstBlockZ)) return false;
if (!isIsland(secondBlockX, secondBlockZ)) return false;
dfs.Setup(secondBlockX, secondBlockZ, firstBlockX, firstBlockZ);
boolean res = dfs.buildTree();
dfs.setStartPosition(firstBlockX, firstBlockZ);
dfs.setEndPosition(secondBlockX, secondBlockZ);
boolean res = dfs.buildPathToEndNode();
dfs.deleteTree();
return res;
}

View File

@ -1,5 +1,6 @@
package ca.recrown.islandsurvivalcraft.pathfinding;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -15,96 +16,143 @@ import org.junit.jupiter.api.TestInstance.Lifecycle;
@TestInstance(Lifecycle.PER_CLASS)
public class DepthFirstSearchTest {
private class Validator implements CoordinateValidatable {
private boolean[][] map;
private class Validator implements CoordinateValidatable, CoordinateTargetValidatable {
private byte[][] map;
public Validator(boolean[][] map) {
public Validator(byte[][] map) {
this.map = map;
}
@Override
public boolean validate(int x, int y) {
try {
return map[y][x];
return map[y][x] >= 1;
} catch (IndexOutOfBoundsException e) {
return false;
}
}
@Override
public boolean isCoordinateTarget(int x, int y) {
try {
return map[y][x] == 2;
} catch (IndexOutOfBoundsException e) {
return false;
}
}
}
private DepthFirstSearch dfs;
private boolean[][] mapA = new boolean[][] {
{true, true},
{true, false},
{true, true}
private byte[][] mapA = new byte[][] {
{1, 1},
{1, 0},
{1, 1}
};
private boolean[][] mapB = new boolean[][] {
{true, true, true, true},
{true, false, false, false},
{true, true, true, true}
private byte[][] mapB = new byte[][] {
{1, 1, 1, 1},
{1, 0, 0, 0},
{1, 2, 1, 1}
};
private boolean[][] mapC = new boolean[][] {
{true, true, true, true},
{false, false, false, false},
{true, true, true, true}
private byte[][] mapC = new byte[][] {
{1, 1, 1, 1},
{0, 0, 0, 0},
{1, 1, 1, 1}
};
private boolean[][] mapD = new boolean[][] {
{true, true, true, true},
{false, false, false, true},
{true, true, true, true},
{true, false, false, false},
private byte[][] mapD = new byte[][] {
{1, 1, 1, 1},
{0, 0, 0, 1},
{1, 1, 1, 1},
{2, 0, 0, 0},
};
private boolean[][] mapE = new boolean[][] {
{true, true, true, true},
{false, false, false, false},
{true, true, true, true},
{true, false, false, false},
private byte[][] mapE = new byte[][] {
{1, 1, 1, 1},
{0, 0, 0, 0},
{1, 1, 1, 1},
{1, 0, 2, 0},
};
@BeforeAll
protected void setUp() {
dfs = new DepthFirstSearch();
}
@Test
public void testDFSMapAValid()
public void testDFSBuildPathToEndNodeMapAValid()
{
DepthFirstSearch dfs = new DepthFirstSearch(new Validator(mapA));
dfs.Setup(1, 0, 1, 2);
assertTrue(dfs.buildTree());
dfs.setValidatable(new Validator(mapA));
dfs.setStartPosition(1, 2);
dfs.setEndPosition(1, 0);
assertTrue(dfs.buildPathToEndNode());
}
@Test
public void testDFSMapBValid()
public void testDFSBuildPathToEndNodeMapBValid()
{
DepthFirstSearch dfs = new DepthFirstSearch(new Validator(mapB));
dfs.Setup(3, 2, 3, 0);
assertTrue(dfs.buildTree());
dfs.setValidatable(new Validator(mapB));
dfs.setStartPosition(3, 0);
dfs.setEndPosition(3, 2);
assertTrue(dfs.buildPathToEndNode());
}
@Test
public void testDFSMapDValid()
public void testDFSBuildPathToEndNodeMapDValid()
{
DepthFirstSearch dfs = new DepthFirstSearch(new Validator(mapD));
dfs.Setup(0, 3, 0, 0);
assertTrue(dfs.buildTree());
dfs.setValidatable(new Validator(mapD));
dfs.setStartPosition(0, 0);
dfs.setEndPosition(0, 3);
assertTrue(dfs.buildPathToEndNode());
}
@Test
public void testDFSMapCInvalid()
public void testDFSBuildPathToEndNodeMapCInvalid()
{
DepthFirstSearch dfs = new DepthFirstSearch(new Validator(mapC));
dfs.Setup(3, 2, 3, 0);
assertFalse(dfs.buildTree());
dfs.setValidatable(new Validator(mapC));
dfs.setStartPosition(3, 0);
dfs.setEndPosition(3, 2);
assertFalse(dfs.buildPathToEndNode());
}
@Test
public void testDFSMapEInvalid()
public void testDFSBuildPathToEndNodeMapEInvalid()
{
DepthFirstSearch dfs = new DepthFirstSearch(new Validator(mapE));
dfs.Setup(3, 2, 3, 0);
assertFalse(dfs.buildTree());
dfs.setValidatable(new Validator(mapE));
dfs.setStartPosition(3, 0);
dfs.setEndPosition(3, 2);
assertFalse(dfs.buildPathToEndNode());
}
@Test
public void testDFSFindEndNodeMapBValid() {
Validator validator = new Validator(mapB);
dfs.setValidatable(validator);
dfs.setStartPosition(3, 0);
assertTrue(dfs.findEndNode(validator));
assertEquals(1, dfs.getEndX());
assertEquals(2, dfs.getEndY());
}
@Test
public void testDFSFindEndNodeMapDValid() {
Validator validator = new Validator(mapD);
dfs.setValidatable(validator);
dfs.setStartPosition(0, 0);
assertTrue(dfs.findEndNode(validator));
assertEquals(0, dfs.getEndX());
assertEquals(3, dfs.getEndY());
}
@Test
public void testDFSFindEndNodeMapEInvalid() {
Validator validator = new Validator(mapE);
dfs.setValidatable(validator);
dfs.setStartPosition(0, 0);
assertFalse(dfs.findEndNode(validator));
}
}