Skip to content

Commit d15abf3

Browse files
committed
Added search strategis
1 parent 7f1c0b7 commit d15abf3

19 files changed

+3568
-0
lines changed

Search/01-Search_Problem_Formulation_NPuzzle.ipynb

+430
Large diffs are not rendered by default.

Search/02-Data_Structures.ipynb

+439
Large diffs are not rendered by default.

Search/03-Uninformed_Search_Strategies.ipynb

+721
Large diffs are not rendered by default.

Search/04-Informed_Search_Strategies.ipynb

+793
Large diffs are not rendered by default.

Search/05-Search_Strategies_OOP.ipynb

+918
Large diffs are not rendered by default.

Search/imgs/.DS_Store

8 KB
Binary file not shown.

Search/imgs/BFS.png

614 KB
Loading

Search/imgs/Best_First.png

1.12 MB
Loading

Search/imgs/DFS_Example.png

1.12 MB
Loading

Search/imgs/DLS_IDS.png

1.17 MB
Loading
72.7 KB
Loading
72.7 KB
Loading

Search/imgs/Romania.png

764 KB
Loading
Loading

Search/imgs/example_solution_path.png

311 KB
Loading

Search/imgs/sample_state.png

16.7 KB
Loading

Search/imgs/vaccum_world.png

746 KB
Loading

Search/npuzzle.py

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import math
2+
import matplotlib.pyplot as plt
3+
4+
5+
class NPuzzleState:
6+
7+
def __init__(self, N=8, tiles=None):
8+
if tiles is None:
9+
self.tiles = tuple(range(N + 1)) # [0, 1, 2, ..., N] and 0 is blank
10+
else:
11+
N = len(tiles) - 1
12+
self.tiles = tuple(tiles[:])
13+
14+
self.N = N
15+
self.grid_size = int(math.sqrt(N + 1)) # for 8-puzzle, this is 3x3
16+
17+
def successors(self):
18+
''' Returns a list of possible actions, their costs and their resulting states.
19+
'''
20+
21+
blank_idx = self.tiles.index(0)
22+
successors = []
23+
24+
# left
25+
if blank_idx % self.grid_size > 0:
26+
tiles = list(self.tiles)
27+
tiles[blank_idx], tiles[blank_idx - 1] = tiles[blank_idx - 1], tiles[blank_idx]
28+
successor = NPuzzleState(tiles=tiles)
29+
successors.append((successor, 'Left', 1))
30+
31+
# up
32+
if blank_idx >= self.grid_size:
33+
tiles = list(self.tiles)
34+
tiles[blank_idx], tiles[blank_idx - self.grid_size] = tiles[blank_idx - self.grid_size], tiles[blank_idx]
35+
successor = NPuzzleState(tiles=tiles)
36+
successors.append((successor, 'Up', 1))
37+
38+
# right
39+
if blank_idx % self.grid_size < self.grid_size - 1:
40+
tiles = list(self.tiles)
41+
tiles[blank_idx], tiles[blank_idx + 1] = tiles[blank_idx + 1], tiles[blank_idx]
42+
successor = NPuzzleState(tiles=tiles)
43+
successors.append((successor, 'Right', 1))
44+
45+
# down
46+
if blank_idx + self.grid_size < len(self.tiles):
47+
tiles = list(self.tiles)
48+
tiles[blank_idx], tiles[blank_idx + self.grid_size] = tiles[blank_idx + self.grid_size], tiles[blank_idx]
49+
successor = NPuzzleState(tiles=tiles)
50+
successors.append((successor, 'Down', 1))
51+
52+
return successors
53+
54+
def is_goal(self, goal_state):
55+
return self == goal_state
56+
57+
def plot(self, ax=None, title=None, fs=20):
58+
if ax is None:
59+
fig, ax = plt.subplots(1)
60+
61+
size = 10
62+
gs = self.grid_size
63+
64+
# draw border
65+
border = plt.Rectangle((0, 0), gs * size, gs * size, ec='k', fc='w', lw=3)
66+
ax.add_patch(border)
67+
68+
# draw tiles
69+
for i, tile in enumerate(self.tiles):
70+
if tile == 0: continue
71+
col = self.grid_size - 1 - i // self.grid_size
72+
row = i % self.grid_size
73+
cell = plt.Rectangle((row * size, col * size), size, size, fc='gray', ec='k', lw=3)
74+
ax.add_patch(cell)
75+
ax.text(row * size + size//2, col * size + size//2, "%d" % tile, color='w',
76+
fontsize=fs, verticalalignment='center', horizontalalignment='center')
77+
78+
ax.axis('square')
79+
ax.axis('off')
80+
if title:
81+
ax.set_title(title, fontsize=fs)
82+
83+
def __hash__(self):
84+
return hash(self.tiles)
85+
86+
def __eq__(self, other):
87+
if self is other: return True # True object equallity test for efficiency
88+
if not isinstance(other, NPuzzleState): return False
89+
90+
return self.tiles == other.tiles
91+
92+
def __str__(self):
93+
""" An string representation of the tiles configuration in 2d format.
94+
"""
95+
result = ''
96+
for i in range(len(self.tiles)):
97+
result += f' {self.tiles[i]:2d} ' if self.tiles[i] != 0 else ' '
98+
if i % self.grid_size == self.grid_size - 1 and i < self.N:
99+
result += '\n'
100+
return result
101+
102+
def __repr__(self):
103+
return f'NPuzzleState(N={self.N}, tiles={self.tiles})'

