-
Notifications
You must be signed in to change notification settings - Fork 11
/
SearchTree.java
345 lines (297 loc) · 15 KB
/
SearchTree.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
import java.util.*;
/**
* Created by HABDOLLA on 1/19/2016.
*
* @author Himan Abdollahpouri
* This class contains all search algorithms plus some utilty methods needed in those algorithm
*/
public class SearchTree {
private Node root;
private String goalSate;
public Node getRoot() {
return root;
}
public void setRoot(Node root) {
this.root = root;
}
public String getGoalSate() {
return goalSate;
}
public void setGoalSate(String goalSate) {
this.goalSate = goalSate;
}
public SearchTree(Node root, String goalSate) {
this.root = root;
this.goalSate = goalSate;
}
//******************************************************************************************************
// breadthFirstSearch() find the goal state using Breadth First Search algorithm
// we start with the initial state. check if it is goal or not and expand its children if it is not the goal.
// pop the first element of the queue and check if it is goal node. if not add its children to the queue.
// repeat the process untill the solution is found
public void breadthFirstSearch() {
// stateSet is a set that contains node that are already visited
Set<String> stateSets = new HashSet<String>();
int totalCost = 0;
int time = 0;
Node node = new Node(root.getState());
Queue<Node> queue = new LinkedList<Node>();
Node currentNode = node;
while (!currentNode.getState().equals(goalSate)) {
stateSets.add(currentNode.getState());
List<String> nodeSuccessors = NodeUtil.getSuccessors(currentNode.getState());
for (String n : nodeSuccessors) {
if (stateSets.contains(n))
continue;
stateSets.add(n);
Node child = new Node(n);
currentNode.addChild(child);
child.setParent(currentNode);
queue.add(child);
}
currentNode = queue.poll();
time += 1;
}
NodeUtil.printSolution(currentNode, stateSets, root, time);
}
//**********************************************************************************************
public void depthFirstSearch() {
// stateSet is a set that contains node that are already visited
Set<String> stateSets = new HashSet<String>();
int totalCost = 0;
int time = 0;
Node node = new Node(root.getState());
//the queue that store nodes that we should expand
MyQueue<Node> mainQueue = new MyQueue<>();
//the queue that contains the successors of the expanded node
MyQueue<Node> successorsQueue = new MyQueue<>();
Node currentNode = node;
while (!currentNode.getState().equals(goalSate)) {
stateSets.add(currentNode.getState());
List<String> nodeSuccessors = NodeUtil.getSuccessors(currentNode.getState());
for (String n : nodeSuccessors) {
if (stateSets.contains(n))
continue;
stateSets.add(n);
Node child = new Node(n);
currentNode.addChild(child);
child.setParent(currentNode);
successorsQueue.enqueue(child);
}
//we add the queue that contains the successors of the visted node to the beginning of the main queue
mainQueue.addQueue(successorsQueue);
//successors queue should be cleared in order to store the next expaneded's successors
successorsQueue.clear();
currentNode = mainQueue.dequeue();
time += 1;
nodeSuccessors.clear();
}
NodeUtil.printSolution(currentNode, stateSets, root, time);
}
/**
* Find the goal using Uniform cost algorithm. in each step the node with minimum cost will be expanded
*/
//***************************************************************************************************
public void uniformCostSearch() {
// stateSet is a set that contains node that are already visited
Set<String> stateSets = new HashSet<String>();
int totalCost = 0;
int time = 0;
Node node = new Node(root.getState());
node.setCost(0);
// the comparator compare the cost values and make the priority queue to be sorted based on cost values
NodePriorityComparator nodePriorityComparator = new NodePriorityComparator();
// a queue that contains nodes and their cost values sorted. 10 is the initial size
PriorityQueue<Node> nodePriorityQueue = new PriorityQueue<Node>(10, nodePriorityComparator);
Node currentNode = node;
while (!currentNode.getState().equals(goalSate)) {
stateSets.add(currentNode.getState());
List<String> nodeSuccessors = NodeUtil.getSuccessors(currentNode.getState());
for (String n : nodeSuccessors) {
if (stateSets.contains(n))
continue;
stateSets.add(n);
Node child = new Node(n);
currentNode.addChild(child);
child.setParent(currentNode);
child.setTotalCost(currentNode.getTotalCost() + Character.getNumericValue(child.getState().charAt(child.getParent().getState().indexOf('0'))), 0);
nodePriorityQueue.add(child);
}
currentNode = nodePriorityQueue.poll();
time += 1;
}
NodeUtil.printSolution(currentNode, stateSets, root, time);
}
/**
* Find the goal using Best Search First algorithm. The heuristic is
* the estimated cost from the current node to the goal node
*/
//*****************************************************************************************
public void bestFirstSearch() {
// stateSet is a set that contains node that are already visited
Set<String> stateSets = new HashSet<String>();
int totalCost = 0;
int time = 0;
Node node = new Node(root.getState());
node.setCost(0);
// the comparator compare the cost values and make the priority queue to be sorted based on cost values
NodePriorityComparator nodePriorityComparator = new NodePriorityComparator();
// a queue that contains nodes and their cost values sorted. 10 is the initial size
PriorityQueue<Node> nodePriorityQueue = new PriorityQueue<Node>(10, nodePriorityComparator);
Node currentNode = node;
while (!currentNode.getState().equals(goalSate)) {
stateSets.add(currentNode.getState());
List<String> nodeSuccessors = NodeUtil.getSuccessors(currentNode.getState());
for (String n : nodeSuccessors) {
if (stateSets.contains(n))
continue;
stateSets.add(n);
Node child = new Node(n);
currentNode.addChild(child);
child.setParent(currentNode);
child.setTotalCost(0, heuristicOne(child.getState(), goalSate));
nodePriorityQueue.add(child);
}
currentNode = nodePriorityQueue.poll();
time += 1;
}
// Here we try to navigate from the goal node to its parent( and its parent's parent and so on) to find the path
NodeUtil.printSolution(currentNode, stateSets, root, time);
}
/**
* Find the goal using A* algorithm. The huristic is the real cost to the current node from the initial Node
* plus the estimated cost from the current node to the goal node
*/
//*************************************************************************************************
public void aStar(Heuristic heuristic) {
// stateSet is a set that contains node that are already visited
Set<String> stateSets = new HashSet<String>();
int totalCost = 0;
int time = 0;
Node node = new Node(root.getState());
node.setTotalCost(0);
// the comparator compare the cost values and make the priority queue to be sorted based on cost values
NodePriorityComparator nodePriorityComparator = new NodePriorityComparator();
// a queue that contains nodes and their cost values sorted. 10 is the initial size
PriorityQueue<Node> nodePriorityQueue = new PriorityQueue<Node>(10, nodePriorityComparator);
Node currentNode = node;
while (!currentNode.getState().equals(goalSate)) {
stateSets.add(currentNode.getState());
List<String> nodeSuccessors = NodeUtil.getSuccessors(currentNode.getState());
for (String n : nodeSuccessors) {
if (stateSets.contains(n))
continue;
stateSets.add(n);
Node child = new Node(n);
currentNode.addChild(child);
child.setParent(currentNode);
if (heuristic == Heuristic.H_ONE)
child.setTotalCost(currentNode.getTotalCost() + Character.getNumericValue(child.getState().charAt(child.getParent().getState().indexOf('0'))), heuristicOne(child.getState(), goalSate));
else if (heuristic == Heuristic.H_TWO)
child.setTotalCost(currentNode.getTotalCost() + Character.getNumericValue(child.getState().charAt(child.getParent().getState().indexOf('0'))), heuristicTwo(child.getState(), goalSate));
else if (heuristic == Heuristic.H_THREE)
child.setTotalCost(currentNode.getTotalCost() + Character.getNumericValue(child.getState().charAt(child.getParent().getState().indexOf('0'))), heuristicThree(child.getState(), goalSate));
nodePriorityQueue.add(child);
}
currentNode = nodePriorityQueue.poll();
time += 1;
}
NodeUtil.printSolution(currentNode, stateSets, root, time);
}
/**
* Search for the goal state using limited depth First Search algorithm
* the depth limit starts from 1 and it increases to the defiend value by user that passed to the method
*/
//*****************************************************************************************************
public void iterativeDeepening(int depthLimit) {
Node currentNode = new Node(root.getState());
boolean solutionFound = false;
// stateSet is a set that contains node that are already visited
Set<String> stateSets = new HashSet<String>();
Set<String> totalVisitedStates = new HashSet<>();
int time = 0;
for (int maxDepth = 1; maxDepth < depthLimit; maxDepth++) {
//we should clear the visited list in each iteration
stateSets.clear();
//the queue that store nodes that we should expand
MyQueue<Node> mainQueue = new MyQueue<>();
//the queue that stores the successors of the expanded node
MyQueue<Node> successorsQueue = new MyQueue<>();
Node node = new Node(root.getState());
mainQueue.enqueue(node);
currentNode = node;
List<String> nodeSuccessors = null;
stateSets.add(currentNode.getState());
while (!mainQueue.isEmpty()) {
currentNode = mainQueue.dequeue();
time += 1;
if (currentNode.getState().equals(goalSate)) {
solutionFound = true;
break;
}
if (currentNode.getDepth() < maxDepth) {
nodeSuccessors = NodeUtil.getSuccessors(currentNode.getState());
for (String n : nodeSuccessors) {
if (stateSets.contains(n))
continue;
stateSets.add(n);
Node child = new Node(n);
currentNode.addChild(child);
child.setParent(currentNode);
child.setVisited(true);
child.setDepth(currentNode.getDepth() + 1);
successorsQueue.enqueue(child);
}
//we add the queue that contains the successors of the visted node to the beginning of the main queue
mainQueue.addQueue(successorsQueue);
//successors queue should be cleared in order to store the next expaneded's successors
successorsQueue.clear();
}
}
if (solutionFound)
break;
totalVisitedStates.addAll(stateSets);
}
if (!solutionFound)
System.out.println("No solution Found!");
else {
NodeUtil.printSolution(currentNode, totalVisitedStates, root, time);
}
}
//****************************************************************************************************
// This heuristic estimates the cost to the goal from current state by counting the number of misplaced tiles
private int heuristicOne(String currentState, String goalSate) {
int difference = 0;
for (int i = 0; i < currentState.length(); i += 1)
if (currentState.charAt(i) != goalSate.charAt(i))
difference += 1;
return difference;
}
//*************************************************************************************************
// This heuristic estimates the cost to the goal from current state by calculating the Manhathan distance from each misplaced
// tile to its right position in the goal state
private int heuristicTwo(String currentState, String goalSate) {
int difference = 0;
for (int i = 0; i < currentState.length(); i += 1)
for (int j = 0; j < goalSate.length(); j += 1)
if (currentState.charAt(i) == goalSate.charAt(j))
difference = difference + ((Math.abs(i % 3 - j % 3)) + Math.abs(i / 3 + j / 3));
return difference;
}
//***************************************************************************************************
// This heurisic is almost similar to heuristicTwo except that heuristicThree has higher value. when we estimate the cost
// by calculating the manhattan distance , we underestimate this cost because we do not count to the number moves the intermediate tiles shoud move
// in order to make space for the source tile to move. therefore, we think if the manhattan distance is, let say, 4, it means
// there are 3 tiles in between that they should also move. so the estimate is (manhattan distance +manhattan distance-1=
//2*manhattan distance-1
private int heuristicThree(String currentState, String goalSate) {
int difference = 0;
int manhattanDistance = 0;
for (int i = 0; i < currentState.length(); i += 1)
for (int j = 0; j < goalSate.length(); j += 1)
if (currentState.charAt(i) == goalSate.charAt(j))
manhattanDistance = (Math.abs(i % 3 - j % 3)) + Math.abs(i / 3 + j / 3);
difference = difference + 2 * manhattanDistance - 1;
return difference;
}
}