Skip to content

Commit 1a85702

Browse files
committed
Implementation in Python 3
1 parent 492846a commit 1a85702

10 files changed

+529
-1
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,5 @@ docs/_build/
5656

5757
# PyBuilder
5858
target/
59+
60+
\.vscode/

Constants.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Direction:
2+
OLD_TO_NEW = 1
3+
NEW_TO_OLD = 0
4+
5+
6+
class CONST:
7+
def __init__(self, MAX_M, MAX_C, CAP_BOAT, MAX_TIME_S, MAX_NODES):
8+
self.MAX_M = MAX_M
9+
self.MAX_C = MAX_C
10+
self.CAP_BOAT = CAP_BOAT
11+
12+
self.MAX_TIME = MAX_TIME_S
13+
self.MAX_NODES = MAX_NODES
14+
15+
# TERMINAL_STATE = State(-1, -1, Direction.NEW_TO_OLD, -1, -1, 0)
16+
# INITIAL_STATE = None
17+
# # State(MAX_M, MAX_C, Direction.OLD_TO_NEW, 0, 0,0)

Graph.py

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
from collections import defaultdict
2+
3+
from State import TERMINAL_STATE
4+
5+
import time
6+
7+
8+
class Graph:
9+
10+
def __init__(self):
11+
12+
self.bfs_parent = {}
13+
self.dfs_parent = {}
14+
15+
self.expandedBFS = 0
16+
self.expandedDFS = 0
17+
18+
def BFS(self, s):
19+
self.expandedBFS = 0
20+
self.bfs_parent[s] = None
21+
visited = {(s.missionaries, s.cannibals, s.dir): True}
22+
s.level = 0
23+
24+
start_time = time.time()
25+
queue = [s]
26+
while queue:
27+
self.expandedBFS += 1
28+
29+
u = queue.pop(0)
30+
31+
if u.isGoalState():
32+
print("No of Expanded Nodes: " + str(self.expandedBFS))
33+
print("No of Explored Nodes: " + str(visited.__len__()))
34+
queue.clear()
35+
self.bfs_parent[TERMINAL_STATE] = u
36+
return self.bfs_parent
37+
38+
# Stops searching after a certain time/node limit
39+
t = time.time() - start_time
40+
if t > u.CONSTANTS.MAX_TIME or self.expandedBFS > u.CONSTANTS.MAX_NODES:
41+
if t > u.CONSTANTS.MAX_TIME:
42+
print("%.2fs EXCEEDED TIME LIMIT of %.2fs" % (t, u.CONSTANTS.MAX_TIME))
43+
else:
44+
print("EXCEEDED NODE LIMIT of %d" % u.CONSTANTS.MAX_NODES)
45+
print("No of Expanded Nodes: " + str(self.expandedBFS))
46+
print("No of Explored Nodes: " + str(visited.__len__()))
47+
queue.clear()
48+
return {}
49+
50+
for v in reversed(u.successors()):
51+
if (v.missionaries, v.cannibals, v.dir) not in visited.keys():
52+
self.bfs_parent[v] = u
53+
v.level = u.level + 1
54+
queue.append(v)
55+
visited[(v.missionaries, v.cannibals, v.dir)] = True
56+
57+
return {}
58+
59+
def DFS(self, s):
60+
self.expandedDFS = 0
61+
self.dfs_parent[s] = None
62+
visited = {(s.missionaries, s.cannibals, s.dir): True}
63+
64+
start_time = time.time()
65+
stack = [s]
66+
while stack:
67+
u = stack.pop()
68+
self.expandedDFS += 1
69+
70+
if u.isGoalState():
71+
print("No of Expanded Nodes: " + str(self.expandedDFS))
72+
print("No of Explored Nodes: " + str(visited.__len__()))
73+
self.dfs_parent[TERMINAL_STATE] = u
74+
stack.clear()
75+
return self.dfs_parent
76+
77+
t = time.time() - start_time
78+
# Stops searching after a certain time/node limit
79+
if t > u.CONSTANTS.MAX_TIME or self.expandedDFS > u.CONSTANTS.MAX_NODES:
80+
if t > u.CONSTANTS.MAX_TIME:
81+
print("%.2fs EXCEEDED TIME LIMIT of %.2fs" % (t, u.CONSTANTS.MAX_TIME))
82+
else:
83+
print("EXCEEDED NODE LIMIT of %d" % u.CONSTANTS.MAX_NODES)
84+
print("No of Expanded Nodes: " + str(self.expandedDFS))
85+
print("No of Explored Nodes: " + str(visited.__len__()))
86+
stack.clear()
87+
return {}
88+
89+
for v in u.successors():
90+
if (v.missionaries, v.cannibals, v.dir) not in visited.keys():
91+
visited[(v.missionaries, v.cannibals, v.dir)] = True
92+
self.dfs_parent[v] = u
93+
stack.append(v)
94+
return {}
95+
96+
# Prints the path returned by BFS/DFS
97+
def printPath(self, parentList, tail):
98+
if tail is None:
99+
return
100+
if parentList == {} or parentList is None: # tail not in parentList.keys():
101+
return
102+
if tail == TERMINAL_STATE: tail = parentList[tail]
103+
104+
stack = []
105+
106+
while tail is not None:
107+
stack.append(tail)
108+
tail = parentList[tail]
109+
110+
while stack:
111+
print(stack.pop())

