人工智能实验 搜索策略(pacman)吃豆人

目录

问题1:深度优先算法

问题2:广度优先搜索

问题3:不同的费用

问题4:A*搜索

问题5:查找所有角落

问题6:角落问题:启发式

问题7:吃掉所有的“豆”

对象总览

具体代码


具体项目见

https://github.com/chunxi-alpc/gcx_pacman

问题1:深度优先算法

在search.py中depthFirstSearch函数中实现深度优先算法。

在cmd 输入 Python2 pacman.py -l mediumMaze -p SearchAgent -a fn=dfs

 

[SearchAgent] using function dfs

[SearchAgent] using problem type PositionSearchProblem

Path found with total cost of 130 in 0.0 seconds

Search nodes expanded: 146

Pacman emerges victorious! Score: 380

Average Score: 380.0

Scores:        380.0

Win Rate:      1/1 (1.00)

Record:        Win

对于已经搜索过的状态Pacman棋盘上将显一个叠加物(overlay),并显示出访问的顺序(红色由深到浅). Pacman 在到达目的地的过程中,并不是遍访每个正方形而是把一种走法显示出来。

使用栈Stack数据结构, 则通过DFS算法求得的mediumMaze的解长度应该为130 (假定你将后继元素按getSuccessors得到的顺序压栈; 如果按相反顺序压栈,则可能是244). 这可能不是最短的路径因为为了时间和效率,我们只追求搜索到满足目标测试的情况,DFS扩展深度最大的节点,因此第一次满足目标测试的动作序列不一定是最短的路径。

问题2:广度优先搜索

在search.py中breadthFirstSearch函数中,实现广度优先搜索 (BFS) 算法。

在cmd 输入 Python2 pacman.py -l mediumMaze -p SearchAgent -a fn=bfs

[SearchAgent] using function bfs

[SearchAgent] using problem type PositionSearchProblem

Path found with total cost of 68 in 0.0 seconds

Search nodes expanded: 269

Pacman emerges victorious! Score: 442

Average Score: 442.0

Scores:        442.0

Win Rate:      1/1 (1.00)

Record:        Win

提示: 如Pacman移动太慢,可以试一下选项--frameTime 0

注意: 如果你的搜索代码具有通用性, 则不用做任何修改,该代码将同样能对eight-puzzle搜索问题适用。

问题3:不同的费用

通过修改代价函数,我们鼓励Pacman发现不同路径。例如,有恶魔的区域,我们增加每步的代价,而在食物丰富的区域减少每步的代价,一个理性的Pacman应该相应地调整它的行为。

在search.py的uniformCostSearch函数中,实现一致代价图搜索算法。util.py中有一些数据结构,也许会对你的实现有用。现在你应该能观察到在下面三个样板中的成功行为,所使用的智能体都是UCS(uniform cost search)智能体,其唯一区别是所使用的费用函数(其智能体和费用函数已经帮你写好了):

Python2 pacman.py -l mediumMaze -p SearchAgent -a fn=ucs

Python2 pacman.py -l mediumDottedMaze -p StayEastSearchAgent

Python2 pacman.py -l mediumScaryMaze -p StayWestSearchAgent

注: 由于其指数费用函数,在StayEastSearchAgent 和 StayWestSearchAgent中,你将分别看到很低的和很高的路径费用total  cost(详细细节可见searchAgents.py).

[SearchAgent] using function ucs

[SearchAgent] using problem type PositionSearchProblem

Path found with total cost of 68 in 0.0 seconds

Search nodes expanded: 269

Pacman emerges victorious! Score: 442

Average Score: 442.0

Scores:        442.0

Win Rate:      1/1 (1.00)

Record:        Win

问题4:A*搜索

在search.py的aStarSearch函数中实现A*图搜索 A*输入参数包括一个启发式函数。启发式函数有两个输入变量:搜索问题的状态 (主参数), 和问题本身(相关参考信息). search.py中的nullHeuristic 启发函数是一个普通的实例.可以针对求通过迷宫到达固定点的原问题来测试A*实现,具体可使用Manhattan距离启发(已经在searchAgents.py中实现为 manhattanHeuristic).

Python2 pacman.py -l bigMaze -z .5 -p SearchAgent -a fn=astar,heuristic=manhattanHeuristic

[SearchAgent] using function astar and heuristic manhattanHeuristic

[SearchAgent] using problem type PositionSearchProblem

Path found with total cost of 210 in 0.1 seconds

Search nodes expanded: 549

Pacman emerges victorious! Score: 300

Average Score: 300.0

Scores:        300.0

Win Rate:      1/1 (1.00)

Record:        Win

问题5:查找所有角落

注意:确保你已经完成问题2,然后再来完成问题5,因为问题5依赖于问题2的答案。

A*搜索的真正强大之处,在具有更大挑战性的问题上才能显现。下面,我们需要先构造一个新问题,然后为其设计一个启发式的算法。

