#

# Implementation of the Graph Search algorithm, for Stanford's online

# Intro to Artificial Intelligence course. October 16, 2011.

#

def main ( ) :

#A 3 level, 9 node tree to be expanded from left to right

MySearchTree = SearchTree ( "A" )

#tier 1

MySearchTree. addLeaf ( "B" , "A" , 1 )

MySearchTree. addLeaf ( "C" , "A" , 2 )

MySearchTree. addLeaf ( "D" , "A" , 3 )

#tier 2

MySearchTree. addLeaf ( "E" , "B" , 4 )

MySearchTree. addLeaf ( "F" , "B" , 5 )

MySearchTree. addLeaf ( "G" , "C" , 6 )

MySearchTree. addLeaf ( "H" , "C" , 7 )

MySearchTree. addLeaf ( "I" , "D" , 8 )

MySearchTree. addLeaf ( "J" , "D" , 9 )

print GraphSearch ( MySearchTree , UniformCost , "F" )

def GraphSearch ( problemTree , choiceFunction , goal ) :

'''

Graph Search algorithm implementation, takes

the problem's search tree object, the path selection function

and the name of the goal node

'''

frontier = [ ]

explored = [ ]

currentState = None

try :

if isinstance ( problemTree , SearchTree ) :

frontier. append ( [ problemTree ] )

else :

raise NameError

try :

while True :

if not ( len ( frontier ) ) :

print ( "Unable to find path to destination." )

return

path = frontier. pop ( frontier. index ( choiceFunction ( frontier ) ) )

currentState = path [ - 1 ]

explored. append ( currentState )

if currentState. name == goal:

pathToGoal = [ ]

for leaf in path:

pathToGoal. append ( leaf. name )

return pathToGoal

for action in currentState. children :

newPath = path [ : ]

if action not in explored:

newPath. append ( action )

if newPath not in frontier:

frontier. append ( newPath )

except ValueError :

print ( "Path choice function unable to determine next step. Search failed." )

except NameError :

print ( "Tree %s does not exist!" % problemTree )

def UniformCost ( searchFrontier ) :

'''

Uniform Cost algorithm selection function

'''

lowestCost = None

bestPath = None

for path in searchFrontier:

pathCost = 0

previousLeaf = None

for leaf in path:

if leaf. parents [ 0 ] != None : #make sure the leaf is not the root node

pathCost + = leaf. distanceToParents [ leaf. parents . index ( previousLeaf ) ]

previousLeaf = leaf

if lowestCost == None :

lowestCost = pathCost

bestPath = path

else :

if pathCost < lowestCost:

lowestCost = pathCost

bestPath = path

return bestPath

def BreadthFirst ( searchFrontier ) :

'''

Breadth First algorithm selection function

'''

lowestCost = None

bestPath = None

for path in searchFrontier:

pathCost = 0

if len ( path ) > 1 : #make sure root node not the only node in the path

# the path cost is just the distance between

# the last leaf on the frontier and the one before it

pathCost = path [ - 1 ] . distanceToParents [ path [ - 1 ] . parents . index ( path [ - 2 ] ) ] #before it

if lowestCost == None :

lowestCost = pathCost

bestPath = path

else :

if pathCost < lowestCost:

lowestCost = pathCost

bestPath = path

return bestPath

def Astar ( searchFrontier ) :

'''

A* algorithm selection function

'''

lowestCost = None

bestPath = None

for path in searchFrontier:

pathCost = 0

previousLeaf = None

for leaf in path:

if leaf. parents [ 0 ] != None : #make sure the leaf is not the root node

if leaf. distanceToGoal != None :

pathCost + = leaf. distanceToParents [ leaf. parents . index ( previousLeaf ) ] + leaf. distanceToGoal

else :

return None

previousLeaf = leaf

if lowestCost == None :

lowestCost = pathCost

bestPath = path

else :

if pathCost < lowestCost:

lowestCost = pathCost

bestPath = path

return bestPath

class Leaf ( object ) :

