artificial intelligence hw

profileliyuan25
a2-starter-code.tar

a2-starter-code/Int_Solv_Client.py

#!/usr/bin/python3 """Int_Solv_Client.py This file implements a simple interactive problem-solving client. The user can interactively explore a problem space for a suitably-formulated problem. The user only has to input single-character commands to control the search. Output is purely textual, and thus the complexity of a graphical interface is avoided. This client runs standalone -- no server connection. It thus provides a bare-bones means of testing a problem formulation. ---- PURPOSE OF THIS MODULE: This module supports what we can call "interactive state space search". Whereas traditional search algorithms in the context of artificial intelligence work completely automatically, this module lets the user make the moves. It provides support to the user in terms of computing new states, displaying that portion of the state space that the user has embodied, and providing controls to permit the user to adapt the presentation to his or her needs. This type of tool could ultimately be a powerful problem solving tool, useful in several different modes of use: interactive processing of individual objects, programming by demonstration (the path from the root to any other node in the state space represents a way of processing any object similar in structure to that of the root object.) """ def mainloop(): print(TITLE) print(PROBLEM.PROBLEM_NAME+"; "+PROBLEM.PROBLEM_VERSION) global STEP, DEPTH, OPERATORS, CURRENT_STATE, STATE_STACK CURRENT_STATE = PROBLEM.CREATE_INITIAL_STATE() STATE_STACK = [CURRENT_STATE] STEP = 0 DEPTH = 0 while(True): print("\nStep "+str(STEP)+", Depth "+str(DEPTH)) print("CURRENT_STATE = "+str(CURRENT_STATE)) if PROBLEM.goal_test(CURRENT_STATE): print('''CONGRATULATIONS! You have solved the problem by reaching a goal state. Do you wish to continue exploring? ''') answer = input("Y or N? >> ") if answer=="Y" or answer=="y": print("OK, continue") else: return applicability_vector = get_applicability_vector(CURRENT_STATE) #print("applicability_vector = "+str(applicability_vector)) for i in range(len(OPERATORS)): if applicability_vector[i]: print(str(i)+": "+OPERATORS[i].name) command = input("Enter command: 0, 1, 2, etc. for operator; B-back; H-help; Q-quit. >> ") if command=="B" or command=="b": if len(STATE_STACK)>1: STATE_STACK.pop() DEPTH -= 1 STEP += 1 else: print("You're already back at the initial state.") CURRENT_STATE = STATE_STACK[-1] continue if command=="H" or command=="h": show_instructions(); continue if command=="Q" or command=="q": break if command=="": continue try: i = int(command) except: print("Unknown command or bad operator number.") continue print("Operator "+str(i)+" selected.") if i<0 or i>= len(OPERATORS): print("There is no operator with number "+str(i)) continue if applicability_vector[i]: CURRENT_STATE = OPERATORS[i].apply(CURRENT_STATE) STATE_STACK.append(CURRENT_STATE) DEPTH += 1 STEP += 1 continue else: print("Operator "+str(i)+" is not applicable to the current state.") continue #print("Operator "+command+" not yet supported.") def get_applicability_vector(s): #print("OPERATORS: "+str(OPERATORS)) return [op.is_applicable(s) for op in OPERATORS] def exit_client(): print("Terminating Text_SOLUZION_Client session.") log("Exiting") exit() def show_instructions(): print('''\nINSTRUCTIONS:\n The current state of your problem session represents where you are in the problem-solving process. You can try to progress forward by applying an operator to change the state. To do this, type the number of an applicable operator. The program shows you a list of what operators are applicable in the current state. You can also go backwards (undoing a previous step) by typing 'B'. If you reach a goal state, you have solved the problem, and the computer will usually tell you that, but it depends on what kind of problem you are solving.''') def apply_one_op(): """Populate a popup menu with the names of currently applicable operators, and let the user choose which one to apply.""" currently_applicable_ops = applicable_ops(CURRENT_STATE) #print "Applicable operators: ",\ # map(lambda o: o.name, currently_applicable_ops) print("Now need to apply the op") def applicable_ops(s): """Returns the subset of OPERATORS whose preconditions are satisfied by the state s.""" return [o for o in OPERATORS if o.is_applicable(s)] import sys, importlib.util # Get the PROBLEM name from the command-line arguments if len(sys.argv)<2: print(''' Usage: ./Int_Solv_Client <PROBLEM NAME> For example: ./Int_Solv_Client Missionaries ''') exit(1) problem_name = sys.argv[1] print("problem_name = "+problem_name) try: spec = importlib.util.spec_from_file_location(problem_name, problem_name+".py") PROBLEM = spec.loader.load_module() except Exception as e: print(e) exit(1) OPERATORS=PROBLEM.OPERATORS STATE_STACK = [] TITLE="Int_Solv_Client (Version 1)" # The following is only executed if this module is being run as the main # program, rather than imported from another one. if __name__ == '__main__': mainloop() print("The session is finished.")

