Skip to content

Commit 62144f6

Browse files
authored
Add tests for AStar.java, enhance documentation (#5603)
1 parent d422bf5 commit 62144f6

File tree

3 files changed

+92
-99
lines changed

3 files changed

+92
-99
lines changed

DIRECTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@
694694
* dynamicarray
695695
* [DynamicArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java)
696696
* graphs
697+
* [AStarTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java)
697698
* [BipartiteGraphDFSTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFSTest.java)
698699
* [BoruvkaAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithmTest.java)
699700
* [DijkstraAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java)
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
1-
/*
2-
Time Complexity = O(E), where E is equal to the number of edges
3-
*/
41
package com.thealgorithms.datastructures.graphs;
52

63
import java.util.ArrayList;
7-
import java.util.Arrays;
84
import java.util.Comparator;
95
import java.util.List;
106
import java.util.PriorityQueue;
117

8+
/**
9+
* AStar class implements the A* pathfinding algorithm to find the shortest path in a graph.
10+
* The graph is represented using an adjacency list, and the algorithm uses a heuristic to estimate
11+
* the cost to reach the destination node.
12+
* Time Complexity = O(E), where E is equal to the number of edges
13+
*/
1214
public final class AStar {
1315
private AStar() {
1416
}
1517

16-
private static class Graph {
17-
18-
// Graph's structure can be changed only applying changes to this class.
19-
18+
/**
19+
* Represents a graph using an adjacency list.
20+
*/
21+
static class Graph {
2022
private ArrayList<ArrayList<Edge>> graph;
2123

22-
// Initialise ArrayLists in Constructor
2324
Graph(int size) {
2425
this.graph = new ArrayList<>();
2526
for (int i = 0; i < size; i++) {
@@ -31,15 +32,17 @@ private ArrayList<Edge> getNeighbours(int from) {
3132
return this.graph.get(from);
3233
}
3334

34-
// Graph is bidirectional, for just one direction remove second instruction of this method.
35+
// Add a bidirectional edge to the graph
3536
private void addEdge(Edge edge) {
3637
this.graph.get(edge.getFrom()).add(new Edge(edge.getFrom(), edge.getTo(), edge.getWeight()));
3738
this.graph.get(edge.getTo()).add(new Edge(edge.getTo(), edge.getFrom(), edge.getWeight()));
3839
}
3940
}
4041

42+
/**
43+
* Represents an edge in the graph with a start node, end node, and weight.
44+
*/
4145
private static class Edge {
42-
4346
private int from;
4447
private int to;
4548
private int weight;
@@ -63,12 +66,13 @@ public int getWeight() {
6366
}
6467
}
6568

66-
// class to iterate during the algorithm execution, and also used to return the solution.
67-
private static class PathAndDistance {
68-
69-
private int distance; // distance advanced so far.
70-
private ArrayList<Integer> path; // list of visited nodes in this path.
71-
private int estimated; // heuristic value associated to the last node od the path (current node).
69+
/**
70+
* Contains information about the path and its total distance.
71+
*/
72+
static class PathAndDistance {
73+
private int distance; // total distance from the start node
74+
private ArrayList<Integer> path; // list of nodes in the path
75+
private int estimated; // heuristic estimate for reaching the destination
7276

7377
PathAndDistance(int distance, ArrayList<Integer> path, int estimated) {
7478
this.distance = distance;
@@ -87,112 +91,54 @@ public ArrayList<Integer> getPath() {
8791
public int getEstimated() {
8892
return estimated;
8993
}
90-
91-
private void printSolution() {
92-
if (this.path != null) {
93-
System.out.println("Optimal path: " + this.path + ", distance: " + this.distance);
94-
} else {
95-
System.out.println("There is no path available to connect the points");
96-
}
97-
}
9894
}
9995

100-
private static void initializeGraph(Graph graph, ArrayList<Integer> data) {
96+
// Initializes the graph with edges defined in the input data
97+
static void initializeGraph(Graph graph, ArrayList<Integer> data) {
10198
for (int i = 0; i < data.size(); i += 4) {
10299
graph.addEdge(new Edge(data.get(i), data.get(i + 1), data.get(i + 2)));
103100
}
104-
/*
105-
.x. node
106-
(y) cost
107-
- or | or / bidirectional connection
108-
109-
( 98)- .7. -(86)- .4.
110-
|
111-
( 85)- .17. -(142)- .18. -(92)- .8. -(87)- .11.
112-
|
113-
. 1. -------------------- (160)
114-
| \ |
115-
(211) \ .6.
116-
| \ |
117-
. 5. (101)-.13. -(138) (115)
118-
| | | /
119-
( 99) ( 97) | /
120-
| | | /
121-
.12. -(151)- .15. -(80)- .14. | /
122-
| | | | /
123-
( 71) (140) (146)- .2. -(120)
124-
| | |
125-
.19. -( 75)- . 0. .10. -(75)- .3.
126-
| |
127-
(118) ( 70)
128-
| |
129-
.16. -(111)- .9.
130-
*/
131-
}
132-
133-
public static void main(String[] args) {
134-
// heuristic function optimistic values
135-
int[] heuristic = {
136-
366,
137-
0,
138-
160,
139-
242,
140-
161,
141-
178,
142-
77,
143-
151,
144-
226,
145-
244,
146-
241,
147-
234,
148-
380,
149-
98,
150-
193,
151-
253,
152-
329,
153-
80,
154-
199,
155-
374,
156-
};
157-
158-
Graph graph = new Graph(20);
159-
ArrayList<Integer> graphData = new ArrayList<>(Arrays.asList(0, 19, 75, null, 0, 15, 140, null, 0, 16, 118, null, 19, 12, 71, null, 12, 15, 151, null, 16, 9, 111, null, 9, 10, 70, null, 10, 3, 75, null, 3, 2, 120, null, 2, 14, 146, null, 2, 13, 138, null, 2, 6, 115, null, 15, 14, 80, null,
160-
15, 5, 99, null, 14, 13, 97, null, 5, 1, 211, null, 13, 1, 101, null, 6, 1, 160, null, 1, 17, 85, null, 17, 7, 98, null, 7, 4, 86, null, 17, 18, 142, null, 18, 8, 92, null, 8, 11, 87));
161-
initializeGraph(graph, graphData);
162-
163-
PathAndDistance solution = aStar(3, 1, graph, heuristic);
164-
solution.printSolution();
165101
}
166102

103+
/**
104+
* Implements the A* pathfinding algorithm to find the shortest path from a start node to a destination node.
105+
*
106+
* @param from the starting node
107+
* @param to the destination node
108+
* @param graph the graph representation of the problem
109+
* @param heuristic the heuristic estimates for each node
110+
* @return a PathAndDistance object containing the shortest path and its distance
111+
*/
167112
public static PathAndDistance aStar(int from, int to, Graph graph, int[] heuristic) {
168-
// nodes are prioritised by the less value of the current distance of their paths, and the
169-
// estimated value
170-
// given by the heuristic function to reach the destination point from the current point.
113+
// PriorityQueue to explore nodes based on their distance and estimated cost to reach the destination
171114
PriorityQueue<PathAndDistance> queue = new PriorityQueue<>(Comparator.comparingInt(a -> (a.getDistance() + a.getEstimated())));
172115

173-
// dummy data to start the algorithm from the beginning point
174-
queue.add(new PathAndDistance(0, new ArrayList<>(List.of(from)), 0));
116+
// Start with the initial node
117+
queue.add(new PathAndDistance(0, new ArrayList<>(List.of(from)), heuristic[from]));
175118

176119
boolean solutionFound = false;
177120
PathAndDistance currentData = new PathAndDistance(-1, null, -1);
121+
178122
while (!queue.isEmpty() && !solutionFound) {
179-
currentData = queue.poll(); // first in the queue, best node so keep exploring.
180-
int currentPosition = currentData.getPath().get(currentData.getPath().size() - 1); // current node.
123+
currentData = queue.poll(); // get the best node from the queue
124+
int currentPosition = currentData.getPath().get(currentData.getPath().size() - 1); // current node
125+
126+
// Check if the destination has been reached
181127
if (currentPosition == to) {
182128
solutionFound = true;
183129
} else {
184130
for (Edge edge : graph.getNeighbours(currentPosition)) {
185-
if (!currentData.getPath().contains(edge.getTo())) { // Avoid Cycles
131+
// Avoid cycles by checking if the next node is already in the path
132+
if (!currentData.getPath().contains(edge.getTo())) {
186133
ArrayList<Integer> updatedPath = new ArrayList<>(currentData.getPath());
187-
updatedPath.add(edge.getTo()); // Add the new node to the path, update the distance,
188-
// and the heuristic function value associated to that path.
134+
updatedPath.add(edge.getTo());
135+
136+
// Update the distance and heuristic for the new path
189137
queue.add(new PathAndDistance(currentData.getDistance() + edge.getWeight(), updatedPath, heuristic[edge.getTo()]));
190138
}
191139
}
192140
}
193141
}
194142
return (solutionFound) ? currentData : new PathAndDistance(-1, null, -1);
195-
// Out of while loop, if there is a solution, the current Data stores the optimal path, and
196-
// its distance
197143
}
198144
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNull;
5+
6+
import java.util.ArrayList;
7+
import java.util.Arrays;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.Test;
10+
11+
public class AStarTest {
12+
13+
private AStar.Graph graph;
14+
private int[] heuristic;
15+
16+
@BeforeEach
17+
public void setUp() {
18+
// Initialize graph and heuristic values for testing
19+
graph = new AStar.Graph(5);
20+
ArrayList<Integer> graphData = new ArrayList<>(Arrays.asList(0, 1, 1, null, 0, 2, 2, null, 1, 3, 1, null, 2, 3, 1, null, 3, 4, 1, null));
21+
AStar.initializeGraph(graph, graphData);
22+
23+
heuristic = new int[] {5, 4, 3, 2, 0}; // Heuristic values for each node
24+
}
25+
26+
@Test
27+
public void testAStarFindsPath() {
28+
AStar.PathAndDistance result = AStar.aStar(0, 4, graph, heuristic);
29+
assertEquals(3, result.getDistance(), "Expected distance from 0 to 4 is 3");
30+
assertEquals(Arrays.asList(0, 1, 3, 4), result.getPath(), "Expected path from 0 to 4");
31+
}
32+
33+
@Test
34+
public void testAStarPathNotFound() {
35+
AStar.PathAndDistance result = AStar.aStar(0, 5, graph, heuristic); // Node 5 does not exist
36+
assertEquals(-1, result.getDistance(), "Expected distance when path not found is -1");
37+
assertNull(result.getPath(), "Expected path should be null when no path exists");
38+
}
39+
40+
@Test
41+
public void testAStarSameNode() {
42+
AStar.PathAndDistance result = AStar.aStar(0, 0, graph, heuristic);
43+
assertEquals(0, result.getDistance(), "Expected distance from 0 to 0 is 0");
44+
assertEquals(Arrays.asList(0), result.getPath(), "Expected path should only contain the start node");
45+
}
46+
}

0 commit comments

Comments
 (0)