灯会游戏自动点击脚本编写历程总结

(本次总结仅为学习总结分享)

前因:

某游戏开展了一个灯会游戏的活动,一开始也没在意,没怎么参加,然后突然有一天有点感兴趣,自己点了点,太难了。。。,过不了第5关。。。

灯会游戏介绍:

这个灯会游戏就是“开灯游戏”等,可以百度这个词,可以发现是一个很古老的经典的游戏,
举例:在一个5X5的方格,每个方格存在一个灯,灯的状态只有两种,点亮或未点亮,在游戏初会随机有部分灯处于点亮状态,假如对一个灯进行点击,则灯本身,及灯四周(上、下、左、右)的灯的状态都会发生变化,变化规则:亮的灯变成不亮,而不亮的灯会变亮。过关条件则是所有灯处于点亮状态。同时该游戏有计分机制,每点亮一展灯就积一分,并且有时间限制,大概在80S左右,因为前几关过关后会加时。

历程一

看了下排行榜,前十,大部分处于300多积分,前三名可能有400分,最高看到一次500分(特别佩服,真的大神,本人自己点只能点100多)。一想肯定有技巧,于是百度找攻略,果然最常见最简单的一种技巧,描述如下(以5X5方格的情况讲述):首先从第一行开始,如果该行有未点亮的灯,则点击该灯正下方一格,这样就能点亮第一行灯的同时,又不会影响第二行其他的灯,如此重复点亮第一行所有的灯,然后是第二行,第三行,直到点亮前4行,这样只剩下最后一行有可能存在未点亮的灯(运气好的话可能刚好点亮第4行的同时,也点亮第5行),然后根据最后一行点亮灯的分布情况,寻找解法,最常见的7种情况是能够在网上找到方案解决的。所以按上面的方子可以解决大部分5x5方格的点灯。也是这里开始想,完全可以写一个脚本,来辨别最后一行灯的情况,然后根据情况选择相应的方案进行自动点击,这样就不用人工记了,想着想着可行,就开始做了。
思路如下:因为要判断灯的状态,截图肯定少不了,然后直接取5X5也就是总共25个方格,每个方格选取一个坐标点(可以通过画图确认坐标位置),因为灯点亮与暗的颜色差异较大,可以直接通过分析坐标点所在的像素值进行灯状态的判断。
截图方案有4种:
1、直接全屏截图
2、刚好截整个灯会游戏的图(这样性能稍微上相对于第一种肯定要更好点)
3、分析哪一行灯就截取哪一行的图
4、因为只需要确认坐标点所在的像素值,所以也可以截宽度为1像素的图
最终选择了第2种方案,因为处理的比较快,不用太优化,第2种也比较方便简单些。
于是又想,为何不把先前逐行点灯的操作,也顺便写了,大部分代码在先前就写好了,于是就开始写了,这里需要注意的是因为在为了点击第一行的灯的时候,我们在对第二行的灯进行操作,于是会影响第二行的灯状态发生变化,所以当我们要将第二行灯点亮的时候,就需要重新截图,重新判断第二行所需要点亮的灯。

验证脚本效果的时候到了

在一段时间的排查后,确认逻辑上没有问题,于是准备开始实验测试了,于是打开游戏开始运行脚本,结果发现每次点击都没有点到预想的位置上,是哪里出问题了?似乎好像以前也有过这种问题,于是把图调出来放画板里再次确认坐标的问题。发现坐标没有问题。也排查了脚本,没有问题,于是开始百度大法。一查,原来坐标的确是跟分辨率有关,但是当我们打开程序的时候,程序可能是缩放状态打开的,什么意思呢,以WIN10为列,右键桌面->显示设置

手机实现自动点击脚本java_文件名


只有100%的时候,应用的分辨率才是我们设置的那个分辨率。

于是我把缩放设置成了100%后,再开始运行脚本,nice,符合预期,但也发现问题,点击太慢了(因为有限点击限速,大概点击间隔在0.36s比较稳定),这样也导致虽然脚本能够正常点击通关,但是点击太慢,导致最终得分不高,大概是200到250左右。这样的脚本太弱了,于是就优化吧,时间上的优化,因为要等待点击间隔,以及画面刷新间隔时间,所以对这部分时间再进行了测试,尽量减小。但最终效果不明显。也试过手动逐行点击至最后一行,再开启脚本进行自动点击,但还是差不多,毕竟自己菜,跟自动点的没啥区别,还可能更慢。。。

历程二

思考整个脚本过慢的原因就在于,点击步数过多,而点击间隔也需要0.36S,如果我们能减小步数的话,换言之用更少的步数进行求解,那么能大大减少点击时间,又想如果根据一开始根据游戏中亮灯的分布情况,就能够得知解题方案,那么不但步数少了,而且只需要等待一次游戏画面的刷新(之前需要5次),这样一算下来,过一关的时间上能节省4S左右。
但是最大的问题就是如何根据游戏中亮灯的分布情况,从而判断答案
经过思考,有两种方案:
1、设计出求解算法
2、进行方案收集,即对每一种亮灯分布的情况进行收集,并找出答案。(划重点:1、因为对于一个灯,点击该灯时,只会影响该灯的本身及四周,所以如果一个灯点击次数为偶数次的话,就相当该灯没有点击过。2、同时解题方案与每个灯的点击顺序无关,只与需要点击的灯的位置有关)答案的获取可以通过历程一中所使用的那种方法,因为偶数次点击灯相当于没点,所以可以在答案中出现偶数次的方格坐标直接删除。这样一来就减少了点击步数。