a2-starter-code/TowersOfHanoi.py

'''TowersOfHanoi.py ''' #<METADATA> QUIET_VERSION = "0.2" PROBLEM_NAME = "Towers of Hanoi" PROBLEM_VERSION = "0.2" PROBLEM_AUTHORS = ['S. Tanimoto'] PROBLEM_CREATION_DATE = "7-JAN-2018" PROBLEM_DESC=\ '''This formulation of the Towers of Hanoi problem uses generic Python 3 constructs and has been tested with Python 3.6. It is designed to work according to the QUIET2 tools interface. ''' #</METADATA> #<COMMON_DATA> N_disks = 4 # Use default, but override if new value supplied # by the user on the command line. try: import sys arg2 = sys.argv[2] N_disks = int(arg2) print("Number of disks is "+arg2) except: print("Using default number of disks: "+str(N_disks)) print(" (To use a specific number, enter it on the command line, e.g.,") print("python3 ../Int_Solv_Client.py TowersOfHanoi 3") #</COMMON_DATA> #<COMMON_CODE> class State: def __init__(self, d): self.d = d def __eq__(self,s2): for p in ['peg1','peg2','peg3']: if self.d[p] != s2.d[p]: return False return True def __str__(self): # Produces a textual description of a state. # Might not be needed in normal operation with GUIs. txt = "[" for peg in ['peg1','peg2','peg3']: txt += str(self.d[peg]) + " ," return txt[:-2]+"]" def __hash__(self): return (self.__str__()).__hash__() def copy(self): # Performs an appropriately deep copy of a state, # for use by operators in creating new states. news = State({}) for peg in ['peg1', 'peg2', 'peg3']: news.d[peg]=self.d[peg][:] return news def can_move(self,From,To): '''Tests whether it's legal to move a disk in state s from the From peg to the To peg.''' try: pf=self.d[From] # peg disk goes from pt=self.d[To] # peg disk goes to if pf==[]: return False # no disk to move. df=pf[-1] # get topmost disk at From peg.. if pt==[]: return True # no disk to worry about at To peg. dt=pt[-1] # get topmost disk at To peg. if df<dt: return True # Disk is smaller than one it goes on. return False # Disk too big for one it goes on. except (Exception) as e: print(e) def move(self,From,To): '''Assuming it's legal to make the move, this computes the new state resulting from moving the topmost disk from the From peg to the To peg.''' news = self.copy() # start with a deep copy. pf=self.d[From] # peg disk goes from. pt=self.d[To] df=pf[-1] # the disk to move. news.d[From]=pf[:-1] # remove it from its old peg. news.d[To]=pt[:]+[df] # Put disk onto destination peg. return news # return new state def goal_test(s): '''If the first two pegs are empty, then s is a goal state.''' return s.d['peg1']==[] and s.d['peg2']==[] def goal_message(s): return "The Tower Transport is Triumphant!" class Operator: def __init__(self, name, precond, state_transf): self.name = name self.precond = precond self.state_transf = state_transf def is_applicable(self, s): return self.precond(s) def apply(self, s): return self.state_transf(s) #</COMMON_CODE> #<INITIAL_STATE> INITIAL_DICT = {'peg1': list(range(N_disks,0,-1)), 'peg2':[], 'peg3':[] } CREATE_INITIAL_STATE = lambda: State(INITIAL_DICT) #DUMMY_STATE = {'peg1':[], 'peg2':[], 'peg3':[] } #</INITIAL_STATE> #<OPERATORS> peg_combinations = [('peg'+str(a),'peg'+str(b)) for (a,b) in [(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]] OPERATORS = [Operator("Move disk from "+p+" to "+q, lambda s,p1=p,q1=q: s.can_move(p1,q1), # The default value construct is needed # here to capture the values of p&q separately # in each iteration of the list comp. iteration. lambda s,p1=p,q1=q: s.move(p1,q1) ) for (p,q) in peg_combinations] #</OPERATORS> #<GOAL_TEST> (optional) GOAL_TEST = lambda s: goal_test(s) #</GOAL_TEST> #<GOAL_MESSAGE_FUNCTION> (optional) GOAL_MESSAGE_FUNCTION = lambda s: goal_message(s) #</GOAL_MESSAGE_FUNCTION>

