5
5
sys .path .append (os .path .join (os .path .dirname (__file__ ), '..' ))
6
6
from search import *
7
7
from 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
9
9
from utils import Stack , FIFOQueue , PriorityQueue
10
10
from copy import deepcopy
11
11
@@ -163,7 +163,7 @@ def create_map(root):
163
163
romania_locations ['Pitesti' ][0 ],
164
164
height -
165
165
romania_locations ['Pitesti' ][1 ],
166
- romania_map .get ('Bucharest' , 'Pitesti' ))
166
+ romania_map .get ('Bucharest' , 'Pitesti' ))
167
167
make_line (
168
168
city_map ,
169
169
romania_locations ['Fagaras' ][0 ],
@@ -284,7 +284,7 @@ def make_rectangle(map, x0, y0, margin, city_name):
284
284
y0 - 2 * margin ,
285
285
text = city_name ,
286
286
anchor = E )
287
- else :
287
+ else :
288
288
map .create_text (
289
289
x0 - 2 * margin ,
290
290
y0 - 2 * margin ,
@@ -446,7 +446,7 @@ def breadth_first_search(problem):
446
446
node = frontier .pop ()
447
447
display_current (node )
448
448
explored .add (node .state )
449
- if counter % 3 == 1 and counter >= 0 :
449
+ if counter % 3 == 1 and counter >= 0 :
450
450
for child in node .expand (problem ):
451
451
if child .state not in explored and child not in frontier :
452
452
if problem .goal_test (child .state ):
@@ -466,9 +466,55 @@ def depth_first_graph_search(problem):
466
466
return graph_search (problem )
467
467
468
468
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
+
469
515
# TODO:
470
516
# 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.
472
518
def on_click ():
473
519
'''
474
520
This function defines the action of the 'Next' button.
@@ -507,6 +553,14 @@ def on_click():
507
553
display_final (final_path )
508
554
next_button .config (state = "disabled" )
509
555
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
510
564
511
565
512
566
def reset_map ():
@@ -532,9 +586,10 @@ def main():
532
586
goal .set ('Bucharest' )
533
587
cities = sorted (romania_map .locations .keys ())
534
588
algorithm_menu = OptionMenu (
535
- root ,
589
+ root ,
536
590
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" )
538
593
Label (root , text = "\n Search Algorithm" ).pack ()
539
594
algorithm_menu .pack ()
540
595
Label (root , text = "\n Start City" ).pack ()
0 commit comments