Skip to content

Commit 718bb86

Browse files
committed
Add A*, summary, bonus tasks, resources
1 parent ab66292 commit 718bb86

File tree

3 files changed

+305
-468
lines changed

3 files changed

+305
-468
lines changed

search.py

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ def get_root(parents):
239239
# return State(world)
240240

241241

242-
def show_search_tree(tree, fringe, explored, costs=None):
242+
def show_search_tree(tree, fringe, explored, costs=None, heuristic=None):
243243
state = explored[-1] if explored else get_root(tree)
244244
show_state(state)
245245
for child, parent in tree.items():
@@ -250,9 +250,18 @@ def show_search_tree(tree, fringe, explored, costs=None):
250250
xs = [s.spaceship.col + 0.5 for s in explored]
251251
ys = [state.n - s.spaceship.row - 0.5 for s in explored]
252252
if costs:
253-
labels = [
254-
'{order}\nc={cost}'.format(order=i, cost=costs[s])
255-
for i, s in enumerate(explored, start=1)]
253+
if heuristic:
254+
labels = [
255+
'{order}\n{g}+{h}={f}'.format(
256+
order=i,
257+
g=costs[s],
258+
h=heuristic[s],
259+
f=costs[s]+heuristic[s])
260+
for i, s in enumerate(explored, start=1)]
261+
else:
262+
labels = [
263+
'{order}\nc={cost}'.format(order=i, cost=costs[s])
264+
for i, s in enumerate(explored, start=1)]
256265
else:
257266
labels = [str(i) for i in range(1,len(explored)+1)]
258267
for label, x, y in zip(labels, xs, ys):
@@ -269,6 +278,14 @@ def show_search_tree(tree, fringe, explored, costs=None):
269278
labels = [
270279
'{order}\nc={cost}'.format(order='?', cost=costs[s])
271280
for s in fringe]
281+
if heuristic:
282+
labels = [
283+
'{order}\n{g}+{h}={f}'.format(
284+
order='?',
285+
g=costs[s],
286+
h=heuristic[s],
287+
f=costs[s]+heuristic[s])
288+
for s in fringe]
272289
for label, x, y in zip(labels, xs, ys):
273290
plt.text(
274291
x, y, label,
@@ -280,7 +297,8 @@ def show_search_tree(tree, fringe, explored, costs=None):
280297

281298

282299
def create_tree_search_widget(explored_states, trees, fringes,
283-
costs=None, interactive=False):
300+
costs=None, heuristic=None,
301+
interactive=False):
284302
if not trees:
285303
print('Zadne stromy k zobrazeni.')
286304
return
@@ -292,6 +310,7 @@ def show_search_tree_at(step):
292310
tree,
293311
fringe=fringe,
294312
costs=costs[step] if costs else None,
313+
heuristic=heuristic[step] if heuristic else None,
295314
explored=explored_states[:step])
296315

297316
if interactive:
@@ -313,17 +332,20 @@ def set_output(self, text=False, widget=False):
313332
self.output_text = text
314333
self.output_widget = widget
315334

316-
def start_search(self, state, costs=False):
335+
def start_search(self, state, costs=False, heuristic=False):
317336
self.explored_states = []
318337
self.trees = [{state: None}]
319338
self.fringes = [set([state])]
320339
self.costs = [{state: 0}] if costs else None
340+
# TODO: fix hodnota heuristiky v pocatecnim stavu
341+
# (neni jasne jak, mozna post-fix po prvnim zalognuti)
342+
self.heuristic = [{state: 0}] if heuristic else None
321343
self.log('start search')
322344

323345
def end_search(self, interactive=False):
324346
create_tree_search_widget(
325347
self.explored_states, self.trees, self.fringes,
326-
costs=self.costs,
348+
costs=self.costs, heuristic=self.heuristic,
327349
interactive=interactive)
328350

329351
def log(self, message):
@@ -332,7 +354,7 @@ def log(self, message):
332354
print('{step}: {message}'.format(
333355
step=step, message=message))
334356

335-
def log_search_step(self, explored_state, fringe, costs=None):
357+
def log_search_step(self, explored_state, fringe, costs=None, heuristic=None):
336358
# TODO: check type of states from fringe
337359
fringe = set(state for state in fringe)
338360
last_tree = self.trees[-1]
@@ -350,18 +372,23 @@ def log_search_step(self, explored_state, fringe, costs=None):
350372
if costs is None:
351373
raise ValueError('Zadejte i ceny stavu.')
352374
self.costs.append(deepcopy(costs))
375+
if self.heuristic:
376+
if heuristic is None:
377+
raise ValueError('Zadejte i heuristiky.')
378+
self.heuristic.append(deepcopy(heuristic))
353379
self.log(str(fringe))
354380