Problem Statement.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
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.
3+
4+
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.
5+
6+
In addition, your computer implementation need to be able to deal with a scaled-up version of this problem (for example, a problem with
7+
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.
8+

README.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,19 @@
1-
Missionaries-and-Cannibals
1+
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.
2+
3+
4+
Here the problem formulation has been solved by BFS and the DFS search strategy.
5+
6+
7+
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".
8+
9+
>> Run "main.py"(in Python3)
10+
>> Input format("in.txt"):
11+
>Line1:m
12+
>Line2:c
13+
>Line3:k
14+
>Line4:TIME_LIMIT_IN_SECONDS
15+
>Line5:NO_OF_EXPLORED_NODES_LIMIT
16+
17+
>> Output:
18+
"outBFS.txt" contains the output generated by Breadth First Search and
19+
"outDFS.txt" contains the output generated by Depth First Search.

State.py

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from Constants import Direction
2+
3+
MAX_M = 30
4+
MAX_C = 30
5+
CAP_BOAT = 20
6+
CNST = None
7+
8+
9+
class State(object):
10+
11+
def __init__(self, missionaries, cannibals, dir, missionariesPassed, cannibalsPassed, level, CONSTS,moves):
12+
self.missionaries = missionaries
13+
self.cannibals = cannibals
14+
self.dir = dir
15+
self.action = ""
16+
self.level = level
17+
self.missionariesPassed = missionariesPassed
18+
self.cannibalsPassed = cannibalsPassed
19+
self.CONSTANTS = CONSTS
20+
21+
self.moves = moves
22+
23+
global MAX_M
24+
global MAX_C
25+
global CAP_BOAT
26+
global CNST
27+
28+
if not CONSTS is None:
29+
CNST = CONSTS
30+
31+
MAX_M = CONSTS.MAX_M
32+
MAX_C = CONSTS.MAX_C
33+
CAP_BOAT = CONSTS.CAP_BOAT
34+
35+
# pass True to count forward
36+
def successors(self):
37+
listChild = []
38+
if not self.isValid() or self.isGoalState():
39+
return listChild
40+
if self.dir == Direction.OLD_TO_NEW:
41+
sgn = -1
42+
direction = "from the original shore to the new shore"
43+
else:
44+
sgn = 1
45+
direction = "back from the new shore to the original shore"
46+
for i in self.moves:
47+
(m, c) = i
48+
self.addValidSuccessors(listChild, m, c, sgn, direction)
49+
return listChild
50+
51+
def addValidSuccessors(self, listChild, m, c, sgn, direction):
52+
newState = State(self.missionaries + sgn * m, self.cannibals + sgn * c, self.dir + sgn * 1,
53+
self.missionariesPassed - sgn * m, self.cannibalsPassed - sgn * c, self.level + 1,
54+
self.CONSTANTS,self.moves)
55+
if newState.isValid():
56+
newState.action = " take %d missionaries and %d cannibals %s." % (m, c, direction)
57+
listChild.append(newState)
58+
59+
def isValid(self):
60+
# obvious
61+
if self.missionaries < 0 or self.cannibals < 0 or self.missionaries > MAX_M or self.cannibals > MAX_C or (
62+
self.dir != 0 and self.dir != 1):
63+
return False
64+
65+
# then check whether missionaries outnumbered by cannibals in any shore
66+
if (self.cannibals > self.missionaries > 0) or (
67+
self.cannibalsPassed > self.missionariesPassed > 0): # more cannibals then missionaries on original shore
68+
return False
69+
70+
return True
71+
72+
def isGoalState(self):
73+
return self.cannibals == 0 and self.missionaries == 0 and self.dir == Direction.NEW_TO_OLD
74+
75+
def __repr__(self):
76+
return "\n%s\n\n< @Depth:%d State (%d, %d, %d, %d, %d) >" % (
77+
self.action, self.level, self.missionaries, self.cannibals, self.dir, self.missionariesPassed,
78+
self.cannibalsPassed)
79+
80+
def __eq__(self, other):
81+
return self.missionaries == other.missionaries and self.cannibals == other.cannibals and self.dir == other.dir
82+
83+
def __hash__(self):
84+
return hash((self.missionaries, self.cannibals, self.dir))
85+
86+
def __ne__(self, other):
87+
return not (self == other)
88+
89+
90+
TERMINAL_STATE = State(-1, -1, Direction.NEW_TO_OLD, -1, -1, 0, CNST,None)
91+
# INITIAL_STATE = State(MAX_M, MAX_C, Direction.OLD_TO_NEW, 0, 0, 0, CNST)

in.txt

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
3
2+
3
3+
2
4+
30
5+
100000
6+
7+
10000
8+
10000
9+
5
10+
120
11+
100000
12+
13+
10000
14+
10000
15+
20
16+
120
17+
100000
18+
19+
300
20+
300
21+
4
22+
30
23+
100000
24+
25+
329
26+
297
27+
17
28+
30
29+
100000
30+
31+
300
32+
300
33+
200
34+
120
35+
100000
36+
37+
200
38+
200
39+
11
40+
120
41+
100000
42+
43+
30
44+
30
45+
20
46+
30
47+
100000
48+
49+
4
50+
4
51+
3
52+
120
53+
100000
54+
55+
77
56+
47
57+
11
58+
30
59+
10000
60+
61+
2
62+
2
63+
2
64+
120
65+
100000
66+
67+
79
68+
47
69+
23
70+
120
71+
100000
72+
73+
74+
75+
9000
76+
8000
77+
200
78+
2
79+
10000
80+

0 commit comments

Comments
 (0)