人工智能实验 搜索策略(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 utilclass SearchProblem:"""This class outlines the structure of a search problem, but doesn't implementany 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 stateReturns True if and only if the state is a valid goal state."""util.raiseNotDefined()def getSuccessors(self, state):"""state: Search stateFor a given state, this should return a list of triples, (successor,action, stepCost), where 'successor' is a successor to the currentstate, 'action' is the action required to get there, and 'stepCost' isthe incremental cost of expanding to that successor."""util.raiseNotDefined()def getCostOfActions(self, actions):"""actions: A list of actions to takeThis 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 othermaze, the sequence of moves will be incorrect, so only use this for tinyMaze"""from game import Directionss = Directions.SOUTHw = Directions.WESTreturn [s,s,w,s,w,w,s,w]def depthFirstSearch(problem):#初始状态s = problem.getStartState()#标记已经搜索过的状态集合exstatesexstates = []#用栈实现dfsstates = 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()#标记已经搜索过的状态集合exstatesexstates = []#用队列queue实现bfsstates = 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 actionutil.raiseNotDefined()def uniformCostSearch(problem):#初始状态start = problem.getStartState()#标记已经搜索过的状态集合exstatesexstates = []#用优先队列PriorityQueue实现ucsstates = 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 actionsutil.raiseNotDefined()def nullHeuristic(state, problem=None):"""A heuristic function estimates the cost from the current state to the nearestgoal in the provided SearchProblem. This heuristic is trivial.启发式函数有两个输入变量:搜索问题的状态 (主参数), 和问题本身(相关参考信息)"""return 0def 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 = 0while 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 actionsutil.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=depthFirstSearchCommands 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 sysclass 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.WESTelse: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 searchalgorithm for a supplied search problem, then returns actions to follow thatpath.As a default, this agent runs DFS on a PositionSearchProblem to findlocation (1,1)Options for fn include:depthFirstSearch or dfsbreadthFirstSearch or bfsNote: 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 heuristicif 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 = funcelse: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 heuristicself.searchFunction = lambda x: func(x, heuristic=heur)# Get the search problem type from the nameif 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 gameboard. Here, we choose a path to the goal. In this phase, the agentshould 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 problemself.actions = self.searchFunction(problem) # Find a pathtotalCost = 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 (inregisterInitialState). Return Directions.STOP if there is no furtheraction to take.state: a GameState object (pacman.py)"""if 'actionIndex' not in dir(self): self.actionIndex = 0i = self.actionIndexself.actionIndex += 1if i < len(self.actions):return self.actions[i]else:return Directions.STOPclass PositionSearchProblem(search.SearchProblem):"""A search problem defines the state space, start state, goal test, successorfunction and cost function. This search problem can be used to find pathsto 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 numbergoal: A position in the gameState"""self.walls = gameState.getWalls()self.startState = gameState.getPacmanPosition()if start != None: self.startState = startself.goal = goalself.costFn = costFnself.visualize = visualizeif 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 = {}, [], 0def getStartState(self):return self.startStatedef isGoalState(self, state):isGoal = state == self.goal# For display purposes onlyif 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) #@UndefinedVariablereturn isGoaldef 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 asuccessor to the current state, 'action' is the actionrequired to get there, and 'stepCost' is the incrementalcost of expanding to that successor"""successors = []for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:x,y = statedx, 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 purposesself._expanded += 1 # DO NOT CHANGEif state not in self._visited:self._visited[state] = Trueself._visitedlist.append(state)return successorsdef getCostOfActions(self, actions):"""Returns the cost of a particular sequence of actions. If those actionsinclude an illegal move, return 999999."""if actions == None: return 999999x,y= self.getStartState()cost = 0for action in actions:# Check figure out the next state and see whether its' legaldx, dy = Actions.directionToVector(action)x, y = int(x + dx), int(y + dy)if self.walls[x][y]: return 999999cost += self.costFn((x,y))return costclass StayEastSearchAgent(SearchAgent):"""An agent for position search with a cost function that penalizes being inpositions 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.uniformCostSearchcostFn = 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 inpositions 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.uniformCostSearchcostFn = 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 = positionxy2 = problem.goalreturn abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1])def euclideanHeuristic(position, problem, info={}):"The Euclidean distance heuristic for a PositionSearchProblem"xy1 = positionxy2 = problem.goalreturn ( (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-2self.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 = rightself.top = topdef getStartState(self):"""Returns the start state (in your state space, not the full Pacman statespace)""""*** YOUR CODE HERE ***"#初始节点(开始位置,角落情况)allCorners = (False, False, False, False)start = (self.startingPosition, allCorners)return startutil.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 booleanutil.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 currentstate, '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 = currentPositionx,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 successorsdef getCostOfActions(self, actions):"""Returns the cost of a particular sequence of actions. If those actionsinclude an illegal move, return 999999. This is implemented for you."""if actions == None: return 999999x,y= self.startingPositionfor action in actions:dx, dy = Actions.directionToVector(action)x, y = int(x + dx), int(y + dy)if self.walls[x][y]: return 999999return 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 theshortest path from the state to a goal of the problem; i.e. it should beadmissible (as well as consistent)."""corners = problem.corners # These are the corner coordinateswalls = 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.cornerstop = problem.walls.height-2right = problem.walls.width-2node = []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 = 0currPosition = positionwhile len(node) > 0:distArr= []for i in range(0, len(node)):dist = util.manhattanDistance(currPosition, node[i])distArr.append(dist)mindist = min(distArr)cost += mindistminDistI= distArr.index(mindist)currPosition = node[minDistI]del node[minDistI]return costclass AStarCornersAgent(SearchAgent):"A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"def __init__(self):self.searchFunction = lambda prob: search.aStarSearch(prob, cornersHeuristic)self.searchType = CornersProblemclass FoodSearchProblem:"""A search problem associated with finding the a path that collects all of thefood (dots) in a Pacman game.A search state in this problem is a tuple ( pacmanPosition, foodGrid ) wherepacmanPosition: a tuple (x,y) of integers specifying Pacman's positionfoodGrid: 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 = startingGameStateself._expanded = 0 # DO NOT CHANGEself.heuristicInfo = {} # A dictionary for the heuristic to store informationdef getStartState(self):return self.startdef isGoalState(self, state):return state[1].count() == 0def getSuccessors(self, state):"Returns successor states, the actions they require, and a cost of 1."successors = []self._expanded += 1 # DO NOT CHANGEfor 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] = Falsesuccessors.append( ( ((nextx, nexty), nextFood), direction, 1) )return successorsdef getCostOfActions(self, actions):"""Returns the cost of a particular sequence of actions. If those actionsinclude an illegal move, return 999999"""x,y= self.getStartState()[0]cost = 0for action in actions:# figure out the next state and see whether it's legaldx, dy = Actions.directionToVector(action)x, y = int(x + dx), int(y + dy)if self.walls[x][y]:return 999999cost += 1return costclass AStarFoodSearchAgent(SearchAgent):"A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"def __init__(self):self.searchFunction = lambda prob: search.aStarSearch(prob, foodHeuristic)self.searchType = FoodSearchProblemdef 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 = 0food_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):passelse:#使用曼哈顿距离构造启发式函数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 hvalueclass ClosestDotSearchAgent(SearchAgent):"Search for all food using a sequence of searches"def registerInitialState(self, state):self.actions = []currentState = statewhile(currentState.getFood().count() > 0):nextPathSegment = self.findPathToClosestDot(currentState) # The missing pieceself.actions += nextPathSegmentfor 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' % tcurrentState = currentState.generateSuccessor(0, action)self.actionIndex = 0print 'Path found with cost %d.' % len(self.actions)def findPathToClosestDot(self, gameState):"""Returns a path (a list of actions) to the closest dot, starting fromgameState."""# Here are some useful elements of the startStatestartPosition = 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 adifferent goal test, which you need to fill in below. The state space andsuccessor 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 findPathToClosestDotmethod."""def __init__(self, gameState):"Stores information from the gameState. You don't need to change this."# Store the food for later referenceself.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: 1self._visited, self._visitedlist, self._expanded = {}, [], 0# DO NOT CHANGEdef isGoalState(self, state):x,y = stateif self.food[x][y]:return Trueelse:return False"""The state is Pacman's position. Fill this in with a goal test that willcomplete the problem definition."""x,y = state"*** YOUR CODE HERE ***"foodGrid = self.foodif (foodGrid[x][y] == True) or (foodGrid.count() == 0):return Trueutil.raiseNotDefined()##################
# Mini-contest 1 #
##################
class ApproximateSearchAgent(Agent):def registerInitialState(self, state):self.walls = state.getWalls()self.mark = 0self.curPos = state.getPacmanPosition()self.path = []self.path.append(self.curPos)self.starttime = time.time()self.path = ApproximateSearchAgent.findPath2(self,state)self.cost = 0self.DisRecord = {}self.mark = 0self.disTime = 0self.pT = 0self.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.starttimedef 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]=1self.m[x+1+y*30][x+y*30]=1if (x-1,y) in self.path:self.m[x+y*30][x-1+y*30]=1self.m[x-1+y*30][x+y*30]=1if (x,y+1) in self.path:self.m[x+(y+1)*30][x+y*30]=1self.m[x+y*30][x+(y+1)*30]=1if (x,y-1) in self.path:self.m[x+(y-1)*30][x+y*30]=1self.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*30tx,ty=self.path[i]pi=tx+ty*30tx,ty=self.path[j]pj=tx+ty*30if 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] 0:minDis = 9999999for pos in foodMap:t = util.manhattanDistance(curPos,pos)if t < minDis:minDis = tnextpos = posoriginPath.append(nextpos) foodMap.remove(nextpos)curPos = nextposreturn originPath# greedy pathdef findPath2(self, state):from game import Directionss = Directions.SOUTHw = Directions.WESTn = Directions.NORTHe = Directions.EASToriginPath = []foodMap = state.getFood()unvisited = foodMap.asList()curPos = state.getPacmanPosition()originPath.append(curPos)while len(unvisited) > 0:minDis = 999999minMD = 999999for pos in unvisited:# print curPos, post = util.manhattanDistance(curPos,pos)if t < minDis:tt = mazeDistance(curPos,pos,state)if tt < minMD:minDis = tminMD = ttnextpos = posprob = PositionSearchProblem(state, start=curPos, goal=nextpos, warn=False, visualize=False)move = search.bfs(prob)[0]x, y = curPosif move == s:y -= 1if move == w:x -= 1if move == n:y += 1if move == e:x += 1curPos = (x,y)if curPos in unvisited:unvisited.remove(curPos)originPath.append(curPos)return originPathdef TwoOpt(self, state):size = len(self.path)improve = 0bestDis = ApproximateSearchAgent.getTotalDistance(self,self.path,state)while improve < 20:#print bestDisfor 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 = newPathreturn newPathif newDis < bestDis:improve = 0self.path = newPathbestDis = newDisimprove += 1return newPathdef 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 + enddef 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,kreturn newDisdef getDis(self,start,end,state,thispath):if end >= len(thispath):return 0tx,ty=thispath[start]p1=tx+ty*30tx,ty=thispath[end]p2=tx+ty*30return self.m[p1][p2]def getTotalDistance(self,thispath,state):# dt0 = time.time()totalDis = 0for i in range(len(thispath) - 1):tx,ty=thispath[i]p1=tx+ty*30tx,ty=thispath[i+1]p2=tx+ty*30totalDis += 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.disTimereturn totalDisdef brutalDis(self,thispath,state):totalDis = 0for i in range(len(thispath) - 1):totalDis += mazeDistance(thispath[i],thispath[i+1],state)return totalDisdef getAction(self, state):if self.pT == 0:# print self.disTimeself.pT = 1curPos = state.getPacmanPosition() if self.path[self.mark] == curPos:self.mark += 1nextpos = 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 movedef mazeDistance(point1, point2, gameState):"""Returns the maze distance between any two points, using the search functionsyou have already built. The gameState can be any game state -- Pacman's positionin that state is ignored.Example usage: mazeDistance( (2,4), (5,6), gameState)This might be a useful helper function for your ApproximateSearchAgent."""x1, y1 = point1x2, y2 = point2walls = gameState.getWalls()assert not walls[x1][y1], 'point1 is a wall: ' + point1assert 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))
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