a2-starter-code/ItrDFS.py

#!/usr/bin/python3 ''' ItrDFS.py Iterative Depth-First Search of a problem space. Version 0.4, January 15, 2018. Steve Tanimoto, Univ. of Washington. Paul G. Allen School of Computer Science and Engineering Usage: python3 ItrDFS.py TowersOfHanoi # The numbered STEP comments in the function IterativeDFS correspond to the algorithm steps for iterative depth-first as presented in Slide 7 of the "Basic Search Algorithms" lecture. ''' import sys if sys.argv==[''] or len(sys.argv)<2: # import EightPuzzle as Problem import TowersOfHanoi as Problem else: import importlib Problem = importlib.import_module(sys.argv[1]) print("\nWelcome to ItrDFS") COUNT = None BACKLINKS = {} def runDFS(): initial_state = Problem.CREATE_INITIAL_STATE() print("Initial State:") print(initial_state) global COUNT, BACKLINKS, MAX_OPEN_LENGTH COUNT = 0 BACKLINKS = {} MAX_OPEN_LENGTH = 0 IterativeDFS(initial_state) print(str(COUNT)+" states expanded.") print('MAX_OPEN_LENGTH = '+str(MAX_OPEN_LENGTH)) def IterativeDFS(initial_state): global COUNT, BACKLINKS, MAX_OPEN_LENGTH # STEP 1. Put the start state on a list OPEN OPEN = [initial_state] CLOSED = [] BACKLINKS[initial_state] = None # STEP 2. If OPEN is empty, output “DONE” and stop. while OPEN != []: report(OPEN, CLOSED, COUNT) if len(OPEN)>MAX_OPEN_LENGTH: MAX_OPEN_LENGTH = len(OPEN) # STEP 3. Select the first state on OPEN and call it S. # Delete S from OPEN. # Put S on CLOSED. # If S is a goal state, output its description S = OPEN.pop(0) CLOSED.append(S) if Problem.GOAL_TEST(S): print(Problem.GOAL_MESSAGE_FUNCTION(S)) path = backtrace(S) print('Length of solution path found: '+str(len(path)-1)+' edges') return COUNT += 1 # STEP 4. Generate the list L of successors of S and delete # from L those states already appearing on CLOSED. L = [] for op in Problem.OPERATORS: if op.precond(S): new_state = op.state_transf(S) if not (new_state in CLOSED): L.append(new_state) BACKLINKS[new_state] = S # STEP 5. Delete from OPEN any members of OPEN that occur on L. # Insert all members of L at the front of OPEN. for s2 in L: for i in range(len(OPEN)): if (s2 == OPEN[i]): del OPEN[i]; break OPEN = L + OPEN print_state_list("OPEN", OPEN) # STEP 6. Go to Step 2. def print_state_list(name, lst): print(name+" is now: ",end='') for s in lst[:-1]: print(str(s),end=', ') print(str(lst[-1])) def backtrace(S): global BACKLINKS path = [] while S: path.append(S) S = BACKLINKS[S] path.reverse() print("Solution path: ") for s in path: print(s) return path def report(open, closed, count): print("len(OPEN)="+str(len(open)), end='; ') print("len(CLOSED)="+str(len(closed)), end='; ') print("COUNT = "+str(count)) if __name__=='__main__': runDFS()

a2-starter-code/Missionaries.py

