-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBFS.py
117 lines (83 loc) · 3.5 KB
/
BFS.py
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
from collections import defaultdict
import unittest
class BFS:
__slots__ = "initial_state", "goal_state", "successors", "ITERATION_LIMIT"
def __init__(self, initial_state, goal_state, successors, iter_limit=1000):
self.check_validity(initial_state, goal_state, successors, iter_limit)
self.initial_state, self.goal_state, self.successors = initial_state, goal_state, successors
self.ITERATION_LIMIT = iter_limit
def setIterationLimit(self, newLimit):
assert type(newLimit) == int, "INVALID ITERATION LIMIT"
self.ITERATION_LIMIT = newLimit
def start(self, verbose=False):
Que = [self.initial_state]
parents = defaultdict(lambda: None)
done = False
iterationNumber = 0
while not done:
state = Que.pop()
if verbose:
print("current state is {} and Que is {}".format(state, Que))
if self.isGoal(state):
if verbose:
print("Goal Found! Building Path")
return self.build_path(state, parents, verbose)
for child in self.successors(state):
if not parents[child] and child != self.initial_state:
parents[child] = state
Que.append(child)
iterationNumber += 1
done = len(Que) == 0 or iterationNumber > self.ITERATION_LIMIT
print("Solution NOT found, or Iteration Limit reached")
return "FAILURE!"
def isGoal(self, state):
return state == self.goal_state
def build_path(self, state, parents, verbose=False):
path = [state]
while parents[state]:
path.append(parents[state])
state = parents[state]
path = [_ for _ in reversed(path)]
if verbose:
print("Built path! {} Now returning it".format(path))
return path
@staticmethod
def check_validity(initial_state, goal_state, successors, iter_limit):
assert initial_state, "Provide Initial State"
assert goal_state, "Provide Valid Goal State"
assert successors, "Provide a Valid Successor function"
assert callable(successors), "Successors has to be a callable function"
assert type(iter_limit) == int, "INVALID ITERATION LIMIT"
class TestBfsForWaterJugs(unittest.TestCase):
def setUp(self) -> None:
pass
def successors(self, state):
actions = [self.fill3, self.fill5, self.empty3, self.empty5, self.empty3in5, self.empty5in3]
children = [action(state) for action in actions if action(state)]
return children
def fill3(self, state):
return 3, state[1]
def fill5(self, state):
return state[0], 5
def empty3(self, state):
return 0, state[1]
def empty5(self, state):
return state[0], 5
def empty3in5(self, state):
space = 5 - state[1]
if space >= state[0]:
return 0, state[1] + state[0]
return state[0] - space, state[1] + space
def empty5in3(self, state):
space = 3 - state[0]
if space >= state[1]:
return state[0] + space, 0
return state[0] + space, state[1] - space
def test_water_jugs(self):
initial_state = (0, 0)
goal_state = (3, 5)
bfs = BFS(initial_state=initial_state, goal_state=goal_state, successors=self.successors)
# print(bfs.start())
self.assertEqual(bfs.start(verbose=True), [(0, 0), (0, 5), (3, 5)])
if __name__ == '__main__':
unittest.main()