Separated node class from DFS class.

This commit is contained in:
Harrison Deng 2020-04-23 23:07:49 -05:00
parent de3b3a614a
commit be19db1f9b
3 changed files with 120 additions and 92 deletions

View File

@ -0,0 +1,72 @@
package ca.recrown.islandsurvivalcraft.pathfinding;
import java.util.Objects;
public class DFSNode implements Comparable<DFSNode> {
private DFSNode goal;
public DFSNode[] child;
private DFSNode parent;
private int x;
private int y;
public DFSNode(DFSNode parent, int x, int y, DFSNode goal) {
child = new DFSNode[4];
this.x = x;
this.y = y;
this.parent = parent;
this.goal = goal;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public DFSNode getGoal() {
return goal;
}
@Override
public int compareTo(DFSNode o) {
if (goal == null) return 0;
return Math.round(distanceToGoal(goal) - o.distanceToGoal(goal));
}
public DFSNode getParent() {
return parent;
}
public float distanceToGoal(DFSNode goal) {
float distanceX = goal.x - this.x;
float distanceY = goal.y - this.y;
float distance = (float) Math.sqrt(distanceX * distanceX + distanceY * distanceY);
return distance;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DFSNode) {
DFSNode comp = (DFSNode) obj;
if (comp.x == this.x && comp.y == this.y) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
public void copyTo(DFSNode node) {
node.child = child;
node.parent = parent;
node.x = x;
node.y = y;
node.goal = goal;
}
}

View File

@ -1,21 +1,19 @@
package ca.recrown.islandsurvivalcraft.pathfinding; package ca.recrown.islandsurvivalcraft.pathfinding;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.PriorityQueue; import java.util.PriorityQueue;
import java.util.Queue; import java.util.Queue;
import org.apache.commons.lang.NullArgumentException; import org.apache.commons.lang.NullArgumentException;
public class DepthFirstSearch { public class DepthFirstSearch {
private Queue<Node> queue; private Queue<DFSNode> queue;
private CoordinateValidatable coordValidatable; private CoordinateValidatable coordValidatable;
private Node startNode; private DFSNode startNode;
private Node endNode; private DFSNode endNode;
private HashSet<Node> checkedNodes; private HashSet<DFSNode> checkedNodes;
private int maxNodesSearched = -1; private int maxNodesSearched = -1;
private int foundX; private DFSNode foundNode;
private int foundY;
public DepthFirstSearch() { public DepthFirstSearch() {
checkedNodes = new HashSet<>(); checkedNodes = new HashSet<>();
@ -42,19 +40,15 @@ public class DepthFirstSearch {
} }
public void setStartPosition(int x, int y) { public void setStartPosition(int x, int y) {
startNode = new Node(null, x, y); startNode = new DFSNode(null, x, y, endNode);
} }
public void setDirectionPosition(int x, int y) { public void setDirectionPosition(int x, int y) {
endNode = new Node(null, x, y); endNode = new DFSNode(null, x, y, endNode);
} }
public int getEndX() { public DFSNode getFoundNode() {
return foundX; return foundNode;
}
public int getEndY() {
return foundY;
} }
public void deleteTree() { public void deleteTree() {
@ -63,50 +57,55 @@ public class DepthFirstSearch {
} }
public boolean buildPathToEndNode() { public boolean buildPathToEndNode() {
if (coordValidatable == null) throw new IllegalStateException("Need to set a validator."); if (coordValidatable == null)
if (startNode == null) throw new IllegalStateException("Need to set the starting position."); throw new IllegalStateException("Need to set a validator.");
if (startNode == null)
throw new IllegalStateException("Need to set the starting position.");
return findTarget(new CoordinateTargetValidatable() { return findTarget(new CoordinateTargetValidatable() {
@Override @Override
public boolean isCoordinateTarget(int x, int y) { public boolean isCoordinateTarget(int x, int y) {
return endNode.x == x && endNode.y == y; return endNode.getX() == x && endNode.getY() == y;
} }
}); });
} }
/** /**
* Finds the end node. * Finds the end node. If an end node is set, it will try going in that
* If an end node is set, it will try going in that direction first. * direction first. Will not set end node if one wasn't found, therefore,
* Will not set end node if one wasn't found, * previous end node data is kept.
* therefore, previous end node data is kept. *
* @param targetValidator * @param targetValidator
* @return * @return
*/ */
public boolean findTarget(CoordinateTargetValidatable targetValidator) { public boolean findTarget(CoordinateTargetValidatable targetValidator) {
if (coordValidatable == null) throw new IllegalStateException("Need to set a validator."); if (coordValidatable == null)
if (targetValidator == null) throw new NullArgumentException("targetValidator"); throw new IllegalStateException("Need to set a validator.");
if (startNode == null) throw new IllegalStateException("Need to set the starting position."); if (targetValidator == null)
throw new NullArgumentException("targetValidator");
if (startNode == null)
throw new IllegalStateException("Need to set the starting position.");
queue.clear(); queue.clear();
checkedNodes.clear(); checkedNodes.clear();
Node begin = startNode; DFSNode begin = startNode;
queue.add(begin); queue.add(begin);
while (!queue.isEmpty()) { while (!queue.isEmpty()) {
Node n = queue.poll(); DFSNode n = queue.poll();
if (maxNodesSearched != -1 && checkedNodes.size() > maxNodesSearched) return false; if (maxNodesSearched != -1 && checkedNodes.size() > maxNodesSearched)
if (checkedNodes.add(n) && coordValidatable.validate(n.x, n.y)) { return false;
n.child[0] = new Node(n, n.x + 1, n.y); if (checkedNodes.add(n) && coordValidatable.validate(n.getX(), n.getY())) {
n.child[1] = new Node(n, n.x - 1, n.y); n.child[0] = new DFSNode(n, n.getX() + 1, n.getY(), endNode);
n.child[2] = new Node(n, n.x, n.y + 1); n.child[1] = new DFSNode(n, n.getX() - 1, n.getY(), endNode);
n.child[3] = new Node(n, n.x, n.y - 1); n.child[2] = new DFSNode(n, n.getX(), n.getY() + 1, endNode);
if (targetValidator.isCoordinateTarget(n.x, n.y)) { n.child[3] = new DFSNode(n, n.getX(), n.getY() - 1, endNode);
foundX = n.x; if (targetValidator.isCoordinateTarget(n.getX(), n.getY())) {
foundY = n.y; foundNode = new DFSNode(n.getParent(), n.getX(), n.getY(), null);
return true; return true;
} }
for (int i = 0; i < n.child.length; i++) { for (int i = 0; i < n.child.length; i++) {
Node child = n.child[i]; DFSNode child = n.child[i];
queue.add(child); queue.add(child);
} }
} }
@ -117,47 +116,4 @@ public class DepthFirstSearch {
public int getSearchedCount() { public int getSearchedCount() {
return checkedNodes.size(); return checkedNodes.size();
} }
private class Node implements Comparable<Node> {
public Node[] child;
public Node parent;
public int x;
public int y;
public Node(Node parent, int x, int y) {
this.parent = parent;
child = new Node[4];
this.x = x;
this.y = y;
}
@Override
public int compareTo(Node o) {
if (endNode == null) return 0;
return Math.round(distanceToGoal(endNode) - o.distanceToGoal(endNode));
}
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;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Node) {
Node comp = (Node) obj;
if (comp.x == this.x && comp.y == this.y) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
} }

View File

@ -146,8 +146,8 @@ public class DepthFirstSearchTest {
dfs.setStartPosition(3, 0); dfs.setStartPosition(3, 0);
assertTrue(dfs.findTarget(validator)); assertTrue(dfs.findTarget(validator));
assertEquals(1, dfs.getEndX()); assertEquals(1, dfs.getFoundNode().getX());
assertEquals(2, dfs.getEndY()); assertEquals(2, dfs.getFoundNode().getY());
} }
@Test @Test
@ -158,8 +158,8 @@ public class DepthFirstSearchTest {
dfs.setStartPosition(0, 0); dfs.setStartPosition(0, 0);
assertTrue(dfs.findTarget(validator)); assertTrue(dfs.findTarget(validator));
assertEquals(0, dfs.getEndX()); assertEquals(0, dfs.getFoundNode().getX());
assertEquals(3, dfs.getEndY()); assertEquals(3, dfs.getFoundNode().getY());
} }
@Test @Test
@ -169,8 +169,8 @@ public class DepthFirstSearchTest {
dfs.setStartPosition(3, 0); dfs.setStartPosition(3, 0);
assertTrue(dfs.findTarget(validator)); assertTrue(dfs.findTarget(validator));
assertEquals(95, dfs.getEndX()); assertEquals(95, dfs.getFoundNode().getX());
assertEquals(49, dfs.getEndY()); assertEquals(49, dfs.getFoundNode().getY());
} }
@Test @Test
@ -199,8 +199,8 @@ public class DepthFirstSearchTest {
dfs.setStartPosition(0, 0); dfs.setStartPosition(0, 0);
assertTrue(dfs.findTarget(validator)); assertTrue(dfs.findTarget(validator));
assertEquals(26, dfs.getEndX()); assertEquals(26, dfs.getFoundNode().getX());
assertEquals(32, dfs.getEndY()); assertEquals(32, dfs.getFoundNode().getY());
} }
@Test @Test
@ -220,7 +220,7 @@ public class DepthFirstSearchTest {
dfs.setDirectionPosition(42, 84); dfs.setDirectionPosition(42, 84);
assertTrue(dfs.findTarget(validator)); assertTrue(dfs.findTarget(validator));
assertEquals(95, dfs.getEndX()); assertEquals(95, dfs.getFoundNode().getX());
assertEquals(49, dfs.getEndY()); assertEquals(49, dfs.getFoundNode().getY());
} }
} }