在角落迷宫corner mazes中, 四个角上各有一颗豆。我们新的搜索问题是找到穿过迷宫碰到所有四个角的最短路径(不论在迷宫中是否真有食物).  注意,对于象tinyCorners这样的迷宫, 最短路径不一定总是先找最近的食物! 提示: 通过tinyCorners的最短路径需要28步.

在searchAgents.py中实现CornersProblem搜索问题。你需要选择一种状态表示方法,该方法可以对所有必要的信息编码,以便测定所有四个角点是否达到。现在, 搜索智能体应该可解下面的问题:

Python2 pacman.py -l tinyCorners -p SearchAgent -a fn=bfs,prob=CornersProblem

Python2 pacman.py -l mediumCorners -p SearchAgent -a fn=bfs,prob=CornersProblem

进一步,需要定义一个抽象的状态表示,该表示不对无关信息编码(如恶魔的位置, 其他食物的位置等)。特别是不要使用Pacman的GameState作为搜索状态。如果这样,你的代码会非常、非常慢(还出错).

提示: 在实现中,你需要访问的唯一游戏状态是Pacman的起始位置和四个角点的位置。

[SearchAgent] using function bfs

[SearchAgent] using problem type CornersProblem

Path found with total cost of 106 in 0.2 seconds

Search nodes expanded: 1966

Pacman emerges victorious! Score: 434

Average Score: 434.0

Scores:        434.0

Win Rate:      1/1 (1.00)

Record:        Win

问题6:角落问题:启发式

注意:确保你已经完成问题4,然后再来完成问题6,因为问题6依赖于问题4的答案。   

对CornersProblem实现一个启发式搜索cornersHeuristic。请在你的实现前面加上必要的备注。

Python2 pacman.py -l mediumCorners -p AStarCornersAgent -z 0.5

注意:AStarCornersAgent 是 -p SearchAgent -a fn=aStarSearch,prob=CornersProblem,heuristic=cornersHeuristic的缩写。

Path found with total cost of 106 in 0.1 seconds

Search nodes expanded: 692

Pacman emerges victorious! Score: 434

Average Score: 434.0

Scores:        434.0

Win Rate:      1/1 (1.00)

Record:        Win

 

 

问题7:吃掉所有的“豆”

接下来,我们求解一个困难的搜索问题: 使用尽量少的步骤吃掉所有的食物。对此次作业,我们需要定义一个新的搜索问题,在该定义中正确描述吃掉所有食物的问题: 在searchAgents.py中的FoodSearchProblem (已经实现好了). 问题的解定义为一条收集到世界中所有食物的路径。在现在的项目中,不考虑”魔鬼“或"能量药“的存在; 解仅依赖于墙和正常食物在Pacman中的位置(当然,“魔鬼”会损坏解!) 。如果你已经正确地完成了通用搜索算法, 使用null heuristic (等价于一致费用搜索UCS) 的A* 将很快求得testSearch问题的最优解,而不用大家写任何代码(总费用7).

Python2 pacman.py -l testSearch -p AStarFoodSearchAgent

注: AStarFoodSearchAgent是-p SearchAgent -a fn=astar,prob=FoodSearchProblem,heuristic=foodHeuristic的缩写.

你将看到UCS开始慢下来,即使对看起来简单的tinySearch问题。

注意:确保你已经完成问题4,然后再来完成问题7,因为问题7依赖于问题4的答案。

针对FoodSearchProblem,使用一致性启发式函数,在searchAgents.py中完成foodHeuristic。在函数开头添加必要的注释描述你的启发式函数。测试你的Agent:

Python2 pacman.py -l trickySearch -p AStarFoodSearchAgent

问题8:次优搜索

有的时候,即使使用 A* 加上好的启发式,求通过所有“豆”的最优路径也是困难的。此时,我们还是希望能尽快地求得一个足够好的路径。在本节中,你需要写出一个智能体,它总是吃掉最近的豆. 在searchAgents.py中已经实现了ClosestDotSearchAgent, 但缺少一个关键函数,该函数搜索到最近豆的路径。

在文件searchAgents.py中实现findPathToClosestDot函数。

python pacman.py -l bigSearch -p ClosestDotSearchAgent -z .5

提示: 完成 findPathToClosestDot 的最快方式是填满AnyFoodSearchProblem, 该问题缺少目标测试。然后,使用合适的搜索函数来求解问题。解会非常短!

你的ClosestDotSearchAgent 并不总能找到通过迷宫的可能的最短路径。事实上,如果你尝试,你可以做得更好。

对象总览

下面是基础代码中与搜索问题有关的重要对象的总览,供大家参考:

SearchProblem (search.py)

SearchProblem是一个抽象对象,该对象表示状态空间,费用,和问题的目标状态。你只能通过定义在search.py顶上的方法来与SearchProblem交互

PositionSearchProblem (searchAgents.py)

需要处理的一种特别的SearchProblem类型 --- 对应于在迷宫中搜索单个肉丸pellet.

CornersProblem (searchAgents.py)

一种需要定义的特别的SearchProblem问题 --- 目的是搜索出一条到达迷宫中所有四个角点的路径.

FoodSearchProblem (searchAgents.py)