Search/utils.py

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import math
2+
import heapq, random
3+
4+
import matplotlib.pyplot as plt
5+
6+
7+
def print_solution(start_state, path):
8+
if path is None:
9+
print("No solution found!")
10+
return
11+
12+
print(start_state)
13+
for state, action in path:
14+
print("\n {} \n".format(action))
15+
print(state)
16+
17+
18+
def show_solution(start_state, path, ncols=5):
19+
if not isinstance(path, list):
20+
print("No solution found!")
21+
return
22+
23+
N = len(path) + 1
24+
nrows = int(math.ceil(N / ncols))
25+
26+
fig, axes = plt.subplots(nrows, ncols, figsize=(3 * ncols, 3 * nrows))
27+
28+
if nrows > 1:
29+
start_state.plot(axes[0][0], 'start', 18)
30+
for i, (state, action) in enumerate(path):
31+
state.plot(axes[(i + 1) // ncols][(i + 1) % ncols], action, 18)
32+
for i in range(N, nrows * ncols):
33+
axes[nrows-1][i % ncols].axis('off')
34+
35+
else:
36+
start_state.plot(axes[0], 'start', 18)
37+
for i, (state, action) in enumerate(path):
38+
state.plot(axes[i + 1], action, 18)
39+
for i in range(N, ncols):
40+
axes[i].axis('off')
41+
42+
43+
def solution(node):
44+
path = []
45+
while node.parent is not None:
46+
path = [(node.state, node.action)] + path
47+
node = node.parent
48+
return path
49+
50+
51+
def manhatan_distance(tile, state1, state2):
52+
i = state1.tiles.index(tile)
53+
j = state2.tiles.index(tile)
54+
55+
gs = state1.grid_size
56+
57+
row_i, col_i = i // gs, i % gs
58+
row_j, col_j = j // gs, j % gs
59+
60+
return abs(row_i - row_j) + abs(col_i - col_j)
61+
62+
63+
"""
64+
Data structures useful for implementing Search Strategies
65+
"""
66+
67+
class Stack:
68+
def __init__(self, items=None):
69+
self._items = []
70+
71+
if items:
72+
for item in items:
73+
self.push(item)
74+
75+
def push(self, item):
76+
'''Add to the end'''
77+
self._items.append(item)
78+
79+
def pop(self):
80+
'''Remove from end'''
81+
try:
82+
item = self._items.pop()
83+
return item
84+
except:
85+
print('ERROR! trying to pop an element from an empty stack.')
86+
87+
88+
def is_empty(self):
89+
return len(self._items) == 0
90+
91+
def __repr__(self):
92+
return f'Stack(items={self._items})'
93+
94+
def __str__(self):
95+
return f"[{', '.join(self._items)}]"
96+
97+
98+
class Queue:
99+
def __init__(self, items=None):
100+
self._items = []
101+
102+
if items:
103+
for item in items:
104+
self.push(item)
105+
106+
def push(self, item):
107+
'''Add to the rear'''
108+
self._items.append(item)
109+
110+
def pop(self):
111+
'''Remove from front'''
112+
try:
113+
item = self._items[0]
114+
self._items = self._items[1:]
115+
return item
116+
except:
117+
print('ERROR! trying to pop an element from an empty queue.')
118+
119+
120+
def is_empty(self):
121+
return len(self._items) == 0
122+
123+
def __repr__(self):
124+
return f'Queue(items={self._items})'
125+
126+
def __str__(self):
127+
return f"[{', '.join(self._items)}]"
128+
129+
130+
class PriorityQueue:
131+
def __init__(self, items=None):
132+
self._items = []
133+
self.index = 0
134+
135+
if items:
136+
for item, priority in items:
137+
self.push(item, priority)
138+
139+
def push(self, item, priority):
140+
'''Add to the rear'''
141+
entry = (priority, self.index, item)
142+
heapq.heappush(self._items, entry)
143+
self.index += 1
144+
145+
def pop(self):
146+
'''Remove the item with highest priority'''
147+
try:
148+
_, _, item = heapq.heappop(self._items)
149+
return item
150+
except:
151+
print('ERROR! trying to pop an element from an empty priority queue.')
152+
153+
def is_empty(self):
154+
return len(self._items) == 0
155+
156+
def __repr__(self):
157+
return f'PriorityQueue(items={self._items})'
158+
159+
def __str__(self):
160+
res = '['
161+
for priority, _, item in self._items:
162+
res += f' {item}({priority}) '
163+
res += ']'
164+
return res

0 commit comments

Comments
 (0)