CS188-Project 2

  • Minimax
    • 算法简述
    • 游戏过程
    • 代码实现
    • 结果展示
  • Alpha-Beta Pruning
    • 算法简述
    • 游戏过程
    • 代码实现
    • 结果展示
  • Expectimax
    • 算法简述
    • 游戏过程
    • 代码实现
    • 结果展示
  • Evaluation Function
    • 算法简述
    • 游戏过程
    • 代码实现
    • 结果展示
    • Total result
  • 效果对比&感悟
  • 收获

Minimax

算法简述

Minimax算法又名极小化极大算法。该算法基于一种假设,即对手也会进行最优操作并总是会采取最损人利己的策略,最后找出失败的最大可能性中的最小值的结果。

Minimax算法常用于棋类等由两方较量的游戏和程序,这类程序由两个游戏者轮流,每次执行一个步骤。我们众所周知的五子棋、象棋等都属于这类程序,所以说Minimax算法是基于搜索的博弈算法的基础。

该算法是一种零和算法,即一方要在可选的选项中选择将其优势最大化的选择,而另一方则选择令对手优势最小化的方法。

游戏过程

而在吃豆人游戏中,假设吃豆人初始分数为10,在吃到豆子之前每一步会扣一分,吃到豆子时游戏会达到终端状态并结束。

一个状态的值定义为一个agent从该状态出发能得到的最佳输出(效益)。可以简单地认为一个agent的效益就是其得到的分数。一个终端状态的值,称为终端效益。

在游戏中,有一个敌方幽灵想阻止吃豆人吃到豆子,这个幽灵会改变吃豆人原本认为最优的行动,而新的最优行动由minimax算法确定。Minimax算法只通过最大化吃豆人控制节点的子节点并同时将幽灵控制节点的子节点最小化,而不是将树的每一层的子节点的效益最大化。有的时候吃豆人想要到达终端效益,但是通过minimax吃豆人“知道”一个会采取最优行动的幽灵不会让他成功。为了采取最优行动,吃豆人必须止损,并且反直觉地远离豆子来让他的损失最小化。

代码实现

class MinimaxAgent(MultiAgentSearchAgent):"""Your minimax agent (question 2)"""def getAction(self, gameState):"""Returns the minimax action from the current gameState using self.depthand self.evaluationFunction.Here are some method calls that might be useful when implementing minimax.gameState.getLegalActions(agentIndex):Returns a list of legal actions for an agentagentIndex=0 means Pacman, ghosts are >= 1gameState.generateSuccessor(agentIndex, action):Returns the successor game state after an agent takes an actiongameState.getNumAgents():Returns the total number of agents in the gamegameState.isWin():Returns whether or not the game state is a winning stategameState.isLose():Returns whether or not the game state is a losing state""""*** YOUR CODE HERE ***"#将最大值初始定义为负无穷大maxVal = -float('inf')bestAction = Nonefor action in gameState.getLegalActions(0):# 求出接下来的所有MIN值,并和maxVal比较,求出MAX值value = self._getMin(gameState.generateSuccessor(0, action))# 满足条件则更新maxVal值,并记下bestActionif value is not None and value > maxVal:maxVal = valuebestAction = actionreturn bestActiondef _getMax(self, gameState, depth = 0, agentIndex = 0):# 获得下一步legalActions = gameState.getLegalActions(agentIndex)# 如果遍历到根节点或无可继续节点,则返回评价值if depth == self.depth or len(legalActions)==0:return self.evaluationFunction(gameState)maxVal = -float('inf')# 对吃豆人下一步可行的操作进行遍历for action in legalActions:# 从第一个鬼怪开始MIN遍历value = self._getMin(gameState.generateSuccessor(agentIndex, action), depth, 1)if value is not None and value > maxVal:maxVal = valuereturn maxValdef _getMin(self, gameState, depth = 0, agentIndex = 1):# 获得鬼怪的下一步操作legalActions = gameState.getLegalActions(agentIndex)# 同样,如果遍历到根节点或无可继续节点,则返回评价值if depth == self.depth or len(legalActions)==0:return self.evaluationFunction(gameState)minVal = float('inf')# 对当前鬼怪的可行下一步进行遍历,其中要递归调用以计算其他鬼怪的行动for action in legalActions:# 如果当前已经是最后一只鬼怪,那么下一轮就该是计算吃豆人的行为了,即调用MAX函数if agentIndex == gameState.getNumAgents() - 1:value = self._getMax(gameState.generateSuccessor(agentIndex, action), depth+1, 0)else:value = self._getMin(gameState.generateSuccessor(agentIndex, action), depth, agentIndex+1)if value is not None and value < minVal:minVal = valuereturn minVal