一个特定的需要解决的搜索问题。

Search Function

搜索函数是一个函数,该函数以SearchProblem的一个实例作为输入 , 运行一些算法, 返回值一列到达目标的行为. 搜索函数的实例有depthFirstSearch 和 breadthFirstSearch, 这些都要你编写。我们提供了tinyMazeSearch函数,该函数是一个非常差的函数,只能对tinyMaze得到正确结果

SearchAgent

SearchAgent是实现智能体Agent的类(它与世界交互) 且通过搜索函数做出规划。SearchAgent首先使用所提供的搜索函数规划出到达目标状态的行为,然后一次执行一个动作。

具体代码

# coding=UTF-8
# search.py

"""
In search.py, you will implement generic search algorithms which are called by
Pacman agents (in searchAgents.py).
"""

import util

class SearchProblem:
    """
    This class outlines the structure of a search problem, but doesn't implement
    any of the methods (in object-oriented terminology: an abstract class).

    You do not need to change anything in this class, ever.
    """

    def getStartState(self):
        """
        Returns the start state for the search problem.
        """
        util.raiseNotDefined()

    def isGoalState(self, state):
        """
          state: Search state

        Returns True if and only if the state is a valid goal state.
        """
        util.raiseNotDefined()

    def getSuccessors(self, state):
        """
          state: Search state

        For a given state, this should return a list of triples, (successor,
        action, stepCost), where 'successor' is a successor to the current
        state, 'action' is the action required to get there, and 'stepCost' is
        the incremental cost of expanding to that successor.
        """
        util.raiseNotDefined()

    def getCostOfActions(self, actions):
        """
         actions: A list of actions to take

        This method returns the total cost of a particular sequence of actions.
        The sequence must be composed of legal moves.
        """
        util.raiseNotDefined()

def tinyMazeSearch(problem):
    """
    Returns a sequence of moves that solves tinyMaze.  For any other
    maze, the sequence of moves will be incorrect, so only use this for tinyMaze
    """
    from game import Directions
    s = Directions.SOUTH
    w = Directions.WEST
    return  [s,s,w,s,w,w,s,w]

def depthFirstSearch(problem):
    #初始状态
    s = problem.getStartState()
    #标记已经搜索过的状态集合exstates
    exstates = []
    #用栈实现dfs
    states = util.Stack()
    states.push((s, []))
    #循环终止条件:遍历完毕/目标测试成功
    while not states.isEmpty() and not problem.isGoalState(s):
        state, actions = states.pop()
        exstates.append(state)
        successor = problem.getSuccessors(state)
        for node in successor:
            coordinates = node[0]
            direction = node[1]
            #判断状态是否重复
            if not coordinates in exstates:
                states.push((coordinates, actions + [direction]))
            #把最后搜索的状态赋值到s,以便目标测试
            s = coordinates
    #返回动作序列
    return actions + [direction]
    util.raiseNotDefined()

def breadthFirstSearch(problem):
    #初始状态
    s = problem.getStartState()
    #标记已经搜索过的状态集合exstates
    exstates = []
    #用队列queue实现bfs
    states = util.Queue()
    states.push((s, []))
    while not states.isEmpty():
        state, action = states.pop()
        #目标测试
        if problem.isGoalState(state):
            return action
        #检查重复
        if state not in exstates:
            successor = problem.getSuccessors(state)
            exstates.append(state)
            #把后继节点加入队列
            for node in successor:
                coordinates = node[0]
                direction = node[1]
                if coordinates not in exstates:
                    states.push((coordinates, action + [direction]))
    #返回动作序列
    return action
    util.raiseNotDefined()

def uniformCostSearch(problem):
    #初始状态
    start = problem.getStartState()
    #标记已经搜索过的状态集合exstates
    exstates = []
    #用优先队列PriorityQueue实现ucs
    states = util.PriorityQueue()
    states.push((start, []) ,0)
    while not states.isEmpty():
        state, actions = states.pop()
        #目标测试
        if problem.isGoalState(state):
            return actions
        #检查重复
        if state not in exstates:
            #扩展
            successors = problem.getSuccessors(state)
            for node in successors:
                coordinate = node[0]
                direction = node[1]
                if coordinate not in exstates:
                    newActions = actions + [direction]
                    #ucs比bfs的区别在于getCostOfActions决定节点扩展的优先级
                    states.push((coordinate, actions + [direction]), problem.getCostOfActions(newActions))
        exstates.append(state)
    return actions
    util.raiseNotDefined()

def nullHeuristic(state, problem=None):
    """
    A heuristic function estimates the cost from the current state to the nearest
    goal in the provided SearchProblem.  This heuristic is trivial.
    启发式函数有两个输入变量:搜索问题的状态 (主参数), 和问题本身(相关参考信息)
    """
    return 0

