Advertisement
Guest User

Untitled

a guest
Oct 31st, 2014
263
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 22.47 KB | None | 0 0
  1. """
  2. This file contains all of the agents that can be selected to
  3. control Pacman.  To select an agent, use the '-p' option
  4. when running pacman.py.  Arguments can be passed to your agent
  5. using '-a'.  For example, to load a SearchAgent that uses
  6. depth first search (dfs), run the following command:
  7.  
  8. > python pacman.py -p SearchAgent -a searchFunction=depthFirstSearch
  9.  
  10. Commands to invoke other search strategies can be found in the
  11. project description.
  12.  
  13. Please only change the parts of the file you are asked to.
  14. Look for the lines that say
  15.  
  16. "*** YOUR CODE HERE ***"
  17.  
  18. The parts you fill in start about 3/4 of the way down.  Follow the
  19. project description for details.
  20.  
  21. Good luck and happy searching!
  22. """
  23. from game import Directions
  24. from game import Agent
  25. from game import Actions
  26. import util
  27. import time
  28. import search
  29. import searchAgents
  30.  
  31. class GoWestAgent(Agent):
  32.   "An agent that goes West until it can't."
  33.  
  34.   def getAction(self, state):
  35.     "The agent receives a GameState (defined in pacman.py)."
  36.     if Directions.WEST in state.getLegalPacmanActions():
  37.       return Directions.WEST
  38.     else:
  39.       return Directions.STOP
  40.  
  41. #######################################################
  42. # This portion is written for you, but will only work #
  43. #       after you fill in parts of search.py          #
  44. #######################################################
  45.  
  46. class SearchAgent(Agent):
  47.   """
  48.  This very general search agent finds a path using a supplied search algorithm for a
  49.  supplied search problem, then returns actions to follow that path.
  50.  
  51.  As a default, this agent runs DFS on a PositionSearchProblem to find location (1,1)
  52.  
  53.  Options for fn include:
  54.    depthFirstSearch or dfs
  55.    breadthFirstSearch or bfs
  56.    
  57.  
  58.  Note: You should NOT change any code in SearchAgent
  59.  """
  60.    
  61.   def __init__(self, fn='depthFirstSearch', prob='PositionSearchProblem', heuristic='nullHeuristic'):
  62.     # Warning: some advanced Python magic is employed below to find the right functions and problems
  63.    
  64.     # Get the search function from the name and heuristic
  65.     if fn not in dir(search):
  66.       raise AttributeError, fn + ' is not a search function in search.py.'
  67.     func = getattr(search, fn)
  68.     if 'heuristic' not in func.func_code.co_varnames:
  69.       print('[SearchAgent] using function ' + fn)
  70.       self.searchFunction = func
  71.     else:
  72.       if heuristic in dir(searchAgents):
  73.         heur = getattr(searchAgents, heuristic)
  74.       elif heuristic in dir(search):
  75.         heur = getattr(search, heuristic)
  76.       else:
  77.         raise AttributeError, heuristic + ' is not a function in searchAgents.py or search.py.'
  78.       print('[SearchAgent] using function %s and heuristic %s' % (fn, heuristic))
  79.       # Note: this bit of Python trickery combines the search algorithm and the heuristic
  80.       self.searchFunction = lambda x: func(x, heuristic=heur)
  81.      
  82.     # Get the search problem type from the name
  83.     if prob not in dir(searchAgents) or not prob.endswith('Problem'):
  84.       raise AttributeError, prob + ' is not a search problem type in SearchAgents.py.'
  85.     self.searchType = getattr(searchAgents, prob)
  86.     print('[SearchAgent] using problem type ' + prob)
  87.    
  88.   def registerInitialState(self, state):
  89.     """
  90.    This is the first time that the agent sees the layout of the game board. Here, we
  91.    choose a path to the goal.  In this phase, the agent should compute the path to the
  92.    goal and store it in a local variable.  All of the work is done in this method!
  93.    
  94.    state: a GameState object (pacman.py)
  95.    """
  96.     if self.searchFunction == None: raise Exception, "No search function provided for SearchAgent"
  97.     starttime = time.time()
  98.     problem = self.searchType(state) # Makes a new search problem
  99.     self.actions  = self.searchFunction(problem) # Find a path
  100.     totalCost = problem.getCostOfActions(self.actions)
  101.     print('Path found with total cost of %d in %.1f seconds' % (totalCost, time.time() - starttime))
  102.     if '_expanded' in dir(problem): print('Search nodes expanded: %d' % problem._expanded)
  103.    
  104.   def getAction(self, state):
  105.     """
  106.    Returns the next action in the path chosen earlier (in registerInitialState).  Return
  107.    Directions.STOP if there is no further action to take.
  108.    
  109.    state: a GameState object (pacman.py)
  110.    """
  111.     if 'actionIndex' not in dir(self): self.actionIndex = 0
  112.     i = self.actionIndex
  113.     self.actionIndex += 1
  114.     if i < len(self.actions):
  115.       return self.actions[i]    
  116.     else:
  117.       return Directions.STOP
  118.  
  119. class PositionSearchProblem(search.SearchProblem):
  120.   """
  121.  A search problem defines the state space, start state, goal test,
  122.  successor function and cost function.  This search problem can be
  123.  used to find paths to a particular point on the pacman board.
  124.  
  125.  The state space consists of (x,y) positions in a pacman game.
  126.  
  127.  Note: this search problem is fully specified; you should NOT change it.
  128.  """
  129.  
  130.   def __init__(self, gameState, costFn = lambda x: 1, goal=(1,1)):
  131.     """
  132.    Stores the start and goal.  
  133.    
  134.    gameState: A GameState object (pacman.py)
  135.    costFn: A function from a search state (tuple) to a non-negative number
  136.    goal: A position in the gameState
  137.    """
  138.     self.walls = gameState.getWalls()
  139.     self.startState = gameState.getPacmanPosition()
  140.     self.goal = goal
  141.     self.costFn = costFn
  142.     if gameState.getNumFood() != 1 or not gameState.hasFood(*goal):
  143.       print 'Warning: this does not look like a regular search maze'
  144.  
  145.     # For display purposes
  146.     self._visited, self._visitedlist, self._expanded = {}, [], 0
  147.  
  148.   def getStartState(self):
  149.     return self.startState
  150.  
  151.   def isGoalState(self, state):
  152.      isGoal = state == self.goal
  153.      
  154.      # For display purposes only
  155.      if isGoal:
  156.        self._visitedlist.append(state)
  157.        import __main__
  158.        if '_display' in dir(__main__):
  159.          if 'drawExpandedCells' in dir(__main__._display): #@UndefinedVariable
  160.            __main__._display.drawExpandedCells(self._visitedlist) #@UndefinedVariable
  161.        
  162.      return isGoal  
  163.    
  164.   def getSuccessors(self, state):
  165.     """
  166.    Returns successor states, the actions they require, and a cost of 1.
  167.    
  168.     As noted in search.py:
  169.         For a given state, this should return a list of triples,
  170.     (successor, action, stepCost), where 'successor' is a
  171.     successor to the current state, 'action' is the action
  172.     required to get there, and 'stepCost' is the incremental
  173.     cost of expanding to that successor
  174.    """
  175.    
  176.     successors = []
  177.     for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
  178.       x,y = state
  179.       dx, dy = Actions.directionToVector(action)
  180.       nextx, nexty = int(x + dx), int(y + dy)
  181.       if not self.walls[nextx][nexty]:
  182.         nextState = (nextx, nexty)
  183.         cost = self.costFn(nextState)
  184.         successors.append( ( nextState, action, cost) )
  185.        
  186.     # Bookkeeping for display purposes
  187.     self._expanded += 1
  188.     if state not in self._visited:
  189.       self._visited[state] = True
  190.       self._visitedlist.append(state)
  191.      
  192.     return successors
  193.  
  194.   def getCostOfActions(self, actions):
  195.     """
  196.    Returns the cost of a particular sequence of actions.  If those actions
  197.    include an illegal move, return 999999
  198.    """
  199.     if actions == None: return 999999
  200.     x,y= self.getStartState()
  201.     cost = 0
  202.     for action in actions:
  203.       # Check figure out the next state and see whether its' legal
  204.       dx, dy = Actions.directionToVector(action)
  205.       x, y = int(x + dx), int(y + dy)
  206.       if self.walls[x][y]: return 999999
  207.       cost += self.costFn((x,y))
  208.     return cost
  209.  
  210. class StayEastSearchAgent(SearchAgent):
  211.   def __init__(self):
  212.       self.searchFunction = search.uniformCostSearch
  213.       costFn = lambda pos: .5 ** pos[0]
  214.       self.searchType = lambda state: PositionSearchProblem(state, costFn)
  215.      
  216. class StayWestSearchAgent(SearchAgent):
  217.   def __init__(self):
  218.       self.searchFunction = search.uniformCostSearch
  219.       costFn = lambda pos: 2 ** pos[0]
  220.       self.searchType = lambda state: PositionSearchProblem(state, costFn)
  221.  
  222. def manhattanHeuristic(position, problem, info={}):
  223.   "The Manhattan distance heuristic for a PositionSearchProblem"
  224.   xy1 = position
  225.   xy2 = problem.goal
  226.   return abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1])
  227.  
  228. #####################################################
  229. # This portion is incomplete.  Time to write code!  #
  230. #####################################################
  231.  
  232. class CornersProblem(search.SearchProblem):
  233.   """
  234.  This search problem finds paths through all four corners of a layout.
  235.  
  236.  You must select a suitable state space and successor function
  237.  """
  238.  
  239.   def __init__(self, startingGameState):
  240.     "Stores the walls and corners. Hint: you'll also want to store a starting state"
  241.     self.walls = startingGameState.getWalls()
  242.     self.startingGameState = startingGameState
  243.     top, right = self.walls.height-2, self.walls.width-2
  244.     self.corners = ((1,1), (1,top), (right, 1), (right, top))
  245.     for corner in self.corners:
  246.       if not startingGameState.hasFood(*corner): print 'Warning: no food in corner ' + str(corner)
  247.     self._expanded = 0 # Number of search nodes expanded
  248.    
  249.     "*** YOUR CODE HERE ***"
  250.     # state has structure state(current_location, visited_corners_list)
  251.     corners_to_visit = self.corners
  252.     self.start = (startingGameState.getPacmanPosition(), corners_to_visit)
  253.    
  254.   def getStartState(self):
  255.     "Returns the start state (in your state space, not the full Pacman state space)"
  256.     "*** YOUR CODE HERE ***"
  257.     return self.start
  258.     util.raiseNotDefined()
  259.    
  260.   def isGoalState(self, state):
  261.     "Returns whether this search state is a goal state of the problem"
  262.     "*** YOUR CODE HERE ***"
  263.     return len(state[1]) == 0
  264.     util.raiseNotDefined()
  265.        
  266.   def getSuccessors(self, state):
  267.     """
  268.    Returns successor states, the actions they require, and a cost of 1.
  269.    
  270.     As noted in search.py:
  271.         For a given state, this should return a list of triples,
  272.     (successor, action, stepCost), where 'successor' is a
  273.     successor to the current state, 'action' is the action
  274.     required to get there, and 'stepCost' is the incremental
  275.     cost of expanding to that successor
  276.    """
  277.    
  278.     successors = []
  279.     for direction in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
  280.       # Add a successor state to the successor list if the action is legal
  281.       # Here's a code snippet for figuring out whether a new position hits a wall:
  282.       #   x,y = currentPosition
  283.       #   dx, dy = Actions.directionToVector(action)
  284.       #   nextx, nexty = int(x + dx), int(y + dy)
  285.       #   hitsWall = self.walls[nextx][nexty]
  286.      
  287.       "*** YOUR CODE HERE ***"
  288.       x,y = state[0]
  289.       corners_to_visit = state[1]
  290.       new_list = []
  291.       # copy all corners except if the current point is one of the corners
  292.       if (x,y) in corners_to_visit:
  293.           for corners in corners_to_visit:
  294.               if (x,y) != corners:
  295.                   new_list.append(corners)
  296.       else:
  297.           for corners in corners_to_visit:
  298.               new_list.append(corners)
  299.       listing = tuple(new_list)    
  300.       dx, dy = Actions.directionToVector(direction)
  301.       nextx, nexty = int(x + dx), int(y + dy)
  302.       if not self.walls[nextx][nexty]:
  303.           successors.append( ( ((nextx, nexty), listing), direction, 1) )
  304.     self._expanded += 1
  305.     return successors
  306.  
  307.  
  308.   def getCostOfActions(self, actions):
  309.     """
  310.    Returns the cost of a particular sequence of actions.  If those actions
  311.    include an illegal move, return 999999.  This is implemented for you.
  312.    """
  313.     if actions == None: return 999999
  314.     x,y= self.startingGameState.getPacmanPosition()
  315.     for action in actions:
  316.       dx, dy = Actions.directionToVector(action)
  317.       x, y = int(x + dx), int(y + dy)
  318.       if self.walls[x][y]: return 999999
  319.     return len(actions)
  320.  
  321.  
  322. def cornersHeuristic(state, problem):
  323.   """
  324.  A heuristic for the CornersProblem that you defined.
  325.  
  326.    state:   The current search state
  327.             (a data structure you chose in your search problem)
  328.    
  329.    problem: The CornersProblem instance for this layout.  
  330.    
  331.  This function should always return a number that is a lower bound
  332.  on the shortest path from the state to a goal of the problem.
  333.  """
  334.   corners = problem.corners # These are the corner coordinates
  335.   walls = problem.walls # These are the walls of the maze, as a Grid (game.py)
  336.  
  337.   "*** YOUR CODE HERE ***"
  338.   # state[0] is the location of the current node
  339.   # state[1] is a tuple location of the corners that have not been visited
  340.   distance = 0
  341.   corners_list = list(state[1])
  342.   xy1 = state[0]
  343.   temp = 0
  344.   closest =0
  345.   distance_list = []
  346.   min_index = 0
  347.   farthest = 0
  348.   distance_to_closest_corner = 0
  349.   closest = 0
  350.  
  351.   # if no corners left - we are done
  352.   if len(corners_list) == 0:
  353.       None
  354.   elif len(corners_list) > 0:
  355.       # Step 1: If there is at least one corner left, find distance to that corner
  356.       for i in range(len(corners_list)):
  357.           xy2 = corners_list[i]
  358.           # put all distances into a list
  359.           distance_list.append(abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1]))
  360.       distance_to_closest_corner = min(distance_list)      
  361.       min_index = distance_list.index(distance_to_closest_corner)
  362.       closest_corner = corners_list[min_index]
  363.      
  364.       # If using only distance to the closest corner as the heuristics,
  365.       # Search nodes expanded on mediumCorners is 1555.
  366.       total = 0
  367.       # Step 2: if there are more than 1 corner left
  368.       # find the shortest distance from the closest corner
  369.       # to the other corner that is closest to the closest corner
  370.       corners_list.remove(closest_corner)
  371.       while len(corners_list) > 0:
  372.           distance_list = []
  373.           xy1 = closest_corner
  374.           for i in range(len(corners_list)):
  375.               xy2 = corners_list[i]
  376.               # put all distances into a list
  377.               distance_list.append(abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1]))
  378.           closest = min(distance_list)
  379.           min_index = distance_list.index(closest)
  380.           closest_corner = corners_list[min_index]
  381.           corners_list.remove(closest_corner)
  382.          
  383.           total = total + closest
  384.          
  385.       # distance = distance to closest + distance to the closest of the closest    
  386.       distance = distance_to_closest_corner + total
  387.      
  388.   return distance
  389.   return 0 # Default to trivial solution
  390.  
  391. class AStarCornersAgent(SearchAgent):
  392.   "A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"
  393.   def __init__(self):
  394.     self.searchFunction = lambda prob: search.aStarSearch(prob, cornersHeuristic)
  395.     self.searchType = CornersProblem
  396.  
  397. class FoodSearchProblem:
  398.   """
  399.  A search problem associated with finding the a path that collects all of the
  400.  food (dots) in a Pacman game.
  401.  
  402.  A search state in this problem is a tuple ( pacmanPosition, foodGrid ) where
  403.    pacmanPosition: a tuple (x,y) of integers specifying Pacman's position
  404.    foodGrid:       a Grid (see game.py) of either True or False, specifying remaining food
  405.  """
  406.   def __init__(self, startingGameState):
  407.     self.start = (startingGameState.getPacmanPosition(), startingGameState.getFood())
  408.     self.walls = startingGameState.getWalls()
  409.     self.startingGameState = startingGameState
  410.     self._expanded = 0
  411.     self.heuristicInfo = {} # A dictionary for the heuristic to store information
  412.      
  413.   def getStartState(self):
  414.     return self.start
  415.  
  416.   def isGoalState(self, state):
  417.     return state[1].count() == 0
  418.  
  419.   def getSuccessors(self, state):
  420.     "Returns successor states, the actions they require, and a cost of 1."
  421.     successors = []
  422.     self._expanded += 1
  423.     for direction in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
  424.       x,y = state[0]
  425.       dx, dy = Actions.directionToVector(direction)
  426.       nextx, nexty = int(x + dx), int(y + dy)
  427.       if not self.walls[nextx][nexty]:
  428.         nextFood = state[1].copy()
  429.         nextFood[nextx][nexty] = False
  430.         successors.append( ( ((nextx, nexty), nextFood), direction, 1) )
  431.     return successors
  432.  
  433.   def getCostOfActions(self, actions):
  434.     """Returns the cost of a particular sequence of actions.  If those actions
  435.    include an illegal move, return 999999"""
  436.     x,y= self.getStartState()[0]
  437.     cost = 0
  438.     for action in actions:
  439.       # figure out the next state and see whether it's legal
  440.       dx, dy = Actions.directionToVector(action)
  441.       x, y = int(x + dx), int(y + dy)
  442.       if self.walls[x][y]:
  443.         return 999999
  444.       cost += 1
  445.     return cost
  446.  
  447. class AStarFoodSearchAgent(SearchAgent):
  448.   "A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"
  449.   def __init__(self):
  450.     self.searchFunction = lambda prob: search.aStarSearch(prob, foodHeuristic)
  451.     self.searchType = FoodSearchProblem
  452.  
  453. def foodHeuristic(state, problem):
  454.   """
  455.  Your heuristic for the FoodSearchProblem goes here.
  456.  
  457.  This heuristic must be admissible.  If using A* ever finds a solution that is
  458.  longer than running uniform cost search, your heuristic is *not* admissible!  
  459.  
  460.  The state is a tuple ( pacmanPosition, foodGrid ) where foodGrid is a
  461.  Grid (see game.py) of either True or False.
  462.  
  463.  If you want access to info like walls, capsules, etc., you can query the problem.
  464.  For example, problem.walls gives you a Grid of where the walls are.
  465.  
  466.  If you want to *store* information to be reused in other calls to the heuristic,
  467.  there is a dictionary called problem.heuristicInfo that you can use. For example,
  468.  if you only want to count the walls once and store that value, try:
  469.    problem.heuristicInfo['wallCount'] = problem.walls.count()
  470.  Subsequent calls to this heuristic can access problem.heuristicInfo['wallCount']
  471.  """
  472.   position, foodGrid = state
  473.   "*** YOUR CODE HERE ***"
  474.   """
  475.  # Number of food left (gives ~13000 nodes)
  476.  return foodGrid.count()
  477.  """
  478.  
  479.   # Calculate MAX manhattan distance from Pacman to food left to (gives ~9800 nodes)
  480.   h = foodGrid.height
  481.   w = foodGrid.width
  482.   ct = 0
  483.   for x in range(w):
  484.     for y in range(h):
  485.       if foodGrid[x][y]:
  486.         ct = max(ct, abs(x-position[0]) + abs(y-position[1]))
  487.   return ct
  488.  
  489.  
  490.  
  491. #------------------------------------------------------------------------------
  492.  
  493. class ClosestDotSearchAgent(SearchAgent):
  494.     "Search for all food using a sequence of searches"
  495.     "using A* and nullHeuristics"
  496.     def __init__(self):
  497.         self.searchFunction = lambda prob: search.aStarSearch(prob, nullHeuristic)
  498.         self.searchType = ClosestDotSearchProblem
  499.        
  500.     def registerInitialState(self, state):
  501.         self.actions = []
  502.         currentState = state
  503.         while(currentState.getFood().count() > 0):
  504.             nextPathSegment = self.findPathToClosestDot(currentState) # The missing piece
  505.             self.actions += nextPathSegment
  506.             for action in nextPathSegment:
  507.                 legal = currentState.getLegalActions()
  508.                 if action not in legal:
  509.                     t = (str(action), str(currentState))
  510.                     raise Exception, 'findPathToClosestDot returned an illegal move: %s!\n%s' % t
  511.                 currentState = currentState.generateSuccessor(0, action)
  512.         self.actionIndex = 0
  513.         print 'Path found with cost %d.' % len(self.actions)
  514.    
  515.    
  516.     def findPathToClosestDot(self, startState):
  517.         "Returns a path (a list of actions) to the closest dot, starting in startState"
  518.         # Here are some useful elements of the startState
  519.         startPosition = startState.getPacmanPosition()
  520.         food = startState.getFood()
  521.         walls = startState.getWalls()
  522.        
  523.         "*** YOUR CODE HERE ***"
  524.         actions = self.getSuccessors(startState)
  525.         return actions
  526.         util.raiseNotDefined()
  527.    
  528. #------------------------------------------------------------------------------
  529. class ClosestDotSearchProblem:
  530.   """
  531.  A search problem associated with finding the a path that collects all of the
  532.  food (dots) in a Pacman game.
  533.  
  534.  A search state in this problem is a tuple ( pacmanPosition, foodGrid ) where
  535.    pacmanPosition: a tuple (x,y) of integers specifying Pacman's position
  536.    foodGrid:       a Grid (see game.py) of either True or False, specifying remaining food
  537.  """
  538.   def __init__(self, startingGameState):
  539.     self.start = (startingGameState.getPacmanPosition(), startingGameState.getFood())
  540.     self.walls = startingGameState.getWalls()
  541.     self.startingGameState = startingGameState
  542.     self._expanded = 0
  543.     self.heuristicInfo = {} # A dictionary for the heuristic to store information
  544.      
  545.   def getStartState(self):
  546.     return self.start
  547.  
  548.   def isGoalState(self, state):
  549.     return state[1].count() == 0
  550.  
  551.   def getSuccessors(self, state):
  552.     "Returns successor states, the actions they require, and a cost of 1."
  553.     successors = []
  554.     self._expanded += 1
  555.     for direction in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
  556.       x,y = state[0]
  557.       dx, dy = Actions.directionToVector(direction)
  558.       nextx, nexty = int(x + dx), int(y + dy)
  559.       if not self.walls[nextx][nexty]:
  560.         nextFood = state[1].copy()
  561.         nextFood[nextx][nexty] = False
  562.         successors.append( ( ((nextx, nexty), nextFood), direction, 1) )
  563.     return successors
  564.  
  565.   def getCostOfActions(self, actions):
  566.     """Returns the cost of a particular sequence of actions.  If those actions
  567.    include an illegal move, return 999999"""
  568.     x,y= self.getStartState()[0]
  569.     cost = 0
  570.     for action in actions:
  571.       # figure out the next state and see whether it's legal
  572.       dx, dy = Actions.directionToVector(action)
  573.       x, y = int(x + dx), int(y + dy)
  574.       if self.walls[x][y]:
  575.         return 999999
  576.       cost += 1
  577.     return cost
  578. #------------------------------------------------------------------------------
  579. class ApproximateSearchAgent(Agent):
  580.   "Implement your contest entry here.  Change anything but the name."
  581.  
  582.   def registerInitialState(self, state):
  583.     "This method is called before any moves are made."
  584.     "*** YOUR CODE HERE ***"
  585.    
  586.   def getAction(self, state):
  587.     """
  588.    From game.py:
  589.    The Agent will receive a GameState (from either {pacman, capture, sonar}.py) and
  590.    must return an action from Directions.{North, South, East, West, Stop}
  591.    """
  592.     "*** YOUR CODE HERE ***"
  593.     util.raiseNotDefined()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement