资源下载地址:https://download.csdn.net/download/sheziqiong/85656999
资源下载地址:https://download.csdn.net/download/sheziqiong/85656999

搜索在Bait游戏中的应用研究

一、引言

如图1所示为bait游戏的基本规则,这里不再赘述。

图1 bait游戏基本规则

二、实验报告

2.1 深度优先搜索

2.1.1 原理分析

深度优先搜索顾名思义是以深度为优先,依次展开待搜索的树的每一个“叶子节点”,一旦探明某个叶子节点为终点,直接返回整条路径,之后按照这条路径即可到达终点。

2.1.2 代码实现

创建两个ArrayList,一个用来存放搜索过的状态,一个用来存放达到当前状态的动作序列。在第一次运行时,将初始状态添加到状态列表中。此后的运行过程与非初次运行一样。

阅读源代码得知,系统每次调用act函数,会传入当前状态,并且在最大时限之内期盼返回一个动作。由于我们把时限改大了,所以不会超时。因此调用act函数分为两种情况。若已经找到成功路径,则返回路径的第一个动作,然后更新路径列表把第一个动作删掉,如此循环知道动作列表为空,Avatar也就到达了终点。如果没有找到成功路径,则进入一个递归函数“search”,search将会直接找到成功路径。

下面说一下”search”,它会依次遍历当前位置可以走的所有状态,并把每一个下一步状态递归调用自己,同时每一个搜索过的节点将会存储到current_list列表中,当搜到重复节点时直接跳过。因为递归调用过程始终在每一个可走状态中向下展开,所以search的原理是深度优先的。理论上来讲,只要时间足够且游戏是可通关的,search最多搜索完整棵树,一定能找到一个结果。程序的正确性由此可以确定。

2.1.3 结果评估

经过测试,在10000毫秒内可以通前三关,在一个小时内可以通关lv4。经过观察推测lv3的地图大小远远超过其他,故树的深度和搜索难度都较大。

2.2 深度受限搜索

特别感谢“王宸旭”与“朱鑫浩”两位同学给我的提醒,与他们两位的交谈解答了我的疑惑——为什么深度受限的深度优先搜索会需要“启发式函数”,他们对于我完成任务有着无可取代的作用。

2.2.1 原理分析

起初,我对limitdepthfirst的理解,是书上写的iterative deepening depth-first search. 于是我就按照这个逻辑写了一个(并未包含在程序中),并且效果还不错,但是并不知道题目所说的“评估函数”存在的意义在哪里。在此程序中,直接把第一题的程序稍加修改,加上了一个全局变量max_depth,当搜索到max_depth时直接放弃此分支转而搜索下一个。如果全部搜完了都没有找到结果,则把max_depth+1,再次搜索。

后来通过进一步了解后我理解了本题的意思。当深度受限为k时,深度优先搜索并不能确保找到一个真正的“正确结果”,这时候就利用评估函数来辅助,找到相对的“最优结果”,并把它当做“目标”来向他行进,就可以一步一步慢慢向“正确结果”靠近。当没有找到“正确结果”的时候,事实上搜索过程展开了整个k层树,这时的搜索时间复杂度和广度优先没有区别,因为都遍历了整个树。这样做的好处是每一个动作动作周期变得短了很多,可以在更短时间内做出一步决策。这么做的隐患是,因为并没有找到“正确结果”,只是找了一个“最优结果”,所以做出的动作序列并不能确保最终一定能胜利,而是有可能在行进中产生了不可逆转的错误(橡子推到钥匙上、橡子卡墙角等),然后之后的搜索再怎么努力也不可能获胜。因此重点落在了如何合理设计启发式函数,使得上述情况可以最大程度地被避免。

2.2.2 代码实现

相比上一题,增加了参数max_depth,减少了参数found_path,methods(不用搜到完整路径),first_run(改进后的代码第一次和之后的次完全相同,不再需要专门判断是否为第一次执行)。调整了参数current_state为两个参数real_states和virual_states分别用来表征真实游戏已经走过的状态,以及搜索过程中模拟走过的状态。

Eval函数非常简单,只看获得钥匙前人与钥匙的距离,获得钥匙后人与门的距离(采用欧氏距离)。