def aStarSearch(problem, heuristic=nullHeuristic):
    "Search the node that has the lowest combined cost f(n) and heuristic g(n) first."
    start = problem.getStartState()
    exstates = []
    # 使用优先队列,每次扩展都是选择当前代价最小的方向
    states = util.PriorityQueue()
    states.push((start, []), nullHeuristic(start, problem))
    nCost = 0
    while not states.isEmpty():
        state, actions = states.pop()
        #目标测试
        if problem.isGoalState(state):
            return actions
        #检查重复
        if state not in exstates:
            #扩展
            successors = problem.getSuccessors(state)
            for node in successors:
                coordinate = node[0]
                direction = node[1]
                if coordinate not in exstates:
                    newActions = actions + [direction]
                    #计算动作代价和启发式函数值得和
                    newCost = problem.getCostOfActions(newActions) + heuristic(coordinate, problem)
                    states.push((coordinate, actions + [direction]), newCost)
        exstates.append(state)
    #返回动作序列
    return actions
    util.raiseNotDefined()

# Abbreviations
bfs = breadthFirstSearch
dfs = depthFirstSearch
astar = aStarSearch
ucs = uniformCostSearch
# coding=UTF-8
# searchAgents.py
# ---------------



"""
This file contains all of the agents that can be selected to control Pacman.  To
select an agent, use the '-p' option when running pacman.py.  Arguments can be
passed to your agent using '-a'.  For example, to load a SearchAgent that uses
depth first search (dfs), run the following command:

> python pacman.py -p SearchAgent -a fn=depthFirstSearch

Commands to invoke other search strategies can be found in the project
description.

Please only change the parts of the file you are asked to.  Look for the lines
that say

"*** YOUR CODE HERE ***"

The parts you fill in start about 3/4 of the way down.  Follow the project
description for details.

Good luck and happy searching!
"""

from game import Directions
from game import Agent
from game import Actions
import util
import time
import search
import sys

class GoWestAgent(Agent):
    "An agent that goes West until it can't."

    def getAction(self, state):
        "The agent receives a GameState (defined in pacman.py)."
        if Directions.WEST in state.getLegalPacmanActions():
            return Directions.WEST
        else:
            return Directions.STOP

#######################################################
# This portion is written for you, but will only work #
#       after you fill in parts of search.py          #
#######################################################

class SearchAgent(Agent):
    """
    This very general search agent finds a path using a supplied search
    algorithm for a supplied search problem, then returns actions to follow that
    path.

    As a default, this agent runs DFS on a PositionSearchProblem to find
    location (1,1)

    Options for fn include:
      depthFirstSearch or dfs
      breadthFirstSearch or bfs


    Note: You should NOT change any code in SearchAgent
    """

    def __init__(self, fn='uniformCostSearch', prob='PositionSearchProblem', heuristic='nullHeuristic'):
        # Warning: some advanced Python magic is employed below to find the right functions and problems

        # Get the search function from the name and heuristic
        if fn not in dir(search):
            raise AttributeError, fn + ' is not a search function in search.py.'
        func = getattr(search, fn)
        if 'heuristic' not in func.func_code.co_varnames:
            print('[SearchAgent] using function ' + fn)
            self.searchFunction = func
        else:
            if heuristic in globals().keys():
                heur = globals()[heuristic]
            elif heuristic in dir(search):
                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))
            # Note: this bit of Python trickery combines the search algorithm and the heuristic
            self.searchFunction = lambda x: func(x, heuristic=heur)

        # Get the search problem type from the name
        if prob not in globals().keys() or not prob.endswith('Problem'):
            raise AttributeError, prob + ' is not a search problem type in SearchAgents.py.'
        self.searchType = globals()[prob]
        print('[SearchAgent] using problem type ' + prob)

    def registerInitialState(self, state):
        """
        This is the first time that the agent sees the layout of the game
        board. Here, we choose a path to the goal. In this phase, the agent
        should compute the path to the goal and store it in a local variable.
        All of the work is done in this method!

        state: a GameState object (pacman.py)
        """
        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)

    def getAction(self, state):
        """
        Returns the next action in the path chosen earlier (in
        registerInitialState).  Return Directions.STOP if there is no further
        action to take.

        state: a GameState object (pacman.py)
        """
        if 'actionIndex' not in dir(self): self.actionIndex = 0
        i = self.actionIndex
        self.actionIndex += 1
        if i < len(self.actions):
            return self.actions[i]
        else:
            return Directions.STOP

