-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
529 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,3 +56,5 @@ docs/_build/ | |
|
||
# PyBuilder | ||
target/ | ||
|
||
\.vscode/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
class Direction: | ||
OLD_TO_NEW = 1 | ||
NEW_TO_OLD = 0 | ||
|
||
|
||
class CONST: | ||
def __init__(self, MAX_M, MAX_C, CAP_BOAT, MAX_TIME_S, MAX_NODES): | ||
self.MAX_M = MAX_M | ||
self.MAX_C = MAX_C | ||
self.CAP_BOAT = CAP_BOAT | ||
|
||
self.MAX_TIME = MAX_TIME_S | ||
self.MAX_NODES = MAX_NODES | ||
|
||
# TERMINAL_STATE = State(-1, -1, Direction.NEW_TO_OLD, -1, -1, 0) | ||
# INITIAL_STATE = None | ||
# # State(MAX_M, MAX_C, Direction.OLD_TO_NEW, 0, 0,0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
from collections import defaultdict | ||
|
||
from State import TERMINAL_STATE | ||
|
||
import time | ||
|
||
|
||
class Graph: | ||
|
||
def __init__(self): | ||
|
||
self.bfs_parent = {} | ||
self.dfs_parent = {} | ||
|
||
self.expandedBFS = 0 | ||
self.expandedDFS = 0 | ||
|
||
def BFS(self, s): | ||
self.expandedBFS = 0 | ||
self.bfs_parent[s] = None | ||
visited = {(s.missionaries, s.cannibals, s.dir): True} | ||
s.level = 0 | ||
|
||
start_time = time.time() | ||
queue = [s] | ||
while queue: | ||
self.expandedBFS += 1 | ||
|
||
u = queue.pop(0) | ||
|
||
if u.isGoalState(): | ||
print("No of Expanded Nodes: " + str(self.expandedBFS)) | ||
print("No of Explored Nodes: " + str(visited.__len__())) | ||
queue.clear() | ||
self.bfs_parent[TERMINAL_STATE] = u | ||
return self.bfs_parent | ||
|
||
# Stops searching after a certain time/node limit | ||
t = time.time() - start_time | ||
if t > u.CONSTANTS.MAX_TIME or self.expandedBFS > u.CONSTANTS.MAX_NODES: | ||
if t > u.CONSTANTS.MAX_TIME: | ||
print("%.2fs EXCEEDED TIME LIMIT of %.2fs" % (t, u.CONSTANTS.MAX_TIME)) | ||
else: | ||
print("EXCEEDED NODE LIMIT of %d" % u.CONSTANTS.MAX_NODES) | ||
print("No of Expanded Nodes: " + str(self.expandedBFS)) | ||
print("No of Explored Nodes: " + str(visited.__len__())) | ||
queue.clear() | ||
return {} | ||
|
||
for v in reversed(u.successors()): | ||
if (v.missionaries, v.cannibals, v.dir) not in visited.keys(): | ||
self.bfs_parent[v] = u | ||
v.level = u.level + 1 | ||
queue.append(v) | ||
visited[(v.missionaries, v.cannibals, v.dir)] = True | ||
|
||
return {} | ||
|
||
def DFS(self, s): | ||
self.expandedDFS = 0 | ||
self.dfs_parent[s] = None | ||
visited = {(s.missionaries, s.cannibals, s.dir): True} | ||
|
||
start_time = time.time() | ||
stack = [s] | ||
while stack: | ||
u = stack.pop() | ||
self.expandedDFS += 1 | ||
|
||
if u.isGoalState(): | ||
print("No of Expanded Nodes: " + str(self.expandedDFS)) | ||
print("No of Explored Nodes: " + str(visited.__len__())) | ||
self.dfs_parent[TERMINAL_STATE] = u | ||
stack.clear() | ||
return self.dfs_parent | ||
|
||
t = time.time() - start_time | ||
# Stops searching after a certain time/node limit | ||
if t > u.CONSTANTS.MAX_TIME or self.expandedDFS > u.CONSTANTS.MAX_NODES: | ||
if t > u.CONSTANTS.MAX_TIME: | ||
print("%.2fs EXCEEDED TIME LIMIT of %.2fs" % (t, u.CONSTANTS.MAX_TIME)) | ||
else: | ||
print("EXCEEDED NODE LIMIT of %d" % u.CONSTANTS.MAX_NODES) | ||
print("No of Expanded Nodes: " + str(self.expandedDFS)) | ||
print("No of Explored Nodes: " + str(visited.__len__())) | ||
stack.clear() | ||
return {} | ||
|
||
for v in u.successors(): | ||
if (v.missionaries, v.cannibals, v.dir) not in visited.keys(): | ||
visited[(v.missionaries, v.cannibals, v.dir)] = True | ||
self.dfs_parent[v] = u | ||
stack.append(v) | ||
return {} | ||
|
||
# Prints the path returned by BFS/DFS | ||
def printPath(self, parentList, tail): | ||
if tail is None: | ||
return | ||
if parentList == {} or parentList is None: # tail not in parentList.keys(): | ||
return | ||
if tail == TERMINAL_STATE: tail = parentList[tail] | ||
|
||
stack = [] | ||
|
||
while tail is not None: | ||
stack.append(tail) | ||
tail = parentList[tail] | ||
|
||
while stack: | ||
print(stack.pop()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
|
||
The missionaries and cannibals problem, which is a famous problem in AI, is usually stated as follows. Three missionaries and three cannibals are on one side of a river, along with a boat that can hold one or two people. Find a way to get everyone to the other side without ever leaving a group of missionaries in one place outnumbered by the cannibals in that place. | ||
|
||
In this assignment, your task is to do the problem formulation so it can be solved by searching, and do the computer implementation in order to experimentally compare the performance of the BFS and the DFS search strategy. For performance comparison, you may use time, number of nodes explored, number of nodes expanded, effective branching factor etc. | ||
|
||
In addition, your computer implementation need to be able to deal with a scaled-up version of this problem (for example, a problem with | ||
five missionaries and five cannibals). The implementation may have m number of missionaries, c number of cannibals, k number of maximum allowable passengers in the boat. There should a search cut-off limit (for example, termination after 30 seconds, or after 1,000,000 nodes have been expanded) which you should be able to vary. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,19 @@ | ||
Missionaries-and-Cannibals | ||
The missionaries and cannibals problem, which is a famous problem in AI, is usually stated as follows. Three missionaries and three cannibals are on one side of a river, along with a boat that can hold one or two people. Find a way to get everyone to the other side without ever leaving a group of missionaries in one place outnumbered by the cannibals in that place. | ||
|
||
|
||
Here the problem formulation has been solved by BFS and the DFS search strategy. | ||
|
||
|
||
This implementation is able to deal with a scaled-up version of this problem (for example, a problem with five missionaries and five cannibals). The implementation may have m number of missionaries, c number of cannibals, k number of maximum allowable passengers in the boat. There is a search cut-off limit (for example, termination after 30 seconds, or after 1,000,000 nodes have been expanded) which was passed as input via "in.txt". | ||
|
||
>> Run "main.py"(in Python3) | ||
>> Input format("in.txt"): | ||
>Line1:m | ||
>Line2:c | ||
>Line3:k | ||
>Line4:TIME_LIMIT_IN_SECONDS | ||
>Line5:NO_OF_EXPLORED_NODES_LIMIT | ||
>> Output: | ||
"outBFS.txt" contains the output generated by Breadth First Search and | ||
"outDFS.txt" contains the output generated by Depth First Search. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
from Constants import Direction | ||
|
||
MAX_M = 30 | ||
MAX_C = 30 | ||
CAP_BOAT = 20 | ||
CNST = None | ||
|
||
|
||
class State(object): | ||
|
||
def __init__(self, missionaries, cannibals, dir, missionariesPassed, cannibalsPassed, level, CONSTS,moves): | ||
self.missionaries = missionaries | ||
self.cannibals = cannibals | ||
self.dir = dir | ||
self.action = "" | ||
self.level = level | ||
self.missionariesPassed = missionariesPassed | ||
self.cannibalsPassed = cannibalsPassed | ||
self.CONSTANTS = CONSTS | ||
|
||
self.moves = moves | ||
|
||
global MAX_M | ||
global MAX_C | ||
global CAP_BOAT | ||
global CNST | ||
|
||
if not CONSTS is None: | ||
CNST = CONSTS | ||
|
||
MAX_M = CONSTS.MAX_M | ||
MAX_C = CONSTS.MAX_C | ||
CAP_BOAT = CONSTS.CAP_BOAT | ||
|
||
# pass True to count forward | ||
def successors(self): | ||
listChild = [] | ||
if not self.isValid() or self.isGoalState(): | ||
return listChild | ||
if self.dir == Direction.OLD_TO_NEW: | ||
sgn = -1 | ||
direction = "from the original shore to the new shore" | ||
else: | ||
sgn = 1 | ||
direction = "back from the new shore to the original shore" | ||
for i in self.moves: | ||
(m, c) = i | ||
self.addValidSuccessors(listChild, m, c, sgn, direction) | ||
return listChild | ||
|
||
def addValidSuccessors(self, listChild, m, c, sgn, direction): | ||
newState = State(self.missionaries + sgn * m, self.cannibals + sgn * c, self.dir + sgn * 1, | ||
self.missionariesPassed - sgn * m, self.cannibalsPassed - sgn * c, self.level + 1, | ||
self.CONSTANTS,self.moves) | ||
if newState.isValid(): | ||
newState.action = " take %d missionaries and %d cannibals %s." % (m, c, direction) | ||
listChild.append(newState) | ||
|
||
def isValid(self): | ||
# obvious | ||
if self.missionaries < 0 or self.cannibals < 0 or self.missionaries > MAX_M or self.cannibals > MAX_C or ( | ||
self.dir != 0 and self.dir != 1): | ||
return False | ||
|
||
# then check whether missionaries outnumbered by cannibals in any shore | ||
if (self.cannibals > self.missionaries > 0) or ( | ||
self.cannibalsPassed > self.missionariesPassed > 0): # more cannibals then missionaries on original shore | ||
return False | ||
|
||
return True | ||
|
||
def isGoalState(self): | ||
return self.cannibals == 0 and self.missionaries == 0 and self.dir == Direction.NEW_TO_OLD | ||
|
||
def __repr__(self): | ||
return "\n%s\n\n< @Depth:%d State (%d, %d, %d, %d, %d) >" % ( | ||
self.action, self.level, self.missionaries, self.cannibals, self.dir, self.missionariesPassed, | ||
self.cannibalsPassed) | ||
|
||
def __eq__(self, other): | ||
return self.missionaries == other.missionaries and self.cannibals == other.cannibals and self.dir == other.dir | ||
|
||
def __hash__(self): | ||
return hash((self.missionaries, self.cannibals, self.dir)) | ||
|
||
def __ne__(self, other): | ||
return not (self == other) | ||
|
||
|
||
TERMINAL_STATE = State(-1, -1, Direction.NEW_TO_OLD, -1, -1, 0, CNST,None) | ||
# INITIAL_STATE = State(MAX_M, MAX_C, Direction.OLD_TO_NEW, 0, 0, 0, CNST) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
3 | ||
3 | ||
2 | ||
30 | ||
100000 | ||
|
||
10000 | ||
10000 | ||
5 | ||
120 | ||
100000 | ||
|
||
10000 | ||
10000 | ||
20 | ||
120 | ||
100000 | ||
|
||
300 | ||
300 | ||
4 | ||
30 | ||
100000 | ||
|
||
329 | ||
297 | ||
17 | ||
30 | ||
100000 | ||
|
||
300 | ||
300 | ||
200 | ||
120 | ||
100000 | ||
|
||
200 | ||
200 | ||
11 | ||
120 | ||
100000 | ||
|
||
30 | ||
30 | ||
20 | ||
30 | ||
100000 | ||
|
||
4 | ||
4 | ||
3 | ||
120 | ||
100000 | ||
|
||
77 | ||
47 | ||
11 | ||
30 | ||
10000 | ||
|
||
2 | ||
2 | ||
2 | ||
120 | ||
100000 | ||
|
||
79 | ||
47 | ||
23 | ||
120 | ||
100000 | ||
|
||
|
||
|
||
9000 | ||
8000 | ||
200 | ||
2 | ||
10000 | ||
|
Oops, something went wrong.