Implement inefficient state space for corners problem.

This commit is contained in:
2021-10-20 20:58:42 -04:00
parent 515d3f6cd6
commit 7e64e723eb

View File

@@ -4,7 +4,7 @@
# educational purposes provided that (1) you do not distribute or publish # educational purposes provided that (1) you do not distribute or publish
# solutions, (2) you retain this notice, and (3) you provide clear # solutions, (2) you retain this notice, and (3) you provide clear
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. # attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
# #
# Attribution Information: The Pacman AI projects were developed at UC Berkeley. # Attribution Information: The Pacman AI projects were developed at UC Berkeley.
# The core projects and autograders were primarily created by John DeNero # The core projects and autograders were primarily created by John DeNero
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). # (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
@@ -41,6 +41,7 @@ import util
import time import time
import search import search
class GoWestAgent(Agent): class GoWestAgent(Agent):
"An agent that goes West until it can't." "An agent that goes West until it can't."
@@ -56,6 +57,7 @@ class GoWestAgent(Agent):
# after you fill in parts of search.py # # after you fill in parts of search.py #
####################################################### #######################################################
class SearchAgent(Agent): class SearchAgent(Agent):
""" """
This very general search agent finds a path using a supplied search This very general search agent finds a path using a supplied search
@@ -90,7 +92,8 @@ class SearchAgent(Agent):
heur = getattr(search, heuristic) heur = getattr(search, heuristic)
else: else:
raise AttributeError, heuristic + ' is not a function in searchAgents.py or search.py.' 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 # Note: this bit of Python trickery combines the search algorithm and the heuristic
self.searchFunction = lambda x: func(x, heuristic=heur) self.searchFunction = lambda x: func(x, heuristic=heur)
@@ -109,13 +112,16 @@ class SearchAgent(Agent):
state: a GameState object (pacman.py) 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() starttime = time.time()
problem = self.searchType(state) # Makes a new search problem problem = self.searchType(state) # Makes a new search problem
self.actions = self.searchFunction(problem) # Find a path self.actions = self.searchFunction(problem) # Find a path
totalCost = problem.getCostOfActions(self.actions) totalCost = problem.getCostOfActions(self.actions)
print('Path found with total cost of %d in %.1f seconds' % (totalCost, time.time() - starttime)) print('Path found with total cost of %d in %.1f seconds' %
if '_expanded' in dir(problem): print('Search nodes expanded: %d' % problem._expanded) (totalCost, time.time() - starttime))
if '_expanded' in dir(problem):
print('Search nodes expanded: %d' % problem._expanded)
def getAction(self, state): def getAction(self, state):
""" """
@@ -125,7 +131,8 @@ class SearchAgent(Agent):
state: a GameState object (pacman.py) 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 i = self.actionIndex
self.actionIndex += 1 self.actionIndex += 1
if i < len(self.actions): if i < len(self.actions):
@@ -133,6 +140,7 @@ class SearchAgent(Agent):
else: else:
return Directions.STOP return Directions.STOP
class PositionSearchProblem(search.SearchProblem): class PositionSearchProblem(search.SearchProblem):
""" """
A search problem defines the state space, start state, goal test, successor A search problem defines the state space, start state, goal test, successor
@@ -144,7 +152,7 @@ class PositionSearchProblem(search.SearchProblem):
Note: this search problem is fully specified; you should NOT change it. Note: this search problem is fully specified; you should NOT change it.
""" """
def __init__(self, gameState, costFn = lambda x: 1, goal=(1,1), start=None, warn=True, visualize=True): def __init__(self, gameState, costFn=lambda x: 1, goal=(1, 1), start=None, warn=True, visualize=True):
""" """
Stores the start and goal. Stores the start and goal.
@@ -154,7 +162,8 @@ class PositionSearchProblem(search.SearchProblem):
""" """
self.walls = gameState.getWalls() self.walls = gameState.getWalls()
self.startState = gameState.getPacmanPosition() self.startState = gameState.getPacmanPosition()
if start != None: self.startState = start if start != None:
self.startState = start
self.goal = goal self.goal = goal
self.costFn = costFn self.costFn = costFn
self.visualize = visualize self.visualize = visualize
@@ -162,7 +171,7 @@ class PositionSearchProblem(search.SearchProblem):
print 'Warning: this does not look like a regular search maze' print 'Warning: this does not look like a regular search maze'
# For display purposes # For display purposes
self._visited, self._visitedlist, self._expanded = {}, [], 0 # DO NOT CHANGE self._visited, self._visitedlist, self._expanded = {}, [], 0 # DO NOT CHANGE
def getStartState(self): def getStartState(self):
return self.startState return self.startState
@@ -175,8 +184,10 @@ class PositionSearchProblem(search.SearchProblem):
self._visitedlist.append(state) self._visitedlist.append(state)
import __main__ import __main__
if '_display' in dir(__main__): if '_display' in dir(__main__):
if 'drawExpandedCells' in dir(__main__._display): #@UndefinedVariable # @UndefinedVariable
__main__._display.drawExpandedCells(self._visitedlist) #@UndefinedVariable if 'drawExpandedCells' in dir(__main__._display):
__main__._display.drawExpandedCells(
self._visitedlist) # @UndefinedVariable
return isGoal return isGoal
@@ -194,16 +205,16 @@ class PositionSearchProblem(search.SearchProblem):
successors = [] successors = []
for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
x,y = state x, y = state
dx, dy = Actions.directionToVector(action) dx, dy = Actions.directionToVector(action)
nextx, nexty = int(x + dx), int(y + dy) nextx, nexty = int(x + dx), int(y + dy)
if not self.walls[nextx][nexty]: if not self.walls[nextx][nexty]:
nextState = (nextx, nexty) nextState = (nextx, nexty)
cost = self.costFn(nextState) cost = self.costFn(nextState)
successors.append( ( nextState, action, cost) ) successors.append((nextState, action, cost))
# Bookkeeping for display purposes # Bookkeeping for display purposes
self._expanded += 1 # DO NOT CHANGE self._expanded += 1 # DO NOT CHANGE
if state not in self._visited: if state not in self._visited:
self._visited[state] = True self._visited[state] = True
self._visitedlist.append(state) self._visitedlist.append(state)
@@ -215,17 +226,20 @@ class PositionSearchProblem(search.SearchProblem):
Returns the cost of a particular sequence of actions. If those actions Returns the cost of a particular sequence of actions. If those actions
include an illegal move, return 999999. include an illegal move, return 999999.
""" """
if actions == None: return 999999 if actions == None:
x,y= self.getStartState() return 999999
x, y = self.getStartState()
cost = 0 cost = 0
for action in actions: for action in actions:
# Check figure out the next state and see whether its' legal # Check figure out the next state and see whether its' legal
dx, dy = Actions.directionToVector(action) dx, dy = Actions.directionToVector(action)
x, y = int(x + dx), int(y + dy) x, y = int(x + dx), int(y + dy)
if self.walls[x][y]: return 999999 if self.walls[x][y]:
cost += self.costFn((x,y)) return 999999
cost += self.costFn((x, y))
return cost return cost
class StayEastSearchAgent(SearchAgent): class StayEastSearchAgent(SearchAgent):
""" """
An agent for position search with a cost function that penalizes being in 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. The cost function for stepping into a position (x,y) is 1/2^x.
""" """
def __init__(self): def __init__(self):
self.searchFunction = search.uniformCostSearch self.searchFunction = search.uniformCostSearch
costFn = lambda pos: .5 ** pos[0] def costFn(pos): return .5 ** pos[0]
self.searchType = lambda state: PositionSearchProblem(state, costFn, (1, 1), None, False) self.searchType = lambda state: PositionSearchProblem(
state, costFn, (1, 1), None, False)
class StayWestSearchAgent(SearchAgent): class StayWestSearchAgent(SearchAgent):
""" """
@@ -245,27 +262,31 @@ class StayWestSearchAgent(SearchAgent):
The cost function for stepping into a position (x,y) is 2^x. The cost function for stepping into a position (x,y) is 2^x.
""" """
def __init__(self): def __init__(self):
self.searchFunction = search.uniformCostSearch self.searchFunction = search.uniformCostSearch
costFn = lambda pos: 2 ** pos[0] def costFn(pos): return 2 ** pos[0]
self.searchType = lambda state: PositionSearchProblem(state, costFn) self.searchType = lambda state: PositionSearchProblem(state, costFn)
def manhattanHeuristic(position, problem, info={}): def manhattanHeuristic(position, problem, info={}):
"The Manhattan distance heuristic for a PositionSearchProblem" "The Manhattan distance heuristic for a PositionSearchProblem"
xy1 = position xy1 = position
xy2 = problem.goal xy2 = problem.goal
return abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1]) return abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1])
def euclideanHeuristic(position, problem, info={}): def euclideanHeuristic(position, problem, info={}):
"The Euclidean distance heuristic for a PositionSearchProblem" "The Euclidean distance heuristic for a PositionSearchProblem"
xy1 = position xy1 = position
xy2 = problem.goal xy2 = problem.goal
return ( (xy1[0] - xy2[0]) ** 2 + (xy1[1] - xy2[1]) ** 2 ) ** 0.5 return ((xy1[0] - xy2[0]) ** 2 + (xy1[1] - xy2[1]) ** 2) ** 0.5
##################################################### #####################################################
# This portion is incomplete. Time to write code! # # This portion is incomplete. Time to write code! #
##################################################### #####################################################
class CornersProblem(search.SearchProblem): class CornersProblem(search.SearchProblem):
""" """
This search problem finds paths through all four corners of a layout. This search problem finds paths through all four corners of a layout.
@@ -280,29 +301,33 @@ class CornersProblem(search.SearchProblem):
self.walls = startingGameState.getWalls() self.walls = startingGameState.getWalls()
self.startingPosition = startingGameState.getPacmanPosition() self.startingPosition = startingGameState.getPacmanPosition()
top, right = self.walls.height-2, self.walls.width-2 top, right = self.walls.height-2, self.walls.width-2
self.corners = ((1,1), (1,top), (right, 1), (right, top)) self.corners = ((1, 1), (1, top), (right, 1), (right, top))
for corner in self.corners: for corner in self.corners:
if not startingGameState.hasFood(*corner): if not startingGameState.hasFood(*corner):
print 'Warning: no food in corner ' + str(corner) print 'Warning: no food in corner ' + str(corner)
self._expanded = 0 # DO NOT CHANGE; Number of search nodes expanded self._expanded = 0 # DO NOT CHANGE; Number of search nodes expanded
# Please add any code here which you would like to use # Please add any code here which you would like to use
# in initializing the problem # in initializing the problem
"*** YOUR CODE HERE ***"
def getStartState(self): def getStartState(self):
""" """
Returns the start state (in your state space, not the full Pacman state Returns the start state (in your state space, not the full Pacman state
space) space)
""" """
"*** YOUR CODE HERE ***" current = self.startingPosition
util.raiseNotDefined() visited = tuple([1 if corner == current else 0
for corner in self.corners])
return (self.startingPosition, visited)
def isGoalState(self, state): def isGoalState(self, state):
""" """
Returns whether this search state is a goal state of the problem. Returns whether this search state is a goal state of the problem.
""" """
"*** YOUR CODE HERE ***" "*** YOUR CODE HERE ***"
util.raiseNotDefined() position, visited = state
if sum(visited) == 4:
return True
return False
def getSuccessors(self, state): def getSuccessors(self, state):
""" """
@@ -315,18 +340,28 @@ class CornersProblem(search.SearchProblem):
is the incremental cost of expanding to that successor is the incremental cost of expanding to that successor
""" """
position, visited = state
x, y = position
successors = [] successors = []
for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: options = [((x, y + 1), Directions.NORTH),
# Add a successor state to the successor list if the action is legal ((x, y - 1), Directions.SOUTH),
# Here's a code snippet for figuring out whether a new position hits a wall: ((x + 1, y), Directions.EAST),
# x,y = currentPosition ((x - 1, y), Directions.WEST)]
# dx, dy = Actions.directionToVector(action) for newPosition, action in options:
# nextx, nexty = int(x + dx), int(y + dy) x, y = newPosition
# hitsWall = self.walls[nextx][nexty] 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))
"*** YOUR CODE HERE ***" self._expanded += 1 # DO NOT CHANGE
self._expanded += 1 # DO NOT CHANGE
return successors return successors
def getCostOfActions(self, actions): def getCostOfActions(self, actions):
@@ -334,12 +369,14 @@ class CornersProblem(search.SearchProblem):
Returns the cost of a particular sequence of actions. If those actions Returns the cost of a particular sequence of actions. If those actions
include an illegal move, return 999999. This is implemented for you. include an illegal move, return 999999. This is implemented for you.
""" """
if actions == None: return 999999 if actions == None:
x,y= self.startingPosition return 999999
x, y = self.startingPosition
for action in actions: for action in actions:
dx, dy = Actions.directionToVector(action) dx, dy = Actions.directionToVector(action)
x, y = int(x + dx), int(y + dy) 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) return len(actions)
@@ -356,18 +393,23 @@ def cornersHeuristic(state, problem):
shortest path from the state to a goal of the problem; i.e. it should be shortest path from the state to a goal of the problem; i.e. it should be
admissible (as well as consistent). admissible (as well as consistent).
""" """
corners = problem.corners # These are the corner coordinates 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 ***" "*** YOUR CODE HERE ***"
return 0 # Default to trivial solution return 0 # Default to trivial solution
class AStarCornersAgent(SearchAgent): class AStarCornersAgent(SearchAgent):
"A SearchAgent for FoodSearchProblem using A* and your foodHeuristic" "A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"
def __init__(self): def __init__(self):
self.searchFunction = lambda prob: search.aStarSearch(prob, cornersHeuristic) self.searchFunction = lambda prob: search.aStarSearch(
prob, cornersHeuristic)
self.searchType = CornersProblem self.searchType = CornersProblem
class FoodSearchProblem: class FoodSearchProblem:
""" """
A search problem associated with finding the a path that collects all of the A search problem associated with finding the a path that collects all of the
@@ -377,12 +419,14 @@ class FoodSearchProblem:
pacmanPosition: a tuple (x,y) of integers specifying Pacman's position 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 foodGrid: a Grid (see game.py) of either True or False, specifying remaining food
""" """
def __init__(self, startingGameState): def __init__(self, startingGameState):
self.start = (startingGameState.getPacmanPosition(), startingGameState.getFood()) self.start = (startingGameState.getPacmanPosition(),
startingGameState.getFood())
self.walls = startingGameState.getWalls() self.walls = startingGameState.getWalls()
self.startingGameState = startingGameState self.startingGameState = startingGameState
self._expanded = 0 # DO NOT CHANGE self._expanded = 0 # DO NOT CHANGE
self.heuristicInfo = {} # A dictionary for the heuristic to store information self.heuristicInfo = {} # A dictionary for the heuristic to store information
def getStartState(self): def getStartState(self):
return self.start return self.start
@@ -393,21 +437,21 @@ class FoodSearchProblem:
def getSuccessors(self, state): def getSuccessors(self, state):
"Returns successor states, the actions they require, and a cost of 1." "Returns successor states, the actions they require, and a cost of 1."
successors = [] successors = []
self._expanded += 1 # DO NOT CHANGE self._expanded += 1 # DO NOT CHANGE
for direction in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: for direction in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
x,y = state[0] x, y = state[0]
dx, dy = Actions.directionToVector(direction) dx, dy = Actions.directionToVector(direction)
nextx, nexty = int(x + dx), int(y + dy) nextx, nexty = int(x + dx), int(y + dy)
if not self.walls[nextx][nexty]: if not self.walls[nextx][nexty]:
nextFood = state[1].copy() nextFood = state[1].copy()
nextFood[nextx][nexty] = False nextFood[nextx][nexty] = False
successors.append( ( ((nextx, nexty), nextFood), direction, 1) ) successors.append((((nextx, nexty), nextFood), direction, 1))
return successors return successors
def getCostOfActions(self, actions): def getCostOfActions(self, actions):
"""Returns the cost of a particular sequence of actions. If those actions """Returns the cost of a particular sequence of actions. If those actions
include an illegal move, return 999999""" include an illegal move, return 999999"""
x,y= self.getStartState()[0] x, y = self.getStartState()[0]
cost = 0 cost = 0
for action in actions: for action in actions:
# figure out the next state and see whether it's legal # figure out the next state and see whether it's legal
@@ -418,12 +462,16 @@ class FoodSearchProblem:
cost += 1 cost += 1
return cost return cost
class AStarFoodSearchAgent(SearchAgent): class AStarFoodSearchAgent(SearchAgent):
"A SearchAgent for FoodSearchProblem using A* and your foodHeuristic" "A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"
def __init__(self): def __init__(self):
self.searchFunction = lambda prob: search.aStarSearch(prob, foodHeuristic) self.searchFunction = lambda prob: search.aStarSearch(
prob, foodHeuristic)
self.searchType = FoodSearchProblem self.searchType = FoodSearchProblem
def foodHeuristic(state, problem): def foodHeuristic(state, problem):
""" """
Your heuristic for the FoodSearchProblem goes here. Your heuristic for the FoodSearchProblem goes here.
@@ -456,13 +504,16 @@ def foodHeuristic(state, problem):
"*** YOUR CODE HERE ***" "*** YOUR CODE HERE ***"
return 0 return 0
class ClosestDotSearchAgent(SearchAgent): class ClosestDotSearchAgent(SearchAgent):
"Search for all food using a sequence of searches" "Search for all food using a sequence of searches"
def registerInitialState(self, state): def registerInitialState(self, state):
self.actions = [] self.actions = []
currentState = state currentState = state
while(currentState.getFood().count() > 0): while(currentState.getFood().count() > 0):
nextPathSegment = self.findPathToClosestDot(currentState) # The missing piece nextPathSegment = self.findPathToClosestDot(
currentState) # The missing piece
self.actions += nextPathSegment self.actions += nextPathSegment
for action in nextPathSegment: for action in nextPathSegment:
legal = currentState.getLegalActions() legal = currentState.getLegalActions()
@@ -487,6 +538,7 @@ class ClosestDotSearchAgent(SearchAgent):
"*** YOUR CODE HERE ***" "*** YOUR CODE HERE ***"
util.raiseNotDefined() util.raiseNotDefined()
class AnyFoodSearchProblem(PositionSearchProblem): class AnyFoodSearchProblem(PositionSearchProblem):
""" """
A search problem for finding a path to any food. A search problem for finding a path to any food.
@@ -511,18 +563,19 @@ class AnyFoodSearchProblem(PositionSearchProblem):
self.walls = gameState.getWalls() self.walls = gameState.getWalls()
self.startState = gameState.getPacmanPosition() self.startState = gameState.getPacmanPosition()
self.costFn = lambda x: 1 self.costFn = lambda x: 1
self._visited, self._visitedlist, self._expanded = {}, [], 0 # DO NOT CHANGE self._visited, self._visitedlist, self._expanded = {}, [], 0 # DO NOT CHANGE
def isGoalState(self, state): def isGoalState(self, state):
""" """
The state is Pacman's position. Fill this in with a goal test that will The state is Pacman's position. Fill this in with a goal test that will
complete the problem definition. complete the problem definition.
""" """
x,y = state x, y = state
"*** YOUR CODE HERE ***" "*** YOUR CODE HERE ***"
util.raiseNotDefined() util.raiseNotDefined()
def mazeDistance(point1, point2, gameState): def mazeDistance(point1, point2, gameState):
""" """
Returns the maze distance between any two points, using the search functions Returns the maze distance between any two points, using the search functions
@@ -538,5 +591,6 @@ def mazeDistance(point1, point2, gameState):
walls = gameState.getWalls() walls = gameState.getWalls()
assert not walls[x1][y1], 'point1 is a wall: ' + str(point1) assert not walls[x1][y1], 'point1 is a wall: ' + str(point1)
assert not walls[x2][y2], 'point2 is a wall: ' + str(point2) 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)) return len(search.bfs(prob))