class PositionSearchProblem(search.SearchProblem):
    """
    A search problem defines the state space, start state, goal test, successor
    function and cost function.  This search problem can be used to find paths
    to a particular point on the pacman board.

    The state space consists of (x,y) positions in a pacman game.

    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):
        """
        Stores the start and goal.

        gameState: A GameState object (pacman.py)
        costFn: A function from a search state (tuple) to a non-negative number
        goal: A position in the gameState
        """
        self.walls = gameState.getWalls()
        self.startState = gameState.getPacmanPosition()
        if start != None: self.startState = start
        self.goal = goal
        self.costFn = costFn
        self.visualize = visualize
        if warn and (gameState.getNumFood() != 1 or not gameState.hasFood(*goal)):
            print 'Warning: this does not look like a regular search maze'
        self._visited, self._visitedlist, self._expanded = {}, [], 0

    def getStartState(self):
        return self.startState

    def isGoalState(self, state):
        isGoal = state == self.goal

        # For display purposes only
        if isGoal and self.visualize:
            self._visitedlist.append(state)
            import __main__
            if '_display' in dir(__main__):
                if 'drawExpandedCells' in dir(__main__._display): #@UndefinedVariable
                    __main__._display.drawExpandedCells(self._visitedlist) #@UndefinedVariable

        return isGoal

    def getSuccessors(self, state):
        """
        Returns successor states, the actions they require, and a cost of 1.

         As noted in search.py:
             For a given state, this should return a list of triples,
         (successor, action, stepCost), where 'successor' is a
         successor to the current state, 'action' is the action
         required to get there, and 'stepCost' is the incremental
         cost of expanding to that successor
        """

        successors = []
        for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
            x,y = state
            dx, dy = Actions.directionToVector(action)
            nextx, nexty = int(x + dx), int(y + dy)
            if not self.walls[nextx][nexty]:
                nextState = (nextx, nexty)
                cost = self.costFn(nextState)
                successors.append( ( nextState, action, cost) )

        # Bookkeeping for display purposes
        self._expanded += 1 # DO NOT CHANGE
        if state not in self._visited:
            self._visited[state] = True
            self._visitedlist.append(state)

        return successors

    def getCostOfActions(self, actions):
        """
        Returns the cost of a particular sequence of actions. If those actions
        include an illegal move, 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
            cost += self.costFn((x,y))
        return cost

class StayEastSearchAgent(SearchAgent):
    """
    An agent for position search with a cost function that penalizes being in
    positions on the West side of the board.

    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)

class StayWestSearchAgent(SearchAgent):
    """
    An agent for position search with a cost function that penalizes being in
    positions on the East side of the board.

    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]
        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
    xy2 = problem.goal
    return ( (xy1[0] - xy2[0]) ** 2 + (xy1[1] - xy2[1]) ** 2 ) ** 0.5

#####################################################
# This portion is incomplete.  Time to write code!  #
#####################################################

class CornersProblem(search.SearchProblem):
    """
    This search problem finds paths through all four corners of a layout.
    You must select a suitable state space and successor function
    """

    def __init__(self, startingGameState):
        """
        Stores the walls, pacman's starting position and corners.
        """
        self.walls = startingGameState.getWalls()
        self.startingPosition = startingGameState.getPacmanPosition()
        top, right = self.walls.height-2, self.walls.width-2
        self.corners = ((1,1), (1,top), (right, 1), (right, top))
        for corner in self.corners:
            if not startingGameState.hasFood(*corner):
                print 'Warning: no food in corner ' + str(corner)
        self._expanded = 0 # Number of search nodes expanded
        # Please add any code here which you would like to use
        # in initializing the problem
        "*** YOUR CODE HERE ***"
        self.right = right
        self.top = top
        
    def getStartState(self):
        """
        Returns the start state (in your state space, not the full Pacman state
        space)
        """
        "*** YOUR CODE HERE ***"
        #初始节点(开始位置,角落情况)
        allCorners = (False, False, False, False)
        start = (self.startingPosition, allCorners)
        return start
        util.raiseNotDefined()

    def isGoalState(self, state):
        """
        Returns whether this search state is a goal state of the problem.
        """
        "*** YOUR CODE HERE ***"
        #目标测试:四个角落都访问过
        corners = state[1]
        boolean = corners[0] and corners[1] and corners[2] and corners[3]
        return boolean
        util.raiseNotDefined()

    def getSuccessors(self, state):
        """
        Returns successor states, the actions they require, and a cost of 1.

         As noted in search.py:
            For a given state, this should return a list of triples, (successor,
            action, stepCost), where 'successor' is a successor to the current
            state, 'action' is the action required to get there, and 'stepCost'
            is the incremental cost of expanding to that successor
        """
        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
            "*** YOUR CODE HERE ***"
             #   x,y = currentPosition
            x,y = state[0]
            holdCorners = state[1]
            #   dx, dy = Actions.directionToVector(action)
            dx, dy = Actions.directionToVector(action)
            nextx, nexty = int(x + dx), int(y + dy)
            hitsWall = self.walls[nextx][nexty]
            newCorners = ()
            nextState = (nextx, nexty)
            #不碰墙
            if not hitsWall:
                #能到达角落,四种情况判断
                if nextState in self.corners:
                    if nextState == (self.right, 1):
                        newCorners = [True, holdCorners[1], holdCorners[2], holdCorners[3]]
                    elif nextState == (self.right, self.top):
                        newCorners = [holdCorners[0], True, holdCorners[2], holdCorners[3]]
                    elif nextState == (1, self.top):
                        newCorners = [holdCorners[0], holdCorners[1], True, holdCorners[3]]
                    elif nextState == (1,1):
                        newCorners = [holdCorners[0], holdCorners[1], holdCorners[2], True]
                    successor = ((nextState, newCorners), action,  1)
                #去角落的中途
                else:
                    successor = ((nextState, holdCorners), action, 1)
                successors.append(successor)
        self._expanded += 1 
        return successors

    def getCostOfActions(self, actions):
        """
        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
        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
        return len(actions)


def cornersHeuristic(state, problem):
    """
    A heuristic for the CornersProblem that you defined.

      state:   The current search state
               (a data structure you chose in your search problem)

      problem: The CornersProblem instance for this layout.

    This function should always return a number that is a lower bound on the
    shortest path from the state to a goal of the problem; i.e.  it should be
    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)

    "*** YOUR CODE HERE ***"
    position = state[0]
    stateCorners = state[1]
    corners = problem.corners
    top = problem.walls.height-2
    right = problem.walls.width-2
    node = []
    for c in corners:
        if c == (1,1):
            if not stateCorners[3]:
                node.append(c)
        if c == (1, top):
            if not stateCorners[2]:
                node.append(c)
        if c == (right, top):
            if not stateCorners[1]:
                node.append(c)
        if c == (right, 1):
            if not stateCorners[0]:
                node.append(c)
    cost = 0
    currPosition = position
    while len(node) > 0:
        distArr= []
        for i in range(0, len(node)):
            dist = util.manhattanDistance(currPosition, node[i])
            distArr.append(dist)
        mindist = min(distArr)
        cost += mindist
        minDistI= distArr.index(mindist)
        currPosition = node[minDistI]
        del node[minDistI]
    return cost