Evaluate函数为真正的“评估函数”,它采用递归方法来获得当前节点的得分,为其各个子节点得分的最高分,如果为底层节点(depth=max_depth)则返回他的eval值。同时在evaluate函数中增加了用来特殊处理“特殊情况”的判断,比如Avatar死了则返回0分,游戏赢了则返回正无穷,进入先前已经搜索过的重复状态也返回0。

Act函数根据评估函数返回的4个方向的不同得分来选出最优单步操作,并返回。

2.2.3 结果评估

由于下一题A*还要好好设计和调试启发式函数,这一题就只写一个简单版的框架,并不追求其效果。经过测试第一关在设定最大深度为2时,每一步设定100毫秒通关没有问题。

2.3 A*算法

2.3.1 原理分析

原始的A*算法是一种十分精巧的搜索方法,它是开销优先地展开整棵树,直到找到结果,在静态问题中,只要评估函数f=g+h中的h满足:1.不高估代价 2.进行图搜索时,代价满足三角不等式。则必然可以在有限步之内找到正确解。

由于本题限制了每一步的搜索时间,故不能完全展开整个图找到通关解,而是需要在较短时间内返回一个动作。因此本题并非完全意义上的“静态”搜索问题,而是有可能在游戏进行过程中不可逆转地改变局面,从而使先前搜索的各种状态失去意义。在理解A算法的过程中我阅读了一篇名为《A算法详解,看完后全懂了》的文章[1],对我的算法实现有较大帮助。根据根据文中思路,维护两个列表,一个表示内部节点,一个表示外围节点。每次在外围节点中挑选cost最低的节点把它添加到内部,他展开的4个节点,如果已经在内部节点列表中则忽略,如果在外部节点列表中且新cost比原先更低则把原来节点的父亲只想当前节点,如果不在两表之中则把此新节点添加到外部列表。最终外部列表会触及重点,然后根据重点回溯就可以了。

2.3.2 代码实现

每一个节点的结构,包含一个Stateobservation对象表示当前的ob,一个double表示自身cost,一个节点指针指向自己的父亲,一个Types.Action对象表示从父亲走到自己所经历的操作,还有一个int depth表示在树上的深度。

总的思路为:在搜索的同时行动,始终保持Avatar在外围列表中cost最低的位置,实时调整。

首先搭建一个框架,程序总的分为act和eval两部分,act用来返回操作,eval表示f=g+h开销函数。

Act函数中包含两种模式,“探索模式”和“切换节点模式”。探索模式展开当前最优节点的“合法(定义如原理分析所述)”子节点,然后重新取出整个外围列表中的最优节点,把他作为下一步的行动目标。而切换节点模式,表示新最优节点并非处在当前节点的子节点中,而是在其他其他分支之下,此时让Avatar先前往最优节点,之后再执行后续展开操作。同时应当注意的是,由于一些动作可能“不可逆转”地改变地图,因此在程序中简单化为,只要改变地图,就认为这是一张新的地图,因此直接将内部和外部列表清空,把当前节点作为新的起点,重新开始整个A*展开过程。

Eval 函数分为路径代价与启发式函数,路径代价就是当前节点的深度乘上单格大小再乘一个常数c_g。启发式函数大体上分为4部分,由于较为复杂,且听细说。

启发式函数的第一部分是玩家、门、钥匙三者之间的距离距离开销,当没有获取钥匙时是人到钥匙的距离加上钥匙到门的距离,拿到钥匙之后就是人到门的距离。这里的距离使用曼哈顿距离,为了和g(x)中使用深度乘格子大小的算法匹配。此项开销最终再乘一个常数c_Manhattan.

启发式函数的第二部分是游戏分数的开销。推箱子进坑+1,吃蘑菇+1,胜利+5,这3样都是我们希望看到的,这一项cost=游戏分数*c_score,并把c_score设为负数。

经过以上两个启发式函数的探索,经常会出现把木块推到死角等导致游戏失败的情形,于是开始考虑和木块位置相关的cost,因而有了启发式函数的第三项—木块评分。木块的评分又分为这么几个部分,木块自由度评分,木块贴洞评分,还有木块贴洞且可以直接被推进洞的评分。

