You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Pacman/doc/202002422003-黄结伦-实验1.md

541 lines
20 KiB

2 years ago
# Pacman 实验1
## 一.实验目的
1.加深对经典的DFSBFS一致代价搜索A*搜索的理解,并掌握其初步的使用方法
## 二.实验原理
### 1.宽度优先搜索
**宽度优先搜索**bread-first search)是简单搜索策略,先扩展根节点,接着扩展根节点的所有后继,然后再扩展它们的后继,以此类推。其伪代码如下![image-20221021212311445](D:\AI\Subject\Pacman\doc\image-20221021212311445.png)
下图显示了一个简单二叉树的搜索过程
![image-20221021212559638](D:\AI\Subject\Pacman\doc\image-20221021212559638.png)
### 2.深度优先搜索
**深度优先搜索**(depth-first search)总是扩展搜索树的当前边缘结点集中最深的节点。搜索很快推进到搜索树的最深层,那里的结点没有后继。当那些结点扩展完之后,就从边缘结点集中去掉,然后搜索算法回溯到下一个还有未扩展后继的深度稍浅的结点。其伪代码如下![image-20221021212636227](D:\AI\Subject\Pacman\doc\image-20221021212636227.png)
下图显示了一个简单的二叉树搜索过程
![image-20221021212936449](D:\AI\Subject\Pacman\doc\image-20221021212936449.png)
### 3.一致代价搜索
**一致代价搜索**总是扩展路径消耗最小的节点N。N点的路径消耗等于前一节点N-1的路径消耗加上N-1到N节点的路径消耗。其伪代码如下
![image-20221021213229986](D:\AI\Subject\Pacman\doc\image-20221021213229986.png)
下图举了一个简单的例子说明一致代价搜索
![image-20221021213316266](D:\AI\Subject\Pacman\doc\image-20221021213316266.png)
### 4.A*搜索
**A*搜索**对结点的评估结合了g(n),即到达此结点已经花费的代价,和A(n),从该结点到目标结点所花代价:
`f(n)=g(n)+h(n)`
由于g(n)是从开始结点到结点n的路径代价,而A(n)是从结点n到目标结点的最小代价路径的估计值,因此
` f(n)= 经过结点n的最小代价解的估计代价`
这样,如果我们想要找到最小代价的解,首先扩展g(n)+ A(n)值最小的结点是合理的.可以发现这个策略不仅仅合理:假设启发式函数H(n)满足特定的条件,A*搜索既是完备的也是最优的.算法与一致代价搜索类似,除了A*使用g+h而不是g.
下图举了一个简单的例子说明A*搜索的过程
![image-20221021213939136](D:\AI\Subject\Pacman\doc\image-20221021213939136.png)
## 三.实验内容
1.完成search文件夹中search.py中的depthFirstSearchbreadFirstSearchuniformCostSearchaStarSearch四个函数。
在编写完代码后可在终端中的search目录下执行`python2 .\autograder.py` 命令,即可查看是否通过!
## 四.实验结果
搜索算法中四个函数的具体实现如下
### 1.depthFirstSearch
```python
def depthFirstSearch(problem):
"""
Search the deepest nodes in the search tree first.
Your search algorithm needs to return a list of actions that reaches the
goal. Make sure to implement a graph search algorithm.
To get started, you might want to try some of these simple commands to
understand the search problem that is being passed in:
print "Start:", problem.getStartState()
print "Is the start a goal?", problem.isGoalState(problem.getStartState())
print "Start's successors:", problem.getSuccessors(problem.getStartState())
"""
"*** YOUR CODE HERE ***"
visited_node = []
myStack= util.Stack()
actions = []
s = problem.getStartState()
if problem.isGoalState(s):
return actions
myStack.push((s, actions))
while not myStack.isEmpty():
state = myStack.pop()
if state[0] in visited_node:
continue
visited_node.append(state[0])
actions = state[1]
if (problem.isGoalState(state[0])):
return actions
for successor in problem.getSuccessors(state[0]):
child_state = successor[0]
action = successor[1]
sub_action = list(actions)
if not child_state in visited_node:
sub_action.append(action)
myStack.push((child_state, sub_action))
return actions
util.raiseNotDefined()
```
### 2.breadFirstSearch
```python
def breadthFirstSearch(problem):
"""Search the shallowest nodes in the search tree first."""
"*** YOUR CODE HERE ***"
visited_node = []
myQueue = util.Queue()
actions = []
s = problem.getStartState()
if problem.isGoalState(s):
return actions
myQueue.push((s, actions))
while not myQueue.isEmpty():
state = myQueue.pop()
if state[0] in visited_node:
continue
visited_node.append(state[0])
actions = state[1]
if (problem.isGoalState(state[0])):
return actions
for successor in problem.getSuccessors(state[0]):
child_state = successor[0]
action = successor[1]
sub_action = list(actions)
if not child_state in visited_node:
sub_action.append(action)
myQueue.push((child_state, sub_action))
return actions
util.raiseNotDefined()
```
### 3.uniformCostSearch
```python
def uniformCostSearch(problem):
"""Search the node of least total cost first."""
"*** YOUR CODE HERE ***"
visited_node = []
mypriorityQueue = util.PriorityQueue()
actions = []
s = problem.getStartState()
if problem.isGoalState(s):
return actions
mypriorityQueue.push((s, actions), 0)
while not mypriorityQueue.isEmpty():
state = mypriorityQueue.pop()
if state[0] in visited_node:
continue
visited_node.append(state[0])
actions = state[1]
if (problem.isGoalState(state[0])):
return actions
for successor in problem.getSuccessors(state[0]):
child_state = successor[0]
action = successor[1]
sub_action = list(actions)
if not child_state in visited_node:
sub_action.append(action)
mypriorityQueue.push((child_state, sub_action), problem.getCostOfActions(sub_action))
return actions
util.raiseNotDefined()
```
### 4.aStarSearch
```python
def aStarSearch(problem, heuristic=nullHeuristic):
"""Search the node that has the lowest combined cost and heuristic first."""
"*** YOUR CODE HERE ***"
visited_node = []
mypriorityQueue = util.PriorityQueue()
actions = []
s = problem.getStartState()
if problem.isGoalState(s):
return actions
mypriorityQueue.push((s, actions), 0)
while not mypriorityQueue.isEmpty():
state = mypriorityQueue.pop()
if state[0] in visited_node:
continue
visited_node.append(state[0])
actions = state[1]
if (problem.isGoalState(state[0])):
return actions
for successor in problem.getSuccessors(state[0]):
child_state = successor[0]
action = successor[1]
sub_action = list(actions)
if not child_state in visited_node:
sub_action.append(action)
mypriorityQueue.push((child_state, sub_action),heuristic(child_state, problem)
+ problem.getCostOfActions(sub_action))
return actions
util.raiseNotDefined()
```
在写完这四个函数后,在终端中输入 `python2 .\autograder.py`
![image-20221022153157435](D:\AI\Subject\Pacman\doc\image-20221022153157435.png)
等到一段时间后,可以得到测试结果
![image-20221022153238236](D:\AI\Subject\Pacman\doc\image-20221022153238236.png)
测试通过!
## 五.附加实验
在完成了search实验的四个搜索算法的基础上进一步完成附加实验包括[Corners Problem: Representation](http://ai.berkeley.edu/search.html#Q5)[Corners Problem: Heuristic](http://ai.berkeley.edu/search.html#Q6)[Eating All The Dots: Heuristic](http://ai.berkeley.edu/search.html#Q7)[Suboptimal Search](http://ai.berkeley.edu/search.html#Q8).
### 1.Corner Problem
本关主要是实现一个找到所有角落的启发式函数,实现思路为先获取地图的大小,然后记录每个后继的坐标,然后通过比较判断是否为地图角落,实现代码如下
```python
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 # DO NOT CHANGE; Number of search nodes expanded
# Please add any code here which you would like to use
# in initializing the problem
"*** YOUR CODE HERE ***"
self.top = top
self.right = right
```
```python
def getStartState(self):
"""
Returns the start state (in your state space, not the full Pacman state
space)
"""
"*** YOUR CODE HERE ***"
startState = (self.startingPosition,[])
return startState
util.raiseNotDefined()
```
```python
def isGoalState(self, state):
"""
Returns whether this search state is a goal state of the problem.
"""
"*** YOUR CODE HERE ***"
result = state[1]
if state[0] in self.corners:
if state[0] not in result:
result.append(state[0])
if (len(result) == 4):
return True
else:
return False
util.raiseNotDefined()
```
```python
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
# Here's a code snippet for figuring out whether a new position hits a wall:
# x,y = currentPosition
# dx, dy = Actions.directionToVector(action)
# nextx, nexty = int(x + dx), int(y + dy)
# hitsWall = self.walls[nextx][nexty]
"*** YOUR CODE HERE ***"
x, y = state[0]
dx, dy = Actions.directionToVector(action)
nextx, nexty = int(x + dx), int(y + dy)
visited = list(state[1])
if not self.walls[nextx][nexty]:
nextState = (nextx, nexty)
cost = 1
if nextState in self.corners and nextState not in visited:
visited.append(nextState)
successors.append(((nextState, visited), action, cost))
self._expanded += 1 # DO NOT CHANGE
return successors
```
### 2.Corners Heuristic
本关实现一个走到所有角落的最短路径的启发式函数为之后的搜索作准备实现思路为通过last记录四个角落然后依次计算所有后继至四个角落的曼哈顿距离求和之后取最小值该后继作为下一步实现代码如下
```python
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 ***"
visited = state[1]
now_state = state[0]
Heuristic = 0
last = []
if (problem.isGoalState(state)):
return 0
for i in corners:
if i not in visited:
last.append(i)
pos = now_state
cost = 999999
while len(last) != 0:
for i in last:
if cost > (abs(pos[0] - i[0]) + abs(pos[1] - i[1])):
min_con = i
cost = (abs(pos[0] - i[0]) + abs(pos[1] - i[1]))
Heuristic += cost
pos = min_con
cost = 999999
last.remove(min_con)
return Heuristic
```
### 3.Food Heuristic
本关实现一个新的搜索食物的启发式函数,使找到所有食物的路径最小。实现思路为先找到代价最大的食物,然后根据当前位置与最大代价的食物的相对位置,计算出已经吃掉的食物数量,先将代价小的食物都吃掉,最后再吃代价大的食物,即为最短路径。实现代码如下,
```python
def foodHeuristic(state, problem):
"""
Your heuristic for the FoodSearchProblem goes here.
This heuristic must be consistent to ensure correctness. First, try to come
up with an admissible heuristic; almost all admissible heuristics will be
consistent as well.
If using A* ever finds a solution that is worse uniform cost search finds,
your heuristic is *not* consistent, and probably not admissible! On the
other hand, inadmissible or inconsistent heuristics may find optimal
solutions, so be careful.
The state is a tuple ( pacmanPosition, foodGrid ) where foodGrid is a Grid
(see game.py) of either True or False. You can call foodGrid.asList() to get
a list of food coordinates instead.
If you want access to info like walls, capsules, etc., you can query the
problem. For example, problem.walls gives you a Grid of where the walls
are.
If you want to *store* information to be reused in other calls to the
heuristic, there is a dictionary called problem.heuristicInfo that you can
use. For example, if you only want to count the walls once and store that
value, try: problem.heuristicInfo['wallCount'] = problem.walls.count()
Subsequent calls to this heuristic can access
problem.heuristicInfo['wallCount']
"""
position, foodGrid = state
"*** YOUR CODE HERE ***"
lsFoodGrid = foodGrid.asList()
last = list(lsFoodGrid)
Heuristic = 0
cost = 0
max_con = position
for i in last:
if cost < (abs(position[0] - i[0]) + abs(position[1] - i[1])):
max_con = i
cost = (abs(position[0] - i[0]) + abs(position[1] - i[1]))
Heuristic = cost
diff = position[0] - max_con[0]
count = 0
for i in last:
if diff > 0:
if position[0] < i[0]:
count += 1
if diff < 0:
if position[0] > i[0]:
count += 1
if diff == 0:
if position[0] != i[0]:
count += 1
return Heuristic + count
```
### 4.Suboptimal Search
本关实现一个次优搜索在保证吃掉所有食物的条件下在较短的时间内找到一个比较好的路径。实现思路为使用优先队列和AnyFoodSearchProblem类中实现的函数完成本关实现代码如下
```python
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 ***"
Result = []
Visited = []
Queue = util.PriorityQueue()
startState = (problem.getStartState(),[],0)
Queue.push(startState,startState[2])
while not Queue.isEmpty():
(state,path,cost) = Queue.pop()
if problem.isGoalState(state):
Result = path
break
if state not in Visited:
Visited.append(state)
for currentState,currentPath,currentCost in problem.getSuccessors(state):
newPath = path + [currentPath]
newCost = cost + currentCost
newState = (currentState,newPath,newCost)
Queue.push(newState,newCost)
return Result
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):
"""
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
else:
return False
util.raiseNotDefined()
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: ' + str(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))
```
## 六.实验总结
学会了四个经典的搜索算法的用法在前期的算法课中虽然学习过DFSBFSaStar等搜索算法但是之前的考试都考背诵代码都没有真正的理解这些算法的过程。Pacman实验将算法的过程通过吃豆人的移动与路径的选择表现出来让人眼前一亮也让人更加深刻的理解这些算法。