class AStarCornersAgent(SearchAgent):
    "A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"
    def __init__(self):
        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
    food (dots) in a Pacman game.

    A search state in this problem is a tuple ( pacmanPosition, foodGrid ) where
      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.walls = startingGameState.getWalls()
        self.startingGameState = startingGameState
        self._expanded = 0 # DO NOT CHANGE
        self.heuristicInfo = {} # A dictionary for the heuristic to store information

    def getStartState(self):
        return self.start

    def isGoalState(self, state):
        return state[1].count() == 0

    def getSuccessors(self, state):
        "Returns successor states, the actions they require, and a cost of 1."
        successors = []
        self._expanded += 1 # DO NOT CHANGE
        for direction in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
            x,y = state[0]
            dx, dy = Actions.directionToVector(direction)
            nextx, nexty = int(x + dx), int(y + dy)
            if not self.walls[nextx][nexty]:
                nextFood = state[1].copy()
                nextFood[nextx][nexty] = False
                successors.append( ( ((nextx, nexty), nextFood), direction, 1) )
        return successors

    def getCostOfActions(self, actions):
        """Returns the cost of a particular sequence of actions.  If those actions
        include an illegal move, return 999999"""
        x,y= self.getStartState()[0]
        cost = 0
        for action in actions:
            # figure out the next state and see whether it's legal
            dx, dy = Actions.directionToVector(action)
            x, y = int(x + dx), int(y + dy)
            if self.walls[x][y]:
                return 999999
            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.searchType = FoodSearchProblem
        
def foodHeuristic(state, problem):
    """
    状态是一个元组(pacmanPosition,foodGrid)
    其中foodGrid是一个Grid(参见game.py)
    调用foodGrid.asList()来获取食物坐标列表。

    如果你想存储信息,可以在其他调用中重复使用
    启发式,你可以使用一个名为problem.heuristicInfo的字典
    例如,如果您只想计算一次墙壁并存储它
    尝试:problem.heuristicInfo ['wallCount'] = problem.walls.count()
    对此启发式的后续调用可以访问
    problem.heuristicInfo [ 'wallCount']
    """
    position, foodGrid = state
    "*** YOUR CODE HERE ***"
    hvalue = 0
    food_available = []
    total_distance = 0
    #处理食物的位置,以此构造启发式函数
    for i in range(0,foodGrid.width):
        for j in range(0,foodGrid.height):
            if (foodGrid[i][j] == True):
                food_location = (i,j)
                food_available.append(food_location)
    #没有食物就不用找了
    if (len(food_available) == 0):
            return 0        
    #初始化距离(current_food,select_food,distance)
    max_distance=((0,0),(0,0),0)
    for current_food in food_available:
        for select_food in food_available:
            if(current_food==select_food):
                pass
            else:
                #使用曼哈顿距离构造启发式函数
                distance = util.manhattanDistance(current_food,select_food)
                if(max_distance[2] < distance):
                    max_distance = (current_food,select_food,distance)
    #把起点和第一个搜索的食物连接起来
    #处理只有一个食物的情况
    if(max_distance[0]==(0,0) and max_distance[1]==(0,0)):
        hvalue = util.manhattanDistance(position,food_available[0])
    else: 
        d1 = util.manhattanDistance(position,max_distance[0])
        d2 = util.manhattanDistance(position,max_distance[1])
        hvalue = max_distance[2] + min(d1,d2)
    
    return hvalue

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
            self.actions += nextPathSegment
            for action in nextPathSegment:
                legal = currentState.getLegalActions()
                if action not in legal:
                    t = (str(action), str(currentState))
                    raise Exception, 'findPathToClosestDot returned an illegal move: %s!\n%s' % t
                currentState = currentState.generateSuccessor(0, action)
        self.actionIndex = 0
        print 'Path found with cost %d.' % len(self.actions)

    def findPathToClosestDot(self, gameState):
        """
        Returns a path (a list of actions) to the closest dot, starting from
        gameState.
        """
        # Here are some useful elements of the startState
        startPosition = gameState.getPacmanPosition()
        food = gameState.getFood()
        walls = gameState.getWalls()
        problem = AnyFoodSearchProblem(gameState)

        "*** YOUR CODE HERE ***"
        return search.aStarSearch(problem)
        util.raiseNotDefined()

