PS:第一次写博客园BLOG,意义非凡啊!这篇文章本来是想在CSDN写的,但CSDN的写作页面实在慢得出奇(我完全搞不懂它为什么会这么慢),于是就跑博客园来了。听说博客园牛人众多,希望我这篇小文没有拉低博客园文章的总体水平~

又PS:标题写这么多纯粹是为了增加本文章被广大网友同胞搜索到的概率。不然此篇文章有淹没在互联网的汪洋大海之忧啊。

好了以下是正文:

一、前言

闲来无事,见QQ游戏“火拼俄罗斯”甚受欢迎,遂有作一作弊器以打发时间、兼刷分之打算。

要写一个作弊器,第一时间想到的当然是去google一下,看看有没有已经发表的现成的俄罗斯方块AI算法。时间有限(其实是我懒),我只比较了据说是“只看当前的落子”中最厉害的Pierre Dellacherie的算法、和“看当前的落子以及下一个落子”最厉害的Colin Fahey的算法。结果(居然)是Pierre Dellacherie算法最厉害。。。

该算法的实际效果请看视频。视频中用的不是Pierre Dellacherie算法,不过效果也差不多。Pierre Dellacherie算法会在下面简要描述。

二、开始动手

要做一个火拼俄罗斯的作弊器,当然可以简单的分为三部分:游戏界面扫描层、AI层、反馈控制层。好了,画个图出来。据说画出图来显得比较有水平。

图1:作弊器的分层

三、游戏界面扫描

这个没什么好说的,就是找到游戏窗口、把图像从游戏窗口的DC上Blt过来、然后每个方格内取一些特征明显的点的颜色值,从颜色值去判断该方格内有没有方块存在。至于取哪些点、怎样判断,先截个图下来放大N倍慢慢看就看出来了。

图2:火拼俄罗斯游戏界面

我们还可以注意到,在火拼俄罗斯游戏中,除了当前的落子之外,下两个落子都是可以知道的;这对让AI胜过人脑甚为有利。


四、AI层

这个就是本文的重头戏了!

一开始时,我用Pierre Dellacherie算法去实现俄罗斯方块的AI。Pierre Dellacherie算法(见引用[1])简单来说是这样的:

1.尝试着对当前落子的每一种旋转变换、从左到右地落子,产生所有落法。

2.对每一种落法进行评价。评价函数为:

rating = (-1.0) * landingHeight          + ( 1.0) * erodedPieceCellsMetric
         + (-1.0) * boardRowTransitions + (-1.0) * boardColTransitions
         + (-4.0) * boardBuriedHoles    + (-1.0) * boardWells;
其中,landingHeight指当前落子落下去之后,落子中点距底部的方格数;erodedPieceCellsMetric = 消去行 * 当前落子被消去的格子数;boardRowTransitions指各行的“变换次数”之和,一行中从有方块到无方块、无方块到有方块被视为一次“变换”,游戏区域左右边界也视作有方块;boardColTransitions指各列的“变换次数”之和;boardBuriedHoles指各列中间的“空洞”方格个数之和;boardWells指各“井”的深度的连加到1的和之和,“井”指两边皆有方块的空列。

评价还包括优先度。优先度在两个局面的评分相同时发挥作用,取评分相同但优先度高者。优先度的计算方法为:

若落子落于左侧:priority = 100 * 落子水平平移格子数 + 10 + 落子旋转次数;

若落子落于右侧:priority = 100 * 落子水平平移格子数 + 落子旋转次数;

3.比较每一种落法的评分与优先度。在同为最高评分的落法中,取优先度最高者。

算法并不复杂,实际表现也很优秀,如果是单人游戏的话,可以一直玩下去,在几万、几十万次落子内不会game over。使用该算法做成作弊器之后,在火拼俄罗斯新手场很简单地就可以刷到300+的分数。以下是运行时的截图:

图3:使用Pierre Dellacherie算法时作弊器运行截图

但到了这个分数段以后,该算法就无能为力了。因为火拼俄罗斯比的不仅是“不死”,它还有一条规则:

“玩家消层后,将对与其不同队的所有玩家造成伤害。一次消四层则对其他玩家加三层,一次消三层则对其他玩家加两层。”

和高分玩家火拼时,对方往往会等待一次性消去3行、4行的机会,一旦他们得逞,己方马上会增加2、3行:此消彼涨之下,己方会在瞬间陷入比对方多出5、7行的劣势。而Pierre Dellacherie算法注重的是“不死”,很少会一次性消去3行,一次性消去4行的情况更是几乎没有——因此需要有针对性地对此算法进行改进。

对于某一种落法来说,我们从Pierre Dellacherie算法中已经可以获得8个指标了。所剩下的问题就是如何从这些指标算出它的评分。怎么办呢?像我这样的懒人当然不希望自己去凑参数了,就用一个两层的神经网络去算吧。而神经网络中各个节点的参数,就用遗传算法去算它一个晚上——好办法!

这样一来,用indicators[]去表示各个指标,rating表示输出,factors[][]表示各参数,神经网络的结构如下图所示:

图4:新算法神经网络结构


而每个节点都只有一个输出,不存储状态。节点的计算函数为:


(未完待续)


posted on 2009-03-24 16:47  光明左使  阅读(10096)  评论(21编辑  收藏  举报