'''Missionaries.py ("Missionaries and Cannibals" problem) ''' #<METADATA> SOLUZION_VERSION = "2.0" PROBLEM_NAME = "Missionaries and Cannibals" PROBLEM_VERSION = "2.0" PROBLEM_AUTHORS = ['S. Tanimoto'] PROBLEM_CREATION_DATE = "07-JAN-2018" # The following field is mainly for the human solver, via either the Text_SOLUZION_Client. # or the SVG graphics client. PROBLEM_DESC=\ '''The <b>"Missionaries and Cannibals"</b> problem is a traditional puzzle in which the player starts off with three missionaries and three cannibals on the left bank of a river. The object is to execute a sequence of legal moves that transfers them all to the right bank of the river. In this version, there is a boat that can carry at most three people, and one of them must be a missionary to steer the boat. It is forbidden to ever have one or two missionaries outnumbered by cannibals, either on the left bank, right bank, or in the boat. In the formulation presented here, the computer will not let you make a move to such a forbidden situation, and it will only show you moves that could be executed "safely." ''' #</METADATA> #<COMMON_DATA> #</COMMON_DATA> #<COMMON_CODE> M=0 # array index to access missionary counts C=1 # same idea for cannibals LEFT=0 # same idea for left side of river RIGHT=1 # etc. class State(): def __init__(self, d=None): if d==None: d = {'people':[[0,0],[0,0]], 'boat':LEFT} self.d = d def __eq__(self,s2): for prop in ['people', 'boat']: if self.d[prop] != s2.d[prop]: return False return True def __str__(self): # Produces a textual description of a state. p = self.d['people'] txt = "\n M on left:"+str(p[M][LEFT])+"\n" txt += " C on left:"+str(p[C][LEFT])+"\n" txt += " M on right:"+str(p[M][RIGHT])+"\n" txt += " C on right:"+str(p[C][RIGHT])+"\n" side='left' if self.d['boat']==1: side='right' txt += " boat is on the "+side+".\n" return txt def __hash__(self): return (self.__str__()).__hash__() def copy(self): # Performs an appropriately deep copy of a state, # for use by operators in creating new states. news = State({}) news.d['people']=[self.d['people'][M_or_C][:] for M_or_C in [M, C]] news.d['boat'] = self.d['boat'] return news def can_move(self,m,c): '''Tests whether it's legal to move the boat and take m missionaries and c cannibals.''' side = self.d['boat'] # Where the boat is. p = self.d['people'] if m<1: return False # Need an M to steer boat. m_available = p[M][side] if m_available < m: return False # Can't take more m's than available c_available = p[C][side] if c_available < c: return False # Can't take more c's than available m_remaining = m_available - m c_remaining = c_available - c # Missionaries must not be outnumbered on either side: if m_remaining > 0 and m_remaining < c_remaining: return False m_at_arrival = p[M][1-side]+m c_at_arrival = p[C][1-side]+c if m_at_arrival > 0 and m_at_arrival < c_at_arrival: return False return True def move(self,m,c): '''Assuming it's legal to make the move, this computes the new state resulting from moving the boat carrying m missionaries and c cannibals.''' news = self.copy() # start with a deep copy. side = self.d['boat'] # where is the boat? p = news.d['people'] # get the array of arrays of people. p[M][side] = p[M][side]-m # Remove people from the current side. p[C][side] = p[C][side]-c p[M][1-side] = p[M][1-side]+m # Add them at the other side. p[C][1-side] = p[C][1-side]+c news.d['boat'] = 1-side # Move the boat itself. return news def goal_test(s): '''If all Ms and Cs are on the right, then s is a goal state.''' p = s.d['people'] return (p[M][RIGHT]==3 and p[C][RIGHT]==3) def goal_message(s): return "Congratulations on successfully guiding the missionaries and cannibals across the river!" class Operator: def __init__(self, name, precond, state_transf): self.name = name self.precond = precond self.state_transf = state_transf def is_applicable(self, s): return self.precond(s) def apply(self, s): return self.state_transf(s) #</COMMON_CODE> #<INITIAL_STATE> CREATE_INITIAL_STATE = lambda : State(d={'people':[[3, 0], [3, 0]], 'boat':LEFT }) #</INITIAL_STATE> #<OPERATORS> MC_combinations = [(1,0),(2,0),(3,0),(1,1),(2,1)] OPERATORS = [Operator( "Cross the river with "+str(m)+" missionaries and "+str(c)+" cannibals", lambda s, m1=m, c1=c: s.can_move(m1,c1), lambda s, m1=m, c1=c: s.move(m1,c1) ) for (m,c) in MC_combinations] #</OPERATORS> #<GOAL_TEST> (optional) GOAL_TEST = lambda s: goal_test(s) #</GOAL_TEST> #<GOAL_MESSAGE_FUNCTION> (optional) GOAL_MESSAGE_FUNCTION = lambda s: goal_message(s) #</GOAL_MESSAGE_FUNCTION>

a2-starter-code/FranceWithCosts.py