class AnyFoodSearchProblem(PositionSearchProblem):
    """
    A search problem for finding a path to any food.

    This search problem is just like the PositionSearchProblem, but has a
    different goal test, which you need to fill in below.  The state space and
    successor function do not need to be changed.

    The class definition above, AnyFoodSearchProblem(PositionSearchProblem),
    inherits the methods of the PositionSearchProblem.

    You can use this search problem to help you fill in the findPathToClosestDot
    method.
    """

    def __init__(self, gameState):
        "Stores information from the gameState.  You don't need to change this."
        # Store the food for later reference
        self.food = gameState.getFood()

        # Store info for the PositionSearchProblem (no need to change this)
        self.walls = gameState.getWalls()
        self.startState = gameState.getPacmanPosition()
        self.costFn = lambda x: 1
        self._visited, self._visitedlist, self._expanded = {}, [], 0
        # DO NOT CHANGE

    def isGoalState(self, state):
        x,y = state
        if self.food[x][y]:
            return True
        else:
            return False
        """
        The state is Pacman's position. Fill this in with a goal test that will
        complete the problem definition.
        """
        x,y = state
        "*** YOUR CODE HERE ***"
        foodGrid = self.food
        if (foodGrid[x][y] == True) or (foodGrid.count() == 0):
                return True
        util.raiseNotDefined()

        