由于观察5关发现,不存在需要用木块覆盖钥匙的情况,所以直接设置木块覆盖钥匙的代价为无穷大(和走到坑里一样)。(经过这样设置,不需要任何其他判断,就可以过第一关了。)

上述木块自由度的判断,只判断Avatar九宫格之内的木块,因为Avatar移动一步所能改变的木块一定在他四周,其他木块的评分不会有任何变化,所以可以不用计算。自由度的判断使用这样一种方法,获取一个木块周围一圈的物体列表,从Avatar开始顺时针、逆时针遍历,分别获取Avatar可以走到的位置。如果Avatar可以走到某个位置,且对位为空,则这个方向“自由”,由此可见自由度可以为01234中的一个值。而木块是否可以一步如坑,则是判断Avatar可到达位置的对位是否是坑,如果Avatar可以到达一个对位为洞的位置,再往前一步就可以把它推进洞里,这是十分显然的。以上木块的各种分数,分别有其相应的常数系数相对应。

最后的最后,受到蒙特卡洛树的启发,假如了第四个启发式函数random,给他也设定一个常数系数。

这样一来,A*算法的好坏,就完全取决于上述各项系数的设定了,经过反复调参,获得了以下结果。

2.3.3 结果评估

可以通前3关,第4关看起来毫无希望,第5关感觉离成功不远(最后一个木块不正确)。

因为游戏游戏使用了Tick=1000限制,所以我的“始终保持Avatar在最优节点”实际上浪费了大量步数在切换Avatar的位置上,而不是沿着正确的路径行进,效率较低。下一步可以改进的地方,第一就是充分利用给与的搜索时间,在搜到极限时间时才返回动作,这样可以有效减少废动作。第二点是考虑更好的启发式函数,目前能想到的是考虑图中的连通度,比如可以把图的连同区域数量进行打分,连同区域越少越好。

2.4 MCTS

样例程序给出了一个非常朴素的蒙特卡洛树搜索,它仅仅只能通第一关。

在获取每一步动作时,把大部分时间提供给建立蒙特卡洛树,让他迭代的选择当前最优节点,获取roll out值(到设定的最大深度或游戏结束时,判断当前价值),然后反向传播更新父辈系列节点的评分,如此重复直到时间快要耗尽。然后返回一个最频繁到达的节点对应的动作,作为当前act函数的动作,为什么返回最频繁节点而不是胜率最高节点呢?通过查阅资料[2]我了解到,访问量不够大的节点,即使胜率较高,由于模拟次数太少所以他并不可靠。反而是访问量大的节点,因为在“选择”阶段时选的最优节点,所以他能被多次走到说明得分还是比较不错的。

对于节点是好还是坏的评价非常简单,除了判断输赢,只看游戏得分StateObservation.getGameScore()的值。也正是因为他过于简单而且离散,因而在较复杂问题中几乎没用。这就是我对于源代码所示蒙特卡洛树搜索的粗浅理解。

资源下载地址:https://download.csdn.net/download/sheziqiong/85656999
资源下载地址:https://download.csdn.net/download/sheziqiong/85656999