结果展示

Alpha-Beta Pruning

算法简述

alpha-beta剪枝算法是基于极大极小搜索算法的。极大极小搜索策略是考虑双方对弈若干步之后,从可能的步中选一步相对好的走法来走,在有限的搜索范围内进行求解,可以理解为规定一个有限的搜索深度。

定义极大层的下界为alpha,极小层的上界为beta,alpha-beta剪枝规则描述如下:

(1)alpha剪枝。若任一极小值层结点的beta值不大于它任一前驱极大值层结点的alpha值,即alpha(前驱层) >= beta(后继层),则可终止该极小值层中这个MIN结点以下的搜索过程。这个MIN结点最终的倒推值就确定为这个beta值。

(2)beta剪枝。若任一极大值层结点的alpha值不小于它任一前驱极小值层结点的beta值,即alpha(后继层) >= beta(前驱层),则可以终止该极大值层中这个MAX结点以下的搜索过程,这个MAX结点最终倒推值就确定为这个alpha值。

游戏过程

前文提到的Minimax已经能够较好的找到最佳的解决方案。但它的执行与DFS相似,时间复杂度较大,达到了O(b^m)。为了改进这一问题,需对minimax进行优化采用α-β剪枝的方式,为吃豆人寻找最优解决策略,降低时间复杂度。

代码实现

class AlphaBetaAgent(MultiAgentSearchAgent):"""Your minimax agent with alpha-beta pruning (question 3)"""def getAction(self, gameState):"""Returns the minimax action using self.depth and self.evaluationFunction""""*** YOUR CODE HERE ***"# 从根节点开始展开,求MAX值return self._getMax(gameState)[1]def _getMax(self, gameState, depth = 0, agentIndex = 0, alpha = -float('inf'),beta = float('inf')):# 如果到达叶子节点,或者无法继续展开,则返回当前状态的评价值legalActions = gameState.getLegalActions(agentIndex)if depth == self.depth or len(legalActions)==0:return self.evaluationFunction(gameState), None# 否则,就继续往下遍历吃豆人可能的下一步maxVal = NonebestAction = Nonefor action in legalActions:# 考虑只有一个吃豆人的情况,直接求其MIN分支的评价值,agentIndex从1开始遍历所有鬼怪value = self._getMin(gameState.generateSuccessor(agentIndex, action), depth, 1, alpha, beta)[0]if value is not None and (maxVal == None or value > maxVal):maxVal = valuebestAction = action# 按照α-β剪枝算法,如果v>β,则直接返回vif value is not None and value > beta:return value, action# 按照α-β剪枝算法,这里还需要更新α的值if value is not None and value > alpha:alpha = valuereturn maxVal, bestActiondef _getMin(self, gameState, depth = 0, agentIndex = 0, alpha = -float('inf'),beta = float('inf')):# 如果到达叶子节点,或者无法继续展开,则返回当前状态的评价值legalActions = gameState.getLegalActions(agentIndex)if depth == self.depth or len(legalActions)==0:return self.evaluationFunction(gameState), None# 否则,就继续往下遍历当前鬼怪可能的下一步minVal = NonebestAction = Nonefor action in legalActions:# 如果当前是最后一个鬼怪,那么下一轮就该调用MAX函数if agentIndex >= gameState.getNumAgents() - 1:value = self._getMax(gameState.generateSuccessor(agentIndex, action), depth+1, 0, alpha, beta)[0]else:# 如果不是最后一个鬼怪,则继续遍历下一个鬼怪,即agentIndex+1value = self._getMin(gameState.generateSuccessor(agentIndex, action), depth, agentIndex+1, alpha, beta)[0]if value is not None and (minVal == None or value < minVal):minVal = valuebestAction = action# 按照α-β剪枝算法,如果v<α,则直接返回vif value is not None and value < alpha:return value, action# 按照α-β剪枝算法,这里还需要更新β的值if value is not None and value < beta:beta = valuereturn minVal, bestAction