'''FranceWithCosts.py ("Route Planning in France" problem) ''' #<METADATA> SOLUZION_VERSION = "2.0" PROBLEM_NAME = "France-Trip Planning: Driving from Rennes to Avignon" PROBLEM_VERSION = "1.0" PROBLEM_AUTHORS = ['S. Tanimoto'] PROBLEM_CREATION_DATE = "23-JAN-2019" # The following field is mainly for the human solver, via either the Text_SOLUZION_Client. # or the SVG graphics client. PROBLEM_DESC=\ '''The <b>" France-Trip Planning"</b> problem is to find a shortest driving route from the city of Rennes to the city of Avignon, using the map data provided. ''' #</METADATA> #<COMMON_DATA> STARTING_CITY = "Rennes" DESTINATION_CITY = "Avignon" STATES = {} ADJ = {} ADJ['Brest'] = ['Rennes'] ADJ['Rennes'] = ['Caen','Paris','Brest','Nantes'] ADJ['Caen'] = ['Calais','Paris','Rennes'] ADJ['Calais'] = ['Nancy','Paris','Caen'] ADJ['Nancy'] = ['Strasbourg','Dijon','Paris','Calais'] ADJ['Strasbourg'] = ['Dijon','Nancy'] ADJ['Dijon'] = ['Strasbourg','Lyon','Paris','Nancy'] ADJ['Lyon'] = ['Grenoble','Avignon','Limoges','Dijon'] ADJ['Grenoble'] = ['Avignon','Lyon'] ADJ['Avignon'] = ['Grenoble','Marseille','Montpellier','Lyon'] ADJ['Marseille'] = ['Nice','Avignon'] ADJ['Nice'] = ['Marseille'] ADJ['Montpellier'] = ['Avignon','Toulouse'] ADJ['Toulouse'] = ['Montpellier','Bordeaux','Limoges'] ADJ['Bordeaux'] = ['Limoges','Toulouse','Nantes'] ADJ['Limoges'] = ['Lyon','Toulouse','Bordeaux','Nantes','Paris'] ADJ['Nantes'] = ['Limoges','Bordeaux','Rennes'] ADJ['Paris'] = ['Calais','Nancy','Dijon','Limoges','Rennes','Caen'] DISTANCE = {} DISTANCE['Brest'] = {'Rennes':244} DISTANCE['Rennes'] = {'Caen':176,'Paris':348,'Brest':244,'Nantes':107} DISTANCE['Caen'] = {'Calais':120,'Paris':241,'Rennes':176} DISTANCE['Calais'] = {'Nancy':534,'Paris':297,'Caen':120} DISTANCE['Nancy'] = {'Strasbourg':145,'Dijon':201,'Paris':372,'Calais':534} DISTANCE['Strasbourg'] = {'Dijon':335,'Nancy':145} DISTANCE['Dijon'] = {'Strasbourg':335,'Lyon':192,'Paris':313,'Nancy':201} DISTANCE['Lyon'] = {'Grenoble':104,'Avignon':216,'Limoges':389,'Dijon':192} DISTANCE['Grenoble'] = {'Avignon':227,'Lyon':104} DISTANCE['Avignon'] = {'Grenoble':227,'Marseille':99,'Montpellier':212,'Lyon':216} DISTANCE['Marseille'] = {'Nice':188,'Avignon':99} DISTANCE['Nice'] = {'Marseille':188} DISTANCE['Montpellier'] = {'Avignon':212,'Toulouse':240} DISTANCE['Toulouse'] = {'Montpellier':240,'Bordeaux':253,'Limoges':313} DISTANCE['Bordeaux'] = {'Limoges':220,'Toulouse':253,'Nantes':329} DISTANCE['Limoges'] = {'Lyon':389,'Toulouse':313,'Bordeaux':220,'Nantes':329,'Paris':396} DISTANCE['Nantes'] = {'Limoges':329,'Bordeaux':329,'Rennes':107} DISTANCE['Paris'] = {'Calais':297,'Nancy':372,'Dijon':313,'Limoges':396,'Rennes':348,'Caen':241} #</COMMON_DATA> #<COMMON_CODE> class State(): def __init__(self, name="no name yet"): self.name = name def __eq__(self,s2): #print("In State.__eq__: s2 is ", str(s2)) return self.name==s2.name def __str__(self): return self.name def __hash__(self): return (self.__str__()).__hash__() def copy(self): # Performs an appropriately deep copy of a state, # for use by operators in creating new states. news = State() news.name = self.name return news def ith_neighbor_exists(self,i): '''Tests whether there are enough adjacent cities to go to the ith.''' return len(ADJ[self.name])>i def move(self,i): '''Assuming it's legal to transition to the ith neighbor, this does it.''' neighbor = STATES[ADJ[self.name][i]] return neighbor def edge_distance(self, s2): return DISTANCE[self.name][s2.name] def goal_test(s): return s.name==DESTINATION_CITY def goal_message(s): return "Congratulations on finding a route to Avignon!" class Operator: def __init__(self, name, precond, state_transf): self.name = name self.precond = precond self.state_transf = state_transf def is_applicable(self, s): return self.precond(s) def apply(self, s): return self.state_transf(s) def create_all_states(): for name in ADJ.keys(): STATES[name]=State(name) create_all_states() #</COMMON_CODE> #<INITIAL_STATE> CREATE_INITIAL_STATE = lambda : STATES[STARTING_CITY] #</INITIAL_STATE> #<OPERATORS> OPERATORS = [Operator( "Go to neighboring city number "+str(i), lambda s, i1=i: s.ith_neighbor_exists(i1), lambda s, i1=i: s.move(i1)) for i in range(6)] # Paris has the most neighbors (6) #</OPERATORS> #<GOAL_TEST> (optional) GOAL_TEST = lambda s: goal_test(s) #</GOAL_TEST> #<GOAL_MESSAGE_FUNCTION> (optional) GOAL_MESSAGE_FUNCTION = lambda s: goal_message(s) #</GOAL_MESSAGE_FUNCTION>