'''

Creates a Leaf object with a name, a parent leaf,

the distance to the parent, and an estimated distance

to the goal (for A* search, if desired)

'''

def __init__ ( self , leafName , leafParent , distanceToParent , distanceToGoal = None ) :

self . name = leafName

self . children = [ ]

self . parents = [ ]

self . distanceToParents = [ ]

self . distanceToGoal = distanceToGoal

self . parents . append ( leafParent )

self . distanceToParents . append ( distanceToParent )

print ( "Leaf named %s added to tree." % leafName )

def __del__ ( self ) :

print ( "Leaf %s is deleted." % self . name )

def printParents ( self ) :

leafList = [ ]

for leaf in self . parents :

leafList. append ( leaf. name )

print leafList

def printChildren ( self ) :

leafList = [ ]

for leaf in self . children :

leafList. append ( leaf. name )

print leafList

def printDistanceToParents ( self ) :

print self . distanceToParents

class SearchTree ( Leaf ) :

'''

Creates a SearchTree object with a root name

and an estimated distance to goal (for A* search,

if desired

'''

def __init__ ( self , rootName , distanceToGoal = None ) :

super ( SearchTree , self ) . __init__ ( rootName , None , distanceToGoal )

self . rootName = rootName

print ( "Tree named %s created." % rootName )

def addLeaf ( self , leafName , leafParent , distanceToParent ) :

if leafName == leafParent:

print ( "A leaf cannot be its own parent!" )

return

try :

#if the parent is the root and leaf is already in the tree,

#but not a child of the root

if getattr ( self , leafName ) not in self . children and leafParent == self . rootName :

setattr ( self , leafName , Leaf ( leafName , self , distanceToParent ) )

self . children . append ( getattr ( self , leafName ) )

else :

try :

#if the leaf is already in the tree, and the root is the parent

if getattr ( self , leafName ) in self . children and leafParent == self . rootName :

raise ValueError

#if the leaf is already in the tree, but the root is not the parent

elif getattr ( self , leafName ) not in getattr ( getattr ( self , leafParent ) , "children" ) :

setattr ( self , leafName , Leaf ( leafName , getattr ( self , leafParent ) , distanceToParent ) )

getattr ( getattr ( getattr ( self , leafParent ) , "children" ) , "append" ) ( getattr ( self , leafName ) )

else :

raise ValueError

except AttributeError :

print ( "Parent leaf %s is not in the tree!" % leafParent )

except ValueError :

print ( "Parent leaf %s already has a child named %s" % ( leafParent , leafName ) )

except AttributeError : # leaf is not in the tree

if leafParent == self . rootName : #if the parent is the root

setattr ( self , leafName , Leaf ( leafName , self , distanceToParent ) )

self . children . append ( getattr ( self , leafName ) )

else :

try : #if some other leaf is the parent

setattr ( self , leafName , Leaf ( leafName , getattr ( self , leafParent ) , distanceToParent ) )

getattr ( getattr ( getattr ( self , leafParent ) , "children" ) , "append" ) ( getattr ( self , leafName ) )

except AttributeError :

print ( "Parent leaf %s not in the tree!" % leafParent )

def deleteLeaf ( self , leafName ) :

try :

if leafName == self . rootName :

raise ValueError

else :

try :

for child in getattr ( getattr ( self , leafName ) , "children" ) :

child. distanceToParents . extend ( [ elem + child. distanceToParents [ child. parents . index ( getattr ( self , leafName ) ) ]

for elem in getattr ( getattr ( self , leafName ) , "distanceToParents" ) ] )

del child. distanceToParents [ child. parents . index ( getattr ( self , leafName ) ) ]

child. parents . remove ( getattr ( self , leafName ) )

child. parents . extend ( getattr ( getattr ( self , leafName ) , "parents" ) )

child. __del__ ( )

except AttributeError :

print ( "That leaf is not in the tree!" )

except ValueError :

print ( "Cannot delete root!" )

if __name__ == '__main__' :