结果展示

Expectimax

算法简述

然而,在学习minimax后,不难发现由于minimax假设它面对的是一个最优的对手,在对手不一定会做出最优行动的情况下,minimax就不再适用。

故考虑minimax的一种随机化的方式,从而将算法更新为
expectimax。Expectimax在游戏树中加入了机会节点,与考虑最坏情况的最小化节点不同,机会节点会考虑平均情况。更准确的说,最小化节点仅仅计算子节点的最小效益,而机会节点计算期望效益或期望值。

游戏过程

相比之前的Minimax的Agent,ExpectimaxAgent会以一定的概率去拼一拼,采用Expectimax的Agent,吃豆人会以一定的概率选择往有幽灵的方向走,因为假设往下走可能吃完所有的豆豆而取得更好的分数,所以游戏的失败率就降低了。

代码实现

class ExpectimaxAgent(MultiAgentSearchAgent):"""Your expectimax agent (question 4)"""def getAction(self, gameState):"""Returns the expectimax action using self.depth and self.evaluationFunctionAll ghosts should be modeled as choosing uniformly at random from theirlegal moves.""""*** YOUR CODE HERE ***"return self._getMax(gameState)def _getMax(self, gameState, depth = 0, agentIndex = 0):# 获得吃豆人所有下一步行动legalActions = gameState.getLegalActions(agentIndex)# 如果到达根节点或者没有可行的行动,则返回评价函数值if depth == self.depth or len(legalActions)==0:return self.evaluationFunction(gameState)# 否则初始化,并对合法的下一步进行遍历maxVal = NonebestAction = Nonefor action in legalActions:# 从第一个鬼怪开始,进行Expectimax操作value = self._getExpectation(gameState.generateSuccessor(agentIndex, action), depth, 1)if value is not None and (maxVal == None or value > maxVal):maxVal = valuebestAction = action if depth is 0 and agentIndex is 0:return bestActionelse:return maxValdef _getExpectation(self, gameState, depth = 0, agentIndex = 0):legalActions = gameState.getLegalActions(agentIndex)# 如果到达根节点,或者没有下一步了,则返回评价函数值if depth == self.depth or len(legalActions)==0:return self.evaluationFunction(gameState) # 初始化效用值总计totalUtil = 0numActions = len(legalActions)# 轮询当前鬼怪所有可行的下一步for action in legalActions:# 同样,如果是最后一个鬼怪,那么接下来要去算吃豆人的MAX值if agentIndex >= gameState.getNumAgents() - 1:totalUtil += self._getMax(gameState.generateSuccessor(agentIndex, action), depth+1, 0)# 否则,挨个遍历各个鬼怪,计算Expectation值,并计入效用总计else:totalUtil += self._getExpectation(gameState.generateSuccessor(agentIndex, action), depth, agentIndex+1)# 最后需要把所有可能的下一步的效用值求平均,并返回return totalUtil / float(numActions)

结果展示

Evaluation Function

算法简述

估计函数在深度限制minimax中广泛应用,即将最大可解深度处的非叶子结点都视作叶子节点,然后用仔细挑选的估计函数给其赋虚值。由于估计函数只能用于估计非叶子结点的效益,这使得minimax的最优性不再得到保证。

游戏过程

虽然α-β剪枝能有效增加minimax的搜索深度,但是对大多数游戏来说这离到达搜索树的底部仍然还差得远。故使用估计函数(evaluation functions),这种函数输入状态并输出该节点的minimax估计值。简单直接的解释为:一个好的估计函数能给更好的状态赋更高的值。

代码实现