Java实现搜索在Bait游戏中的应用研究相关推荐

  1. Java实现监督学习在Aliens游戏中的探究尝试

    资源下载地址:https://download.csdn.net/download/sheziqiong/85656972 资源下载地址:https://download.csdn.net/downl ...

  2. 编写Java程序,创建Dota游戏中的兵营类,兵营类有一个类成员变量count、一个实例变量name和另一个实例变量selfCount。

    返回本章节 返回作业目录 需求说明: 创建Dota游戏中的兵营类 兵营类有一个类成员变量count.一个实例变量name和另一个实例变量selfCount. count表示的是兵营已经创建士兵的总数: ...

  3. 编写Java程序,创建Dota游戏中的防御塔类,通过两个坐属性显示防御塔所在的位置

    返回本章节 返回作业目录 需求说明: 创建Dota游戏中的防御塔类 通过两个坐属性显示防御塔所在的位置 实现思路: 创建防御塔(TowerDefense)类 在该类中定义了两个属性,分别是int类型横 ...

  4. java 清空jframe_java – 在新游戏中清除我的JFrame和JPanel

    我有一个简单的JPAnel用于tic-tac-toe,绘制线条-- 因此TTT类扩展了JPanel,并在其中包含一个GameLogic对象. 一切都很好,应用程序是主要添加TTT的JFrame,一切都 ...

  5. 如何使用java编程算概率_游戏中概率的编程实现(转之别人)最简单的

    概率 听起来很玄 比如洗法宝 太一石 有一段说明 有一定概率 能改变法宝的技能阶数 那么 概率在编程中如何实现的呢 个人在大学里有学c++ java 以及vb 现以vb实现概率来说明 首先,要先说清楚 ...

  6. 《HTML5 2D游戏编程核心技术》——第1章,第1.1节Snail Bait游戏

    本节书摘来自华章出版社<HTML5 2D游戏编程核心技术>一书中的第1章,第1.1节,作者[美] 戴维·吉尔里,更多章节内容可以访问云栖社区"华章计算机"公众号查看. ...

  7. 一行Java代码实现游戏中交换装备

    摘要:JDK 1.5 开始 JUC 包下提供的 Exchanger 类可用于两个线程之间交换信息. 本文分享自华为云社区<一行Java代码实现两玩家交换装备[并发编程]>,作者:陈皮的Ja ...

  8. Unity Editor自制工具(1)--“Editor目录栏按钮+全局搜索方法+自制Editor窗口”实现搜索与删除场景中任意名称游戏物体

    1,制作Editor上方目录按钮 在C#静态方法上方加上[MenuItem("目录")],可在Editor中上方目录栏生成一个按钮. using System.Collection ...

  9. mc是用java写的吗_都说MC的代码特别差劲,你觉得它在所有游戏中,能排第几?...

    原标题:都说MC的代码特别差劲,你觉得它在所有游戏中,能排第几? 虽然说我的世界这款游戏非常的好玩,销量世界第一,无论国内国外都非常的火热,但是大家都知道,一款游戏他的最原本的面貌是由一个又一个的代码 ...

最新文章

  1. 把大脑「复制-粘贴」到芯片上,三星、哈佛的大胆设想登上Nature子刊
  2. 14、Kubernetes持久化存储
  3. mysql表恢复报错binlog_mysql数据恢复,利用binlog2sql快速闪回
  4. linux内核中的GPIO系统之(2):pin control subsystem
  5. 浅谈Python和VC中的编码问题(转)
  6. java线程代码实现_Java 实现线程的2种方法的具体代码实例
  7. 【转载】别了,摩托罗拉(十六):平台之乱
  8. java datarow 使用_DataRow中的链接(数据表)
  9. android 继承listview,Android listView 继承ListActivity的用法
  10. [ES6] 细化ES6之 -- 数组的扩展
  11. 对刚接触oracle的人比较有用的一些工具 zt
  12. Chrome 插件开发与本地程序交互流程
  13. LInux下如何挂载光盘找rpm包?
  14. 人力资源数据分析师前景_HR熬出头了!人力资源数据分析师年薪18万-90万
  15. Win7激活工具的原理是什么?
  16. 给定经纬度计算距离_根据两点经纬度计算距离
  17. 软件工程基础知识--系统设计
  18. 输入12V-48V输出5V-12V电流:2A
  19. 苹果手机投影_手机和投影同时用流量能否投屏
  20. vue-element-table列内容显示过多隐藏

热门文章

  1. 游戏开发经验之开源游戏盈利的十个有效途径
  2. 《会计信息系统》课程期末复习题与参考答案
  3. EditPlus中文版+英文版+注册码下载
  4. ue4变形、FlipFlop
  5. uniapp 搜索关键字高亮显示实现
  6. 29.7 MySQL灾难恢复
  7. DZ先生怪谈国标28181之大型监控组网ntpd校时方案
  8. 2022-2028年中国喷砂设备行业发展动态及投资前景分析报告
  9. Windows电脑关闭触摸键盘的方法
  10. 使用Labelimg打标签