Skip to content

Commit 90dd4ce

Browse files
committed
Added strategy pattern to check the map
1 parent 8e9d267 commit 90dd4ce

File tree

8 files changed

+163
-90
lines changed

8 files changed

+163
-90
lines changed

src/com/ai/astar/AStar.java

Lines changed: 37 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,38 @@
11
package com.ai.astar;
22

3+
import com.ai.astar.domain.Node;
4+
import com.ai.astar.domain.searchstrategy.DiagonalMapChecker;
5+
import com.ai.astar.domain.searchstrategy.HorizontalVerticalChecker;
6+
import com.ai.astar.domain.searchstrategy.MapChecker;
7+
import com.ai.astar.domain.searchstrategy.NoOpChecker;
8+
39
import java.util.*;
410

5-
/**
6-
* A Star Algorithm
7-
*
8-
* @author Marcelo Surriabre
9-
* @version 2.1, 2017-02-23
10-
*/
1111
public class AStar {
1212
private static final int DEFAULT_HV_COST = 10; // Horizontal - Vertical Cost
1313
private static final int DEFAULT_DIAGONAL_COST = 14;
14-
private final int hvCost;
15-
private final int diagonalCost;
1614
private final Node[][] searchArea;
1715
private final PriorityQueue<Node> openList;
1816
private final Set<Node> closedSet;
1917
private final Node initialNode;
2018
private final Node finalNode;
19+
private final MapChecker diagonalsChecker;
20+
private final MapChecker hvChecker;
2121

22-
public AStar(int rows, int cols, Node initialNode, Node finalNode, int hvCost, int diagonalCost) {
23-
this.hvCost = hvCost;
24-
this.diagonalCost = diagonalCost;
22+
public AStar(int rows, int cols, Node initialNode, Node finalNode, int[][] blocksArray, boolean searchDiagonals) {
2523
this.initialNode = initialNode;
2624
this.finalNode = finalNode;
2725
this.searchArea = new Node[rows][cols];
2826
this.openList = new PriorityQueue<>(Comparator.comparingInt(Node::f));
2927
initNodes();
28+
initBlocks(blocksArray);
3029
this.closedSet = new HashSet<>();
31-
}
32-
33-
public AStar(int rows, int cols, Node initialNode, Node finalNode) {
34-
this(rows, cols, initialNode, finalNode, DEFAULT_HV_COST, DEFAULT_DIAGONAL_COST);
30+
if (searchDiagonals) {
31+
this.diagonalsChecker = new DiagonalMapChecker(searchArea, openList, closedSet, DEFAULT_DIAGONAL_COST);
32+
} else {
33+
this.diagonalsChecker = new NoOpChecker(null, null, null);
34+
}
35+
this.hvChecker = new HorizontalVerticalChecker(searchArea, openList, closedSet, DEFAULT_HV_COST);
3536
}
3637

3738
private void initNodes() {
@@ -44,10 +45,16 @@ private void initNodes() {
4445
}
4546
}
4647

47-
public void initBlocks(int[][] blocksArray) {
48+
private void initBlocks(int[][] blocksArray) {
4849
for (int[] ints : blocksArray) {
4950
int row = ints[0];
5051
int col = ints[1];
52+
if (row < 0 || row >= searchArea.length) {
53+
continue;
54+
}
55+
if (col < 0 || col >= searchArea[0].length) {
56+
continue;
57+
}
5158
this.searchArea[row][col].setAsBlocked();
5259
}
5360
}
@@ -58,15 +65,15 @@ public List<Node> findPath() {
5865
Node currentNode = openList.poll();
5966
closedSet.add(currentNode);
6067
if (isFinalNode(currentNode)) {
61-
return generatePath(currentNode);
68+
return bestPath(currentNode);
6269
} else {
6370
addAdjacentNodes(currentNode);
6471
}
6572
}
6673
return new ArrayList<>();
6774
}
6875

69-
private List<Node> generatePath(Node currentNode) {
76+
private List<Node> bestPath(Node currentNode) {
7077
List<Node> path = new ArrayList<>();
7178
path.add(currentNode);
7279
Node parent;
@@ -78,71 +85,26 @@ private List<Node> generatePath(Node currentNode) {
7885
}
7986

8087
private void addAdjacentNodes(Node currentNode) {
81-
addAdjacentUpperRow(currentNode);
82-
addAdjacentMiddleRow(currentNode);
83-
addAdjacentLowerRow(currentNode);
84-
}
85-
86-
private void addAdjacentLowerRow(Node currentNode) {
8788
int row = currentNode.row();
8889
int col = currentNode.col();
89-
int lowerRow = row + 1;
90-
if (lowerRow >= searchArea.length) {
91-
return;
92-
}
93-
if (col - 1 >= 0) {
94-
checkNode(currentNode, col - 1, lowerRow, diagonalCost); // Comment this line if diagonal movements are not allowed
95-
}
96-
if (col + 1 < searchArea[0].length) {
97-
checkNode(currentNode, col + 1, lowerRow, diagonalCost); // Comment this line if diagonal movements are not allowed
98-
}
99-
checkNode(currentNode, col, lowerRow, hvCost);
90+
addAdjacentUpperRow(currentNode, row, col);
91+
addAdjacentMiddleRow(currentNode, row, col);
92+
addAdjacentLowerRow(currentNode, row, col);
10093
}
10194

102-
private void addAdjacentMiddleRow(Node currentNode) {
103-
int row = currentNode.row();
104-
int col = currentNode.col();
105-
if (col - 1 >= 0) {
106-
checkNode(currentNode, col - 1, row, hvCost);
107-
}
108-
if (col + 1 < searchArea[0].length) {
109-
checkNode(currentNode, col + 1, row, hvCost);
110-
}
95+
private void addAdjacentLowerRow(Node currentNode, int row, int col) {
96+
diagonalsChecker.checkNode(currentNode, col, row + 1);
97+
hvChecker.checkNode(currentNode, col, row + 1);
11198
}
11299

113-
private void addAdjacentUpperRow(Node currentNode) {
114-
int row = currentNode.row();
115-
int col = currentNode.col();
116-
int upperRow = row - 1;
117-
if (upperRow < 0) {
118-
return;
119-
}
120-
if (col - 1 >= 0) {
121-
checkNode(currentNode, col - 1, upperRow, diagonalCost); // Comment this if diagonal movements are not allowed
122-
}
123-
if (col + 1 < searchArea[0].length) {
124-
checkNode(currentNode, col + 1, upperRow, diagonalCost); // Comment this if diagonal movements are not allowed
125-
}
126-
checkNode(currentNode, col, upperRow, hvCost);
100+
private void addAdjacentMiddleRow(Node currentNode, int row, int col) {
101+
hvChecker.checkNode(currentNode, col - 1, row);
102+
hvChecker.checkNode(currentNode, col + 1, row);
127103
}
128104

129-
private void checkNode(Node currentNode, int col, int row, int cost) {
130-
Node adjacentNode = searchArea[row][col];
131-
if (adjacentNode.isBlocked() || closedSet.contains(adjacentNode)) {
132-
return;
133-
}
134-
if (!openList.contains(adjacentNode)) {
135-
adjacentNode.setNodeData(currentNode, cost);
136-
openList.add(adjacentNode);
137-
} else {
138-
boolean changed = adjacentNode.checkBetterPath(currentNode, cost);
139-
if (changed) {
140-
// Remove and Add the changed node, so that the PriorityQueue can sort again its
141-
// contents with the modified "finalCost" value of the modified node
142-
openList.remove(adjacentNode);
143-
openList.add(adjacentNode);
144-
}
145-
}
105+
private void addAdjacentUpperRow(Node currentNode, int row, int col) {
106+
diagonalsChecker.checkNode(currentNode, col, row - 1);
107+
hvChecker.checkNode(currentNode, col, row - 1);
146108
}
147109

148110
private boolean isFinalNode(Node currentNode) {

src/com/ai/astar/AStarTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.ai.astar;
22

3+
import com.ai.astar.domain.Node;
4+
35
import java.util.List;
46

57
public class AStarTest {
@@ -9,9 +11,8 @@ public static void main(String[] args) {
911
Node finalNode = new Node(2, 5);
1012
int rows = 6;
1113
int cols = 7;
12-
AStar aStar = new AStar(rows, cols, initialNode, finalNode);
1314
int[][] blocksArray = new int[][]{{1, 3}, {2, 3}, {3, 3}};
14-
aStar.initBlocks(blocksArray);
15+
AStar aStar = new AStar(rows, cols, initialNode, finalNode, blocksArray, false);
1516
List<Node> path = aStar.findPath();
1617
for (Node node : path) {
1718
System.out.println(node);

src/com/ai/astar/Node.java renamed to src/com/ai/astar/domain/Node.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
1-
package com.ai.astar;
2-
3-
/**
4-
* Node Class
5-
*
6-
* @author Marcelo Surriabre
7-
* @version 2.0, 2018-02-23
8-
*/
1+
package com.ai.astar.domain;
2+
93
public class Node {
104

115
private int g;
126
private int f;
13-
private int h;
7+
private int heuristic;
148
private final int row;
159
private final int col;
1610
private boolean isBlocked;
@@ -23,20 +17,20 @@ public Node(int row, int col) {
2317
}
2418

2519
public void calculateHeuristic(Node finalNode) {
26-
this.h = Math.abs(finalNode.row() - row) + Math.abs(finalNode.col() - col);
20+
this.heuristic = Math.abs(finalNode.row() - row) + Math.abs(finalNode.col() - col);
2721
}
2822

29-
public void setNodeData(Node currentNode, int cost) {
23+
public void updateNode(Node currentNode, int cost) {
3024
int gCost = currentNode.g() + cost;
3125
this.parent = currentNode;
3226
this.g = gCost;
33-
this.f = g + h;
27+
this.f = g + heuristic;
3428
}
3529

3630
public boolean checkBetterPath(Node currentNode, int cost) {
3731
int gCost = currentNode.g() + cost;
3832
if (gCost < g()) {
39-
setNodeData(currentNode, cost);
33+
updateNode(currentNode, cost);
4034
return true;
4135
}
4236
return false;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.ai.astar.domain.searchstrategy;
2+
3+
import com.ai.astar.domain.Node;
4+
5+
import java.util.PriorityQueue;
6+
import java.util.Set;
7+
8+
public class DiagonalMapChecker extends MapChecker {
9+
10+
private final int diagonalCost;
11+
12+
public DiagonalMapChecker(Node[][] searchArea, PriorityQueue<Node> openList, Set<Node> closedSet, int diagonalCost) {
13+
super(searchArea, openList, closedSet);
14+
this.diagonalCost = diagonalCost;
15+
}
16+
17+
@Override
18+
public void checkNode(Node currentNode, int col, int row) {
19+
check(currentNode, col - 1, row, diagonalCost);
20+
check(currentNode, col + 1, row, diagonalCost);
21+
}
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.ai.astar.domain.searchstrategy;
2+
3+
import com.ai.astar.domain.Node;
4+
5+
import java.util.PriorityQueue;
6+
import java.util.Set;
7+
8+
public class HorizontalVerticalChecker extends MapChecker {
9+
10+
private final int hvCost;
11+
12+
public HorizontalVerticalChecker(Node[][] searchArea, PriorityQueue<Node> openList, Set<Node> closedSet, int hvCost) {
13+
super(searchArea, openList, closedSet);
14+
this.hvCost = hvCost;
15+
}
16+
17+
@Override
18+
public void checkNode(Node currentNode, int col, int row) {
19+
check(currentNode, col, row, hvCost);
20+
}
21+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.ai.astar.domain.searchstrategy;
2+
3+
import com.ai.astar.domain.Node;
4+
5+
public interface MapCheck {
6+
7+
void checkNode(Node currentNode, int col, int row);
8+
9+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.ai.astar.domain.searchstrategy;
2+
3+
import com.ai.astar.domain.Node;
4+
5+
import java.util.PriorityQueue;
6+
import java.util.Set;
7+
8+
public abstract class MapChecker implements MapCheck {
9+
protected final Node[][] searchArea;
10+
protected final PriorityQueue<Node> openList;
11+
protected final Set<Node> closedSet;
12+
protected final int columns;
13+
14+
protected MapChecker(Node[][] searchArea, PriorityQueue<Node> openList, Set<Node> closedSet) {
15+
this.searchArea = searchArea;
16+
this.openList = openList;
17+
this.closedSet = closedSet;
18+
this.columns = searchArea == null ? 0 : searchArea[0].length;
19+
}
20+
21+
void check(Node currentNode, int col, int row, int cost) {
22+
if (row < 0 || row >= searchArea.length) {
23+
return;
24+
}
25+
if (col < 0 || col >= columns) {
26+
return;
27+
}
28+
Node adjacentNode = searchArea[row][col];
29+
if (adjacentNode.isBlocked() || closedSet.contains(adjacentNode)) {
30+
return;
31+
}
32+
if (!openList.contains(adjacentNode)) {
33+
adjacentNode.updateNode(currentNode, cost);
34+
openList.add(adjacentNode);
35+
} else {
36+
boolean changed = adjacentNode.checkBetterPath(currentNode, cost);
37+
if (changed) {
38+
// Remove and Add the changed node, so that the PriorityQueue can sort again its
39+
// contents with the modified "finalCost" value of the modified node
40+
openList.remove(adjacentNode);
41+
openList.add(adjacentNode);
42+
}
43+
}
44+
}
45+
46+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.ai.astar.domain.searchstrategy;
2+
3+
import com.ai.astar.domain.Node;
4+
5+
import java.util.PriorityQueue;
6+
import java.util.Set;
7+
8+
public class NoOpChecker extends MapChecker {
9+
10+
public NoOpChecker(Node[][] searchArea, PriorityQueue<Node> openList, Set<Node> closedSet) {
11+
super(searchArea, openList, closedSet);
12+
}
13+
14+
@Override
15+
public void checkNode(Node currentNode, int col, int row) {
16+
// nothing to do
17+
}
18+
}

0 commit comments

Comments
 (0)