def betterEvaluationFunction(currentGameState):"""Your extreme ghost-hunting, pellet-nabbing, food-gobbling, unstoppableevaluation function (question 5).DESCRIPTION: <write something here so we know what you did>""""*** YOUR CODE HERE ***"# 获得计算需要的初始信息,包括吃豆人位置、食物、鬼怪以及鬼怪为惊吓状态的剩余时间pacmanPos = currentGameState.getPacmanPosition()foods = currentGameState.getFood().asList()ghostStates = currentGameState.getGhostStates()scaredTime = [ghost.scaredTimer for ghost in ghostStates]# 先计算最近的食物对吃豆人的影响if len(foods)>0:Foods = [manhattanDistance(food, pacmanPos) for food in foods]# 求最近的豆豆的距离nearestFood = min(Foods)# 考虑估计函数,要将较好的结果最大化foodHeuristic = 0else:foodHeuristic = 0# 通过鬼怪和当前pacman的位置计算危险值if len(ghostStates)>0:# 将所有鬼怪离当前的位置全部计算出来Ghosts = [manhattanDistance(ghost.configuration.pos, pacmanPos) for ghost in ghostStates]# 求最近的鬼怪的距离,其他的鬼怪可以不计nearestGhost = min(Ghosts)dangousScore = -1000 if nearestGhost<2 else 0# 尽量让鬼怪保持惊吓状态,因为这种状态下的鬼怪可以被吃豆人吃掉totalScaredTimes = sum(scaredTime)# 最后把下一个状态的得分也计入评价值结果return  currentGameState.getScore() + foodHeuristic + dangousScore + totalScaredTimes

结果展示

不难看出q5由于采用了Evaluation Function函数,导致最终只能获得估计的非叶子结点的效益,这使得minimax的最优性不再得到保证,导致部分实验Score产生负值。


Total result

效果对比&感悟

一、算法效率
不难发现,从算法效率来看,Minimax函数由于本质上是进行全搜索的过程,min和max过程将所有的可能性进行搜索,然后再从端点的估计值倒推计算,这样的效率非常低下,时空复杂度过高。

而在采用α-β剪枝算法后,部分不可能被选择的节点不会再被遍历,使算法的时空复杂度得到了有效的降低。虽然α-β剪枝算法还是一个深度遍历搜索算法,但它对一些非必要的估计值进行舍弃,效率还是得到了很大的提升。

二、算法准确度
我们可以看到,通过Minimax算法得到的平均分数是84分,而通过α-β剪枝算法得到的平均分数也是84分。从而也验证了α-β剪枝算法只是对原本的Minimax算法进行了时空复杂度上的优化和改进,对最终的结果并没有造成影响。

同时,我们可以通过Minimax算法的假设中发现该算法的一些不足,即Minimax算法认为对手一定会做出最优解,但当对手不做出最优行动的情况下,Minimax算法便显得不适用。此时,expectimax算法便显示其独特性,与考虑最坏情况的最小化节点的Minimax算法不同,expectimax算法会考虑平均情况,从而模拟一个更贴近实际情况的过程。

收获

一、算法的更换以及替代并不是随便得出的,是循序渐进的。我们通过发现Minimax算法本质是深度遍历搜索算法,故能够得到Minimax算法的时空复杂度高的结论。据此,采用剪枝的方法优化算法速度。同时,我们通过发现Minimax算法将对手理想化,这样的假设是存在缺陷的,故我们采用expectimax算法来解决该问题。

二、在学习过程中,搜索算法算得上是老朋友了。搜索算法实际上是根据初始条件和扩展规则构造一棵“解答树”并寻找符合目标状态的节点的过程。无论是数据结构课程学到的深度搜索算法,广度搜索算法,哈希函数,在到现在拓展学习的A*,Minimax算法,Alpha-Beta Pruning,Expectimax等算法。不同算法的流程和时间复杂度都各不相同,适用条件也有所不同,需要根据不同的情况选择合适的搜索算法。

三、理清了Minimax算法,Alpha-Beta Pruning,Expectimax,Evaluation Function四类的区别和适用情况。例如当解空间较大时,则Alpha-Beta Pruning较Minimax算法更合适;expectimax较Minimax算法考虑得更全面。

