Compare commits

...

2 Commits

2 changed files with 134 additions and 76 deletions

View File

@@ -74,7 +74,7 @@ def tinyMazeSearch(problem):
return [s, s, w, s, w, w, s, w]
def genericSearch(problem, costFunction):
def genericSearch(problem, getNewCostAndPriority):
fringe = util.PriorityQueue()
startState = problem.getStartState()
fringe.push((startState, [], 0), 0)
@@ -86,11 +86,11 @@ def genericSearch(problem, costFunction):
return actions
visited[state] = cost
for successor, action, stepCost in problem.getSuccessors(state):
newCost = costFunction(cost, stepCost)
if successor in visited and abs(visited[successor]) <= abs(newCost):
newCost, priority = getNewCostAndPriority(cost, stepCost, successor)
if successor in visited and visited[successor] <= newCost:
continue
newActions = list(actions) + [action]
fringe.push((successor, newActions, newCost), newCost)
fringe.push((successor, newActions, newCost), priority)
print("No path found.")
raise Exception()
@@ -102,24 +102,26 @@ def depthFirstSearch(problem):
Your search algorithm needs to return a list of actions that reaches the
goal. Make sure to implement a graph search algorithm.
"""
def costFunction(currentCost, stepCost):
return currentCost - 1
return genericSearch(problem, costFunction)
def getNewCostAndPriority(cost, stepCost, successor):
newCost = cost + 1
return newCost, -newCost
return genericSearch(problem, getNewCostAndPriority)
def breadthFirstSearch(problem):
"""Search the shallowest nodes in the search tree first."""
def costFunction(currentCost, stepCost):
return currentCost + 1
return genericSearch(problem, costFunction)
def getNewCostAndPriority(cost, stepCost, successor):
newCost = cost + 1
return newCost, newCost
return genericSearch(problem, getNewCostAndPriority)
def uniformCostSearch(problem):
"""Search the node of least total cost first."""
"*** YOUR CODE HERE ***"
def costFunction(currentCost, stepCost):
return currentCost + stepCost
return genericSearch(problem, costFunction)
def getNewCostAndPriority(cost, stepCost, successor):
newCost = cost + stepCost
return newCost, newCost
return genericSearch(problem, getNewCostAndPriority)
def nullHeuristic(state, problem=None):
@@ -133,9 +135,11 @@ def nullHeuristic(state, problem=None):
def aStarSearch(problem, heuristic=nullHeuristic):
"""Search the node that has the lowest combined cost and heuristic first."""
"*** YOUR CODE HERE ***"
def costFunction(currentCost, stepCost):
return currentCost + 1
return genericSearch(problem, costFunction)
def getNewCostAndPriority(cost, stepCost, successor):
newCost = cost + stepCost
newPriority = newCost + heuristic(successor, problem)
return newCost, newPriority
return genericSearch(problem, getNewCostAndPriority)
# Abbreviations

View File

@@ -41,6 +41,7 @@ import util
import time
import search
class GoWestAgent(Agent):
"An agent that goes West until it can't."
@@ -56,6 +57,7 @@ class GoWestAgent(Agent):
# after you fill in parts of search.py #
#######################################################
class SearchAgent(Agent):
"""
This very general search agent finds a path using a supplied search
@@ -90,7 +92,8 @@ class SearchAgent(Agent):
heur = getattr(search, heuristic)
else:
raise AttributeError, heuristic + ' is not a function in searchAgents.py or search.py.'
print('[SearchAgent] using function %s and heuristic %s' % (fn, heuristic))
print('[SearchAgent] using function %s and heuristic %s' %
(fn, heuristic))
# Note: this bit of Python trickery combines the search algorithm and the heuristic
self.searchFunction = lambda x: func(x, heuristic=heur)
@@ -109,13 +112,16 @@ class SearchAgent(Agent):
state: a GameState object (pacman.py)
"""
if self.searchFunction == None: raise Exception, "No search function provided for SearchAgent"
if self.searchFunction == None:
raise Exception, "No search function provided for SearchAgent"
starttime = time.time()
problem = self.searchType(state) # Makes a new search problem
self.actions = self.searchFunction(problem) # Find a path
totalCost = problem.getCostOfActions(self.actions)
print('Path found with total cost of %d in %.1f seconds' % (totalCost, time.time() - starttime))
if '_expanded' in dir(problem): print('Search nodes expanded: %d' % problem._expanded)
print('Path found with total cost of %d in %.1f seconds' %
(totalCost, time.time() - starttime))
if '_expanded' in dir(problem):
print('Search nodes expanded: %d' % problem._expanded)
def getAction(self, state):
"""
@@ -125,7 +131,8 @@ class SearchAgent(Agent):
state: a GameState object (pacman.py)
"""
if 'actionIndex' not in dir(self): self.actionIndex = 0
if 'actionIndex' not in dir(self):
self.actionIndex = 0
i = self.actionIndex
self.actionIndex += 1
if i < len(self.actions):
@@ -133,6 +140,7 @@ class SearchAgent(Agent):
else:
return Directions.STOP
class PositionSearchProblem(search.SearchProblem):
"""
A search problem defines the state space, start state, goal test, successor
@@ -154,7 +162,8 @@ class PositionSearchProblem(search.SearchProblem):
"""
self.walls = gameState.getWalls()
self.startState = gameState.getPacmanPosition()
if start != None: self.startState = start
if start != None:
self.startState = start
self.goal = goal
self.costFn = costFn
self.visualize = visualize
@@ -175,8 +184,10 @@ class PositionSearchProblem(search.SearchProblem):
self._visitedlist.append(state)
import __main__
if '_display' in dir(__main__):
if 'drawExpandedCells' in dir(__main__._display): #@UndefinedVariable
__main__._display.drawExpandedCells(self._visitedlist) #@UndefinedVariable
# @UndefinedVariable
if 'drawExpandedCells' in dir(__main__._display):
__main__._display.drawExpandedCells(
self._visitedlist) # @UndefinedVariable
return isGoal
@@ -215,17 +226,20 @@ class PositionSearchProblem(search.SearchProblem):
Returns the cost of a particular sequence of actions. If those actions
include an illegal move, return 999999.
"""
if actions == None: return 999999
if actions == None:
return 999999
x, y = self.getStartState()
cost = 0
for action in actions:
# Check figure out the next state and see whether its' legal
dx, dy = Actions.directionToVector(action)
x, y = int(x + dx), int(y + dy)
if self.walls[x][y]: return 999999
if self.walls[x][y]:
return 999999
cost += self.costFn((x, y))
return cost
class StayEastSearchAgent(SearchAgent):
"""
An agent for position search with a cost function that penalizes being in
@@ -233,10 +247,13 @@ class StayEastSearchAgent(SearchAgent):
The cost function for stepping into a position (x,y) is 1/2^x.
"""
def __init__(self):
self.searchFunction = search.uniformCostSearch
costFn = lambda pos: .5 ** pos[0]
self.searchType = lambda state: PositionSearchProblem(state, costFn, (1, 1), None, False)
def costFn(pos): return .5 ** pos[0]
self.searchType = lambda state: PositionSearchProblem(
state, costFn, (1, 1), None, False)
class StayWestSearchAgent(SearchAgent):
"""
@@ -245,17 +262,20 @@ class StayWestSearchAgent(SearchAgent):
The cost function for stepping into a position (x,y) is 2^x.
"""
def __init__(self):
self.searchFunction = search.uniformCostSearch
costFn = lambda pos: 2 ** pos[0]
def costFn(pos): return 2 ** pos[0]
self.searchType = lambda state: PositionSearchProblem(state, costFn)
def manhattanHeuristic(position, problem, info={}):
"The Manhattan distance heuristic for a PositionSearchProblem"
xy1 = position
xy2 = problem.goal
return abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1])
def euclideanHeuristic(position, problem, info={}):
"The Euclidean distance heuristic for a PositionSearchProblem"
xy1 = position
@@ -266,6 +286,7 @@ def euclideanHeuristic(position, problem, info={}):
# This portion is incomplete. Time to write code! #
#####################################################
class CornersProblem(search.SearchProblem):
"""
This search problem finds paths through all four corners of a layout.
@@ -287,22 +308,26 @@ class CornersProblem(search.SearchProblem):
self._expanded = 0 # DO NOT CHANGE; Number of search nodes expanded
# Please add any code here which you would like to use
# in initializing the problem
"*** YOUR CODE HERE ***"
def getStartState(self):
"""
Returns the start state (in your state space, not the full Pacman state
space)
"""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
current = self.startingPosition
visited = tuple([1 if corner == current else 0
for corner in self.corners])
return (self.startingPosition, visited)
def isGoalState(self, state):
"""
Returns whether this search state is a goal state of the problem.
"""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
position, visited = state
if sum(visited) == 4:
return True
return False
def getSuccessors(self, state):
"""
@@ -315,16 +340,26 @@ class CornersProblem(search.SearchProblem):
is the incremental cost of expanding to that successor
"""
position, visited = state
x, y = position
successors = []
for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
# Add a successor state to the successor list if the action is legal
# Here's a code snippet for figuring out whether a new position hits a wall:
# x,y = currentPosition
# dx, dy = Actions.directionToVector(action)
# nextx, nexty = int(x + dx), int(y + dy)
# hitsWall = self.walls[nextx][nexty]
"*** YOUR CODE HERE ***"
options = [((x, y + 1), Directions.NORTH),
((x, y - 1), Directions.SOUTH),
((x + 1, y), Directions.EAST),
((x - 1, y), Directions.WEST)]
for newPosition, action in options:
x, y = newPosition
if self.walls[x][y]:
continue
if newPosition in self.corners:
index = self.corners.index(newPosition)
newVisited = list(visited)
newVisited[index] = 1
newVisited = tuple(newVisited)
else:
newVisited = visited
newState = (newPosition, newVisited)
successors.append((newState, action, 1))
self._expanded += 1 # DO NOT CHANGE
return successors
@@ -334,12 +369,14 @@ class CornersProblem(search.SearchProblem):
Returns the cost of a particular sequence of actions. If those actions
include an illegal move, return 999999. This is implemented for you.
"""
if actions == None: return 999999
if actions == None:
return 999999
x, y = self.startingPosition
for action in actions:
dx, dy = Actions.directionToVector(action)
x, y = int(x + dx), int(y + dy)
if self.walls[x][y]: return 999999
if self.walls[x][y]:
return 999999
return len(actions)
@@ -357,17 +394,22 @@ def cornersHeuristic(state, problem):
admissible (as well as consistent).
"""
corners = problem.corners # These are the corner coordinates
walls = problem.walls # These are the walls of the maze, as a Grid (game.py)
# These are the walls of the maze, as a Grid (game.py)
walls = problem.walls
"*** YOUR CODE HERE ***"
return 0 # Default to trivial solution
class AStarCornersAgent(SearchAgent):
"A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"
def __init__(self):
self.searchFunction = lambda prob: search.aStarSearch(prob, cornersHeuristic)
self.searchFunction = lambda prob: search.aStarSearch(
prob, cornersHeuristic)
self.searchType = CornersProblem
class FoodSearchProblem:
"""
A search problem associated with finding the a path that collects all of the
@@ -377,8 +419,10 @@ class FoodSearchProblem:
pacmanPosition: a tuple (x,y) of integers specifying Pacman's position
foodGrid: a Grid (see game.py) of either True or False, specifying remaining food
"""
def __init__(self, startingGameState):
self.start = (startingGameState.getPacmanPosition(), startingGameState.getFood())
self.start = (startingGameState.getPacmanPosition(),
startingGameState.getFood())
self.walls = startingGameState.getWalls()
self.startingGameState = startingGameState
self._expanded = 0 # DO NOT CHANGE
@@ -418,12 +462,16 @@ class FoodSearchProblem:
cost += 1
return cost
class AStarFoodSearchAgent(SearchAgent):
"A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"
def __init__(self):
self.searchFunction = lambda prob: search.aStarSearch(prob, foodHeuristic)
self.searchFunction = lambda prob: search.aStarSearch(
prob, foodHeuristic)
self.searchType = FoodSearchProblem
def foodHeuristic(state, problem):
"""
Your heuristic for the FoodSearchProblem goes here.
@@ -456,13 +504,16 @@ def foodHeuristic(state, problem):
"*** YOUR CODE HERE ***"
return 0
class ClosestDotSearchAgent(SearchAgent):
"Search for all food using a sequence of searches"
def registerInitialState(self, state):
self.actions = []
currentState = state
while(currentState.getFood().count() > 0):
nextPathSegment = self.findPathToClosestDot(currentState) # The missing piece
nextPathSegment = self.findPathToClosestDot(
currentState) # The missing piece
self.actions += nextPathSegment
for action in nextPathSegment:
legal = currentState.getLegalActions()
@@ -487,6 +538,7 @@ class ClosestDotSearchAgent(SearchAgent):
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
class AnyFoodSearchProblem(PositionSearchProblem):
"""
A search problem for finding a path to any food.
@@ -523,6 +575,7 @@ class AnyFoodSearchProblem(PositionSearchProblem):
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
def mazeDistance(point1, point2, gameState):
"""
Returns the maze distance between any two points, using the search functions
@@ -538,5 +591,6 @@ def mazeDistance(point1, point2, gameState):
walls = gameState.getWalls()
assert not walls[x1][y1], 'point1 is a wall: ' + str(point1)
assert not walls[x2][y2], 'point2 is a wall: ' + str(point2)
prob = PositionSearchProblem(gameState, start=point1, goal=point2, warn=False, visualize=False)
prob = PositionSearchProblem(
gameState, start=point1, goal=point2, warn=False, visualize=False)
return len(search.bfs(prob))