55sys .path .append (os .path .join (os .path .dirname (__file__ ), '..' ))
66from search import *
77from search import breadth_first_tree_search as bfts , depth_first_tree_search as dfts , \
8- depth_first_graph_search as dfgs , breadth_first_search as bfs
8+ depth_first_graph_search as dfgs , breadth_first_search as bfs , uniform_cost_search as ucs
99from utils import Stack , FIFOQueue , PriorityQueue
1010from copy import deepcopy
1111
@@ -163,7 +163,7 @@ def create_map(root):
163163 romania_locations ['Pitesti' ][0 ],
164164 height -
165165 romania_locations ['Pitesti' ][1 ],
166- romania_map .get ('Bucharest' , 'Pitesti' ))
166+ romania_map .get ('Bucharest' , 'Pitesti' ))
167167 make_line (
168168 city_map ,
169169 romania_locations ['Fagaras' ][0 ],
@@ -284,7 +284,7 @@ def make_rectangle(map, x0, y0, margin, city_name):
284284 y0 - 2 * margin ,
285285 text = city_name ,
286286 anchor = E )
287- else :
287+ else :
288288 map .create_text (
289289 x0 - 2 * margin ,
290290 y0 - 2 * margin ,
@@ -446,7 +446,7 @@ def breadth_first_search(problem):
446446 node = frontier .pop ()
447447 display_current (node )
448448 explored .add (node .state )
449- if counter % 3 == 1 and counter >= 0 :
449+ if counter % 3 == 1 and counter >= 0 :
450450 for child in node .expand (problem ):
451451 if child .state not in explored and child not in frontier :
452452 if problem .goal_test (child .state ):
@@ -466,9 +466,55 @@ def depth_first_graph_search(problem):
466466 return graph_search (problem )
467467
468468
469+ def best_first_graph_search (problem , f ):
470+ """Search the nodes with the lowest f scores first.
471+ You specify the function f(node) that you want to minimize; for example,
472+ if f is a heuristic estimate to the goal, then we have greedy best
473+ first search; if f is node.depth then we have breadth-first search.
474+ There is a subtlety: the line "f = memoize(f, 'f')" means that the f
475+ values will be cached on the nodes as they are computed. So after doing
476+ a best first search you can examine the f values of the path returned."""
477+ global frontier , node , explored , counter
478+
479+ if counter == - 1 :
480+ f = memoize (f , 'f' )
481+ node = Node (problem .initial )
482+ display_current (node )
483+ if problem .goal_test (node .state ):
484+ return node
485+ frontier = PriorityQueue (min , f )
486+ frontier .append (node )
487+ display_frontier (frontier )
488+ explored = set ()
489+ if counter % 3 == 0 and counter >= 0 :
490+ node = frontier .pop ()
491+ display_current (node )
492+ if problem .goal_test (node .state ):
493+ return node
494+ explored .add (node .state )
495+ if counter % 3 == 1 and counter >= 0 :
496+ for child in node .expand (problem ):
497+ if child .state not in explored and child not in frontier :
498+ frontier .append (child )
499+ elif child in frontier :
500+ incumbent = frontier [child ]
501+ if f (child ) < f (incumbent ):
502+ del frontier [incumbent ]
503+ frontier .append (child )
504+ display_frontier (frontier )
505+ if counter % 3 == 2 and counter >= 0 :
506+ display_explored (node )
507+ return None
508+
509+
510+ def uniform_cost_search (problem ):
511+ """[Figure 3.14]"""
512+ return best_first_graph_search (problem , lambda node : node .path_cost )
513+
514+
469515# TODO:
470516# Remove redundant code.
471- # Make the interchangbility work between various algorithms at each step.
517+ # Make the interchangbility work between various algorithms at each step.
472518def on_click ():
473519 '''
474520 This function defines the action of the 'Next' button.
@@ -507,6 +553,14 @@ def on_click():
507553 display_final (final_path )
508554 next_button .config (state = "disabled" )
509555 counter += 1
556+ elif "Uniform Cost Search" == algo .get ():
557+ node = uniform_cost_search (romania_problem )
558+ if node is not None :
559+ final_path = ucs (romania_problem ).solution ()
560+ final_path .append (start .get ())
561+ display_final (final_path )
562+ next_button .config (state = "disabled" )
563+ counter += 1
510564
511565
512566def reset_map ():
@@ -532,9 +586,10 @@ def main():
532586 goal .set ('Bucharest' )
533587 cities = sorted (romania_map .locations .keys ())
534588 algorithm_menu = OptionMenu (
535- root ,
589+ root ,
536590 algo , "Breadth-First Tree Search" , "Depth-First Tree Search" ,
537- "Breadth-First Search" , "Depth-First Graph Search" )
591+ "Breadth-First Search" , "Depth-First Graph Search" ,
592+ "Uniform Cost Search" )
538593 Label (root , text = "\n Search Algorithm" ).pack ()
539594 algorithm_menu .pack ()
540595 Label (root , text = "\n Start City" ).pack ()
0 commit comments