CS188-Project 2相关推荐

  1. CS188 Project 4: Inference in Bayes Nets(4-6)

    CS188 Project 4: Inference in Bayes Nets Question 4 (4 points): Eliminate 原理 方法 代码 结果 Question 5 (4 ...

  2. 人工智能-CS188 Project 2: Multi-agents

    一.项目介绍 项目介绍网页 项目代码下载 本项目是采用Berkeley的CS188课程内容实习二的内容,在这个项目中,我们将为经典版本的Pacman 设计自动算法,包括幽灵.在此过程中,我们将实现 m ...

  3. 机器学习 贝叶斯方法_机器学习中的常客与贝叶斯方法

    机器学习 贝叶斯方法 There has always been a debate between Bayesian and frequentist statistical inference. Fr ...

  4. ideal如何创建dynamic web project

    步骤如下 ① file -> new -> project ② 选择 Java Enterprise -> next ③ create project from template - ...

  5. This version of Android Studio cannot open this project, please retry with Android Studio 3.5 or new

    今天github 下载一个库 导入 as 提示 This version of Android Studio cannot open this project, please retry with A ...

  6. Error:The SDK Build Tools revision (23.0.3) is too low for project ':app'. Minimum required is 25.0.

    导入github上项目的时候出现 Error:The SDK Build Tools revision (23.0.3) is too low for project ':app'. Minimum ...

  7. Error:(49, 1) A problem occurred evaluating project ':guideview'. Could not read script 'https://r

    出现问题如下: Error:(49, 1) A problem occurred evaluating project ':guideview'. > Could not read script ...

  8. IntelliJ IDEA 的Project structure说明

    IntelliJ IDEA 的Project structure可以在File->Project structure中打开,同时,在新建项目是IDE一般用向导的方式让你填写Project str ...

  9. 将Project的内容导出成单独的XPO文件

    AX跟VSS整合的版本管理可以通过创建知识库将当前层的代码全部签入到VSS中,但是如果不是一个团队开发solution,而是针对客户的需求随时做得一些小改动,一般都希望以Project的形式组织代码和 ...

  10. linux vim project,vim插件project的用法

    用任何编辑器写代码,文件管理的方便与否对编码效率影响很大.一般的IDE都有文件管理功能,并且用来的不错.在vim中,要实现较好的文件管理功能一般都靠插件.在有米实习的第一个月,自己一直用NERDTre ...

最新文章

  1. React Axios 请求解决跨域问题
  2. 如何获取微信openId
  3. Xamarin XAML语言教程使用Xamarin Studio创建XAML(二)
  4. python-dotenv的使用
  5. python高性能服务器编写,Tornado的高性能服务器开发常用方法
  6. Python实训day01pm【练习题、文件编写、列表的使用】
  7. 如何让页面动起来?支付宝2020新春红包前端3D技术揭秘
  8. eclipse 不能切换输入法
  9. android EditText 控件中加入图片(非背景图片)
  10. 如何自动维护全文索引和目录
  11. 基于GCN的推荐该怎么搞?
  12. Mac专业三维建模软件Modo 16
  13. 【转】推荐:全面了解数据库设计中分类算法
  14. iso镜像添加软件包_iso镜像文件怎么安装 安装镜像文件的方法【图文】
  15. getbook netty实战_Netty 实战(精髓)简介(Netty in Action)
  16. 「技术架构」TOGAF建模:环境和位置图
  17. 免费地图资源(持续更新)
  18. HTML对字体的所有操作详解(经典)
  19. mysql1682错误_ERROR 1682 (HY000)
  20. 淘宝修改密码可能引发手机骚扰

热门文章

  1. 10 04Hibernate之HibernateSessionFactory
  2. favourite和favorite一样吗
  3. BUUCTF-MD5强弱比较-MD5()的万能密码-tornado框架注入-中文电码
  4. 互联网产品设计的12个理念(游戏)
  5. 深圳市临床医学研究中心管理办法 (修订征求意见稿)
  6. JS、ActiveXObject、Scripting.FileSystemObject
  7. 推荐一个压缩 png、jpg、pdf 文件的网站
  8. Android 广播+服务+AIDL一起实现手机号码拦截
  9. 中国广电已拥有最多的4G、5G基站,中国联通和中国电信瑟瑟发抖
  10. autoform分析用什么计算机,AutoForm入门操作流程