##################
# Mini-contest 1 #
##################
class ApproximateSearchAgent(Agent):
        def registerInitialState(self, state):
        self.walls = state.getWalls()
        self.mark = 0
        self.curPos = state.getPacmanPosition()
        self.path = []
        self.path.append(self.curPos)
        self.starttime = time.time()
        self.path = ApproximateSearchAgent.findPath2(self,state)
        self.cost = 0
        self.DisRecord = {}
        self.mark = 0
        self.disTime = 0
        self.pT = 0
        self.m = [[0 for col in range(450)] for row in range(450)]
        ApproximateSearchAgent.initFloyed(self)
      #   print ApproximateSearchAgent.getTotalDistance(self,self.path,state)
    #    print self.path
    #     print len(self.path)
        self.path = ApproximateSearchAgent.TwoOpt(self,state)
    #    print ApproximateSearchAgent.brutalDis(self,self.path,state)
    #     print time.time() - self.starttime
        
    def initFloyed(self):
        size = len(self.path)
        for i in range(0,size):
            x,y=self.path[i]
            if (x+1,y) in self.path:
                self.m[x+y*30][x+1+y*30]=1
                self.m[x+1+y*30][x+y*30]=1
            if (x-1,y) in self.path:
                self.m[x+y*30][x-1+y*30]=1
                self.m[x-1+y*30][x+y*30]=1
            if (x,y+1) in self.path:
                self.m[x+(y+1)*30][x+y*30]=1
                self.m[x+y*30][x+(y+1)*30]=1
            if (x,y-1) in self.path:
                self.m[x+(y-1)*30][x+y*30]=1
                self.m[x+y*30][x+(y-1)*30]=1   
        for k in range(0,size):
            for i in range(0,size):
                if not(i==k):
                    for j in range(0,size):
                        if not(i==j) and not(j==k):
                            tx,ty=self.path[k]
                            pk=tx+ty*30
                            tx,ty=self.path[i]
                            pi=tx+ty*30
                            tx,ty=self.path[j]
                            pj=tx+ty*30
                            if not(self.m[pi][pk]==0) and not(self.m[pk][pj]==0):
                                if self.m[pi][pj]==0 or self.m[pi][pk]+self.m[pk][pj]<self.m[pi][pj]:
                                    self.m[pi][pj]=self.m[pi][pk]+self.m[pk][pj]
                                    self.m[pj][pi]=self.m[pi][pj]
        print(self.m[181][121])

    def findPath(self, state):
        originPath = []
        foodMap = state.getFood()
        foodMap = foodMap.asList()
        curPos = state.getPacmanPosition()
        originPath.append(curPos)
        minDis = 9999999
        nextpos = curPos
        while len(foodMap) > 0:
            minDis = 9999999
            for pos in foodMap:
                t = util.manhattanDistance(curPos,pos)
                if t < minDis:
                    minDis = t
                    nextpos = pos
            originPath.append(nextpos) 
            foodMap.remove(nextpos)
            curPos = nextpos
        return originPath

    # greedy path
    def findPath2(self, state):
        from game import Directions
        s = Directions.SOUTH
        w = Directions.WEST
        n = Directions.NORTH
        e = Directions.EAST
        originPath = []
        foodMap = state.getFood()
        unvisited = foodMap.asList()
        curPos = state.getPacmanPosition()
        originPath.append(curPos)
        while len(unvisited) > 0:
            minDis = 999999
            minMD = 999999
            for pos in unvisited:
             #   print curPos, pos
                t = util.manhattanDistance(curPos,pos)
                if t < minDis:
                    tt = mazeDistance(curPos,pos,state)
                    if tt < minMD:
                        minDis = t
                        minMD = tt
                        nextpos = pos
            
            prob = PositionSearchProblem(state, start=curPos, goal=nextpos, warn=False, visualize=False)
            move = search.bfs(prob)[0]
            x, y = curPos
            if move == s:
                y -= 1
            if move == w:
                x -= 1
            if move == n:
                y += 1
            if move == e:
                x += 1
            curPos = (x,y)
            if curPos in unvisited:
                unvisited.remove(curPos)
                originPath.append(curPos)
        return originPath


    def TwoOpt(self, state):
        size = len(self.path)
        improve = 0
        bestDis = ApproximateSearchAgent.getTotalDistance(self,self.path,state)
        while improve < 20:
            #print bestDis
            for i in range(1,size - 1):
                for k in range (i+1, min(i+size/2,size)):
                    newPath = ApproximateSearchAgent.TwoOptSwap(self,i,k)
                    newDis = ApproximateSearchAgent.newTotalDistance(self,i,k,self.path,state,bestDis)     
                    if newDis <= 285:
                        self.path = newPath
                        return newPath
                    if newDis < bestDis:
                        improve = 0
                        self.path = newPath
                        bestDis = newDis
            improve += 1
        return newPath

    def TwoOptSwap(self,i,k):
        size = len(self.path)
        ansPath = list(self.path[0:i])
        rev = list(self.path[i:k+1])
        rev.reverse()
        end = list(self.path[k+1:size])
        return ansPath + rev + end

    def newTotalDistance(self,i,k,thispath,state,oldDis):
        newDis= oldDis + ApproximateSearchAgent.getDis(self,i-1,k,state,thispath) + ApproximateSearchAgent.getDis(self,i,k+1,state,thispath)- ApproximateSearchAgent.getDis(self,i-1,i,state,thispath)- ApproximateSearchAgent.getDis(self,k,k+1,state,thispath)  
        # paht i-1,k
        return newDis



    def getDis(self,start,end,state,thispath):
        if end >= len(thispath):
            return 0
        tx,ty=thispath[start]
        p1=tx+ty*30
        tx,ty=thispath[end]
        p2=tx+ty*30
        return self.m[p1][p2]

    def getTotalDistance(self,thispath,state):
      #  dt0 = time.time()
        totalDis = 0
        for i in range(len(thispath) - 1):
            tx,ty=thispath[i]
            p1=tx+ty*30
            tx,ty=thispath[i+1]
            p2=tx+ty*30
            totalDis += self.m[p1][p2]
            """
            if self.DisRecord.has_key((thispath[i],thispath[i+1])):
                totalDis += self.DisRecord[(thispath[i],thispath[i+1])]
            else:
                self.DisRecord[(thispath[i],thispath[i+1])] = mazeDistance(thispath[i],thispath[i+1],state)
                totalDis += self.DisRecord[(thispath[i],thispath[i+1])]
            """
            #totalDis += mazeDistance(thispath[i],thispath[i+1],state)
   #     dt = time.time() - dt0
   #     self.disTime += dt
    #    print self.disTime
        return totalDis

    def brutalDis(self,thispath,state):
        totalDis = 0
        for i in range(len(thispath) - 1):
            totalDis += mazeDistance(thispath[i],thispath[i+1],state)
        return totalDis

    def getAction(self, state):
        if self.pT == 0:
          #  print self.disTime
            self.pT = 1
        curPos = state.getPacmanPosition() 
        if self.path[self.mark] == curPos:
            self.mark += 1
        nextpos = self.path[self.mark]
        prob = PositionSearchProblem(state, start=curPos, goal=nextpos, warn=False, visualize=False)
        move = (search.bfs(prob))[0]
        self.cost += 1
        #print self.cost
        #print time.time() - self.starttime 
        return move
        
def mazeDistance(point1, point2, gameState):
    """
    Returns the maze distance between any two points, using the search functions
    you have already built.  The gameState can be any game state -- Pacman's position
    in that state is ignored.
    Example usage: mazeDistance( (2,4), (5,6), gameState)
    This might be a useful helper function for your ApproximateSearchAgent.
    """
    x1, y1 = point1
    x2, y2 = point2
    walls = gameState.getWalls()
    assert not walls[x1][y1], 'point1 is a wall: ' + point1
    assert not walls[x2][y2], 'point2 is a wall: ' + str(point2)
    prob = PositionSearchProblem(gameState, start=point1, goal=point2, warn=False, visualize=False)
    return len(search.bfs(prob))

 

  • 71
    点赞
  • 399
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值