对于求解算法,感觉一下子不是很有思路,想了一阵子,因为这是一个比较古老的游戏,就想着看看网上有没有现成的算法,再次百度,其中看到一个讲解的比较好的
参考:https://wenku.baidu.com/view/4072ba3643323968011c928e.html

于是发现第一算法有点复杂,一下子不好看懂,同时活动时间有限,肯定等不了太久,最关键的是时间复杂度过高,所以决定第2种方案。

方案收集:怎么进行收集,一开始想到的就是,直接在游戏里收集,每成功过关的,都保留亮灯的初始分布情况及对应的求解方案,这样一来就可以通过比如亮灯的初始分布情况进行比对从而找到直接求解方案。
然后由于解题速度比较慢,导致一分钟只能收集5种方案左右。在收集了100种方案左右后,天真的我以为,应该差不多了,先写了个脚本看看100种方案中有多少种是重复的(因为收集时,没有去判断是否已存在该方案,只是进行收集),结果是大概10多个是重复的。感觉还行。。。于是接着收集,200多种后再进行判断,发现还是10多个重复,,,开始怀疑是不是脚本有问题,于是把根据灯的初始分布情况直接选择求解方案的脚本进行测试,发现疯狂点刷新,就没碰到重复的。。。于是开始思考。。。

历程三

方法没想出来,睡觉都在想,突然想到5X5的开灯游戏,设计简单,自己完全可以模拟游戏规则,然后从游戏中进行亮灯初始分布情况的收集,不进行求解,这样1分钟就能收集60次,对于每种情况的求解可能利用自己本地模拟的通关规则进行测试,又不需要什么等待时间,所以可以很快求解出答案,这样一想,很有戏,就开始写起来脚本,经过测试,完全OK,开始行动。

在收集了1000份方案后,开始查看重复的情况,一看吓一跳,就20多种是重复的,我就???了,难道这个亮灯的分布情况是经过一定的算法,直接随机生成的,一想这样的可能性还比较大,只需要在满灯的情况下,随机选取一部分灯进行一次点击,就能够随机生成亮灯的分布情况,这样算法实现起来,不是一般的简单。。。
但可能心里还想再试试,毕竟暂时也没想到啥法子,于是接着试吧,收集了2000份后,再次查重,发现只有60种左右是重复的。。。到这里,这种收集的方法基本可以宣告放弃了。。。游戏亮灯的初始分布情况应该是随机生成的

历程四

重大的转折来了,因为在收集的过程中,我发现了一个问题,就是初始亮灯的数量基本在一个范围内,为了验证这个猜想,于是将先前收集的2000多份方案,进行了一个初始亮灯数量的统计,发现亮灯的数量只有分布在12(占25%左右),11(10%左右),10、9和8都在%5到%10之间,具体数值未保留,还有一些其他的亮灯数量,但都比较少。一看似乎有戏,主要是这几个亮灯分布情况,如果针对一种亮灯数量直接推演出所有可能的方案,再进行求解,保存下方案与答案来,建立这样一个类似数据库的感觉。。。这样基本是这种亮灯数量的初始分布情况,就基本在我们这个数据库里了(因为部分我们解不出的情况要排除掉)。于是开始编写算法就是组合算法,比较简单,一算。。。还蛮大的每一种亮灯数量的所有方案都为百W级别。。。,
于是先以亮灯数量12的情况开始进行测试可行性。因为比对的时候我将灯的初始分布情况,存为了字典的key(脚本是用python编写的),所以查询上,非常快,虽然是百W的量,但几乎没有耽搁时间。
怎么谈都是虚的 ,上游戏测吧!!!
OMG,太叼了!!!
于是最终生成了12、11、10、9这4种数量的所有方案(不要8的原因是生成时出问题了,后面也发现加载前4种所要消耗的内存也极大,大概在20G内存左右),然后进行测试,优化后,基本上能跑到450分以上,不是第一就是第二。。。为什么还不能保证第一

这就要看,到底是我写的脚本太菜,还是大神太厉害(我更相信后者)。。。

由于启动的时候需要先加载所有的方案到内存中,对内存的消耗极大,同时加载时间也有几分钟,于是又想到如果我把每一种亮灯的初始分布情况进行hash值计算作为文件名,求解方案作为文件内容,这样可以直接通过文件名找到求解方案是不是一种更好的方案,因为这里是直接去通过文件名打开一个文件(所以这里我也不知道到底存不存在一个文件搜索的时间,个人感觉至少不是检索一个文件的时间,知识没到位,暂不清楚。。。),经过尝试后,发现要生成这么大数量级的文件,太慢了。。。电脑也容易卡机,于是就没再进行尝试了。

以及尝试过其他方案,比如统计所有方案的解题频数,发现高于15步以上的解题步数的情况比较少,可以进行排除,即去除对解题步数高于15步以上的情况,不进行求解,直接刷新换题。

#总结:
在本次的自动点击的脚本编写历程中,有一些比较大的收获,比如原来应用还有缩放这个情况,以前真不知道,同时对于内存的理解也更进一层了,以前只知道,数据加载到内存里,有时候为了节省内存,会把暂时不会的数据暂存到磁盘里即交换区的作用。。。但不能直接感受到,这次加载亮灯方案,内存巅峰时刻跑到了97%(看的我可慌了),我才意识到什么叫做空间复杂度,节省内存的重要性。。。为什么要考虑内存空间。。。等一系列问题。。。,同时也有了经验,不要太天真,想像着游戏只提供了100种亮灯的初始分布情况,实际上人家是用算法随机生成的。。。随机生成的。。。