a2-starter-code/Farmer_Fox.py

'''Farmer_Fox.py by John Nathan Smith UWNetID: jnsmith98 Student number: 9876543 Assignment 2, in CSE 415, Autumn 2019. This file contains my problem formulation for the problem of the Farmer, Fox, Chicken, and Grain. ''' # Put your formulation of the Farmer-Fox-Chicken-and-Grain problem here. # Be sure your name, uwnetid, and 7-digit student number are given above in # the format shown. pass # replace this.

a2-starter-code/starter-file-for-UCS.py

''' starter-file-for.py CHANGE THIS WHEN YOU EDIT IT, TO UCS.py by (YOUR NAME HERE) PUT YOUR ADDITIONAL COMMENTS HERE, SUCH AS EMAIL ADDRESS, VERSION INFO, ETC> STUDENTS: COMPLETE THE IMPLEMENTATION OF UCS.py BY WRITING CODE WHERE INDICATED BELOW IN THE COMMENTS STARTING WITH "***STUDENTS". This file Includes a priority queue implementation by S. Tanimoto, Univ. of Washington. Paul G. Allen School of Computer Science and Engineering Intended USAGE: python3 UCS.py FranceWithCosts ''' VERBOSE = True # Set to True to see progress; but it slows the search. import sys if sys.argv==[''] or len(sys.argv)<2: try: import EightPuzzle as Problem except: print("Note that the EightPuzzle formulation will be used in Assignment 3, not Assignment 2") print("Try python3 UCS.py FranceWithCosts") else: import importlib Problem = importlib.import_module(sys.argv[1]) print("\nWelcome to UCS, by YOUR NAME HERE!") COUNT = None # Number of nodes expanded. MAX_OPEN_LENGTH = None # How long OPEN ever gets. SOLUTION_PATH = None # List of states from initial to goal, along lowest-cost path. TOTAL_COST = None # Sum of edge costs along the lowest-cost path. BACKLINKS = {} # Predecessor links, used to recover the path. # The value g(s) represents the cost along the best path found so far # from the initial state to state s. g = {} # We will use a global hash table to associate g values with states. class My_Priority_Queue: def __init__(self): self.q = [] # Actual data goes in a list. def __contains__(self, elt): '''If there is a (state, priority) pair on the list where state==elt, then return True.''' #print("In My_Priority_Queue.__contains__: elt= ", str(elt)) for pair in self.q: if pair[0]==elt: return True return False def delete_min(self): ''' Standard priority-queue dequeuing method.''' if self.q==[]: return [] # Simpler than raising an exception. temp_min_pair = self.q[0] temp_min_value = temp_min_pair[1] temp_min_position = 0 for j in range(1, len(self.q)): if self.q[j][1] < temp_min_value: temp_min_pair = self.q[j] temp_min_value = temp_min_pair[1] temp_min_position = j del self.q[temp_min_position] return temp_min_pair def insert(self, state, priority): '''We do not keep the list sorted, in this implementation.''' #print("calling insert with state, priority: ", state, priority) if self[state] != -1: print("Error: You're trying to insert an element into a My_Priority_Queue instance,") print(" but there is already such an element in the queue.") return self.q.append((state, priority)) def __len__(self): '''We define length of the priority queue to be the length of its list.''' return len(self.q) def __getitem__(self, state): '''This method enables Pythons right-bracket syntax. Here, something like priority_val = my_queue[state] becomes possible. Note that the syntax is actually used in the insert method above: self[state] != -1 ''' for (S,P) in self.q: if S==state: return P return -1 # This value means not found. def __delitem__(self, state): '''This method enables Python's del operator to delete items from the queue.''' #print("In MyPriorityQueue.__delitem__: state is: ", str(state)) for count, (S,P) in enumerate(self.q): if S==state: del self.q[count] return def __str__(self): txt = "My_Priority_Queue: [" for (s,p) in self.q: txt += '('+str(s)+','+str(p)+') ' txt += ']' return txt def runUCS(): '''This is an encapsulation of some setup before running UCS, plus running it and then printing some stats.''' initial_state = Problem.CREATE_INITIAL_STATE() print("Initial State:") print(initial_state) global COUNT, BACKLINKS, MAX_OPEN_LENGTH, SOLUTION_PATH COUNT = 0 BACKLINKS = {} MAX_OPEN_LENGTH = 0 SOLUTION_PATH = UCS(initial_state) print(str(COUNT)+" states expanded.") print('MAX_OPEN_LENGTH = '+str(MAX_OPEN_LENGTH)) def UCS(initial_state): '''Uniform Cost Search. This is the actual algorithm.''' global g, COUNT, BACKLINKS, MAX_OPEN_LENGTH, CLOSED, TOTAL_COST CLOSED = [] BACKLINKS[initial_state] = None # The "Step" comments below help relate UCS's implementation to # those of Depth-First Search and Breadth-First Search. # STEP 1a. Put the start state on a priority queue called OPEN OPEN = My_Priority_Queue() OPEN.insert(initial_state, 0) # STEP 1b. Assign g=0 to the start state. g[initial_state]=0.0 # STEP 2. If OPEN is empty, output “DONE” and stop. while False: # ***STUDENTS CHANGE THIS CONDITION*** # LEAVE THE FOLLOWING CODE IN PLACE TO INSTRUMENT AND/OR DEBUG YOUR IMPLEMENTATION if VERBOSE: report(OPEN, CLOSED, COUNT) if len(OPEN)>MAX_OPEN_LENGTH: MAX_OPEN_LENGTH = len(OPEN) # STEP 3. Select the state on OPEN having lowest priority value and call it S. # Delete S from OPEN. # Put S on CLOSED. # If S is a goal state, output its description (S,P) = OPEN.delete_min() #print("In Step 3, returned from OPEN.delete_min with results (S,P)= ", (str(S), P)) CLOSED.append(S) if Problem.GOAL_TEST(S): pass # ***STUDENTS CHANGE THE BODY OF THIS IF.*** #HANDLE THE BACKTRACING, RECORDING THE SOLUTION AND TOTAL COST, # AND RETURN THE SOLUTION PATH, TOO. COUNT += 1 # STEP 4. Generate each successor of S # and if it is already on CLOSED, delete the new instance. # ***STUDENTS IMPLEMENT THE GENERATION AND HANDLING OF SUCCESSORS HERE, # USING THE GIVEN PRIORITY QUEUE FOR THE OPEN LIST, AND # DETERMINING THE SHORTEST DISTANCE KNOWN SO FAR FROM THE INITIAL STATE TO # EACH SUCCESSOR.*** # STEP 6. Go to Step 2. return None # No more states on OPEN, and no goal reached. def print_state_queue(name, q): print(name+" is now: ",end='') print(str(q)) def backtrace(S): global BACKLINKS path = [] while S: path.append(S) S = BACKLINKS[S] path.reverse() print("Solution path: ") for s in path: print(s) return path def report(open, closed, count): print("len(OPEN)="+str(len(open)), end='; ') print("len(CLOSED)="+str(len(closed)), end='; ') print("COUNT = "+str(count)) if __name__=='__main__': runUCS()