355381

356382
LOGGER = Logger()
357383
LOGGER.set_output(text=False, widget=True)
358384

359385
@contextmanager
360-
def visualize_search(state, interactive=False, costs=False):
361-
LOGGER.start_search(state, costs=costs)
386+
def visualize_search(state, interactive=False, costs=False, heuristic=False):
387+
LOGGER.start_search(state, costs=costs, heuristic=heuristic)
362388
yield
363389
LOGGER.end_search(interactive=interactive)
364390

365391

366-
def log_search_step(explored_state, fringe, costs=None):
367-
LOGGER.log_search_step(explored_state, fringe, costs=costs)
392+
def log_search_step(explored_state, fringe, costs=None, heuristic=None):
393+
LOGGER.log_search_step(
394+
explored_state, fringe, costs=costs, heuristic=heuristic)

solutions.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,88 @@ def ucs(initial_state):
8383
costs[next_state] = new_cost
8484
plans[next_state] = plans[state] + action
8585
log_search_step(state, fringe, costs)
86+
87+
88+
def heuristic_distance(state):
89+
# Cilovy radek ma hodnotu 0, radek pod nim 1, atd.
90+
vertical_distance = state.spaceship.row
91+
# Jaka by byla cena, kdyby raketka mohla letet porad rovne.
92+
return vertical_distance * ACTION_COSTS['f']
93+
94+
95+
def a_star(initial_state):
96+
fringe = {initial_state}
97+
costs = {initial_state: 0}
98+
heuristic = {initial_state: heuristic_distance(initial_state)}
99+
plans = {initial_state: ''}
100+
while fringe:
101+
state = min(fringe, key=lambda s: costs[s] + heuristic[s])
102+
fringe.remove(state)
103+
if is_goal(state):
104+
log_search_step(state, fringe, costs, heuristic)
105+
return plans[state]
106+
for action in actions(state):
107+
next_state = move(state, action)
108+
new_cost = costs[state] + ACTION_COSTS[action]
109+
old_cost = costs.get(next_state, inf)
110+
if new_cost < old_cost:
111+
fringe.add(next_state)
112+
costs[next_state] = new_cost
113+
plans[next_state] = plans[state] + action
114+
if next_state not in heuristic:
115+
heuristic[next_state] = heuristic_distance(next_state)
116+
log_search_step(state, fringe, costs, heuristic)
117+
118+
119+
# ----------------------------------------------------------------------------
120+
121+
# Obecne schema stromoveho prohledavani.
122+
# Je parametrizovane typem okraje (Fringe), ktery
123+
# popisuje strategii pro vyber stavu k prozkoumani.
124+
def tree_search(initial_state, Fringe=set):
125+
fringe = Fringe([initial_state])
126+
plans = {initial_state: ''}
127+
while fringe:
128+
# Vyber jednoho stavu z okraje.
129+
state = fringe.pop()
130+
# Pokud je tento stav cilovy, muzeme prohledavani ukoncit.
131+
if is_goal(state):
132+
log_search_step(state, fringe)
133+
return plans[state]
134+
# Pokud neni, expandujeme tento stav, tj. pridame na okraj
135+
# vsechny jeho nasledniky.
136+
for action in actions(state):
137+
next_state = move(state, action)
138+
plans[next_state] = plans[state] + action
139+
fringe.add(next_state)
140+
log_search_step(state, fringe)
141+
142+
143+
# Rekurzivni DFS pro stromy (nehlida zacykleni)
144+
def recursive_dfs(state):
145+
if is_goal(state):
146+
return [state]
147+
for action in actions(state):
148+
next_state = move(state, action)
149+
path = recursive_dfs(next_state)
150+
if path:
151+
return [state] + path
152+
return None # no path found
153+
154+
155+
# Rekurzivni DFS pro grafy (hlida zacykleni)
156+
def recursive_graph_dfs(start_state):
157+
explored = set()
158+
def dfs(state):
159+
explored.add(state)
160+
if is_goal(state):
161+
return [state]
162+
for action in actions(state):
163+
next_state = move(state, action)
164+
if next_state in explored:
165+
continue
166+
path = dfs(next_state)
167+
if path:
168+
return [state] + path
169+
return None # no path found
170+
return dfs(start_state)

0 commit comments

Comments
 (0)