APP下载

基于Pierre Dellacherie算法的AI俄罗斯方块设计

2023-04-16陶岚菊

科技创新与生产力 2023年1期
关键词:行数数组方块

薛 鹏,陶岚菊

(成都锦城学院,四川 成都 611700)

1 AI俄罗斯方块的研究背景

俄罗斯方块是一款经典的益智型游戏,如何在俄罗斯方块里实现不同形状的板块的智能旋转、下落并最终摆放到合适的位置上,是人工智能(Artificial Intelligence,AI)领域的一个问题[1]。俄罗斯方块的游戏规则如下:由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条满行或几条满行,这些完整的满行会随即消失,给新落下来的板块腾出空间;与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端的游戏上边界,玩家便告输,游戏结束[2]。学者Heidi Burgiel经过研究,已证明俄罗斯方块游戏最终一定会结束。因此,设计AI的目的就是为了让AI程序获得更多的分数。AI俄罗斯方块的实现方法非常多,其中Thiery&Scherrer算法、Pierre Dellacherie算法、DFS算法都可以实现俄罗斯方块的AI程序。本文主要通过Pierre Dellacherie算法来实现该程序。

2 问题分析与变量抽象

定义俄罗斯方块的“不死性”:由于游戏的规则是未消除的方块累计高度达到游戏上边界的时候游戏失败,因此方块高度越低,整局游戏“不死性”就越强。随着方块的积累,整局游戏的“不死性”也在下降。通过对“不死性”的分析,将问题抽象成以下6个变量。

2.1 最大高度

该变量用于统计放置后方块距离底部的距离。俄罗斯方块游戏结束的条件就是通过决策放置后的方块有一部分超出游戏规定的上边界,这时判定游戏失败。在游戏过程中,方块累计高度越高,整局游戏的“不死性”也就越低。因此,通过决策放置方块后所有方块中的最大高度是决策的重要考察因素之一。

2.2 消除的满行中属于本次下落板块的方块数量

由于决策期望是消除更多的行数,因此通过决策放置方块后,消除的满行中存在本次放置的板块的方块数量越多,则说明这次板块放置的位置越好。需要设置一个全局的数组,数组中存放每一行方块的空格数量,就可以通过全局的数组算出消除的满行中有多少方块是属于本次放置的板块的。游戏过程中,方块模拟放下的过程中只需要记住方块放下去后能够消除的行的编号,遍历出所有的情况,将消除的满行行数作为数组的下标进行相加,变量Max_count为其最大值。

2.3 行变换

行变换是指原本这个位置没有方块但是经过填充后变成有方块,或者这个位置本来有方块但是变成没有方块,这两种情况都可以视为发生过一次行变换。统计出所有的变换量,并返回这个参数,行变换的数量从侧面上反映出了方块的平整程度。如果游戏中方块摆放得不够平整,特别是在方块高度累计过高时,会出现某个高度接近游戏上边界的方块从而导致游戏的结束。方块整体上越平整,游戏整体局面的“不死性”就越强。通过逐层的遍历来实现行变换的统计。

2.4 列变换

列变换是指原本这个位置没有方块但是通过决策导致方块下落在该位置后变成了有方块,或者这个位置原本有方块但是通过决策导致方块被消除变成了该位置没有方块。与行变换几乎相同,不过列变换反映的是空洞的密集程度。空洞对游戏整体局面的影响非常大,列变换的数值越大,出现空洞的概率也就越大。游戏的失败往往也是由于空洞数量过多而导致的。列变换的统计和行变换的统计二者的差别就是:列变换是先遍历列,而行变换是先遍历行。

2.5 空洞数

游戏的失败往往都是由空洞数过多而引起的。在方块的摆放序列中,当一个或者多个空格的周围全部都是方块时决策就将这个或这些空格视为空洞。当空洞累计到一定程度的时候,对于底部方块的消除也将变得困难。空洞数的多少也直接决定着游戏的“不死性”。

2.6 “井”总和

“井”的定义是:它的形状就像生活中的井。中间没有被方块填充,两边都有连续的方块(将游戏的边界也算作方块)。此函数计算的是“井”的高度之和。如果“井”的大小就是1个空格,那么算作这个“井”的大小是1;如果“井”的大小是3个空格组成,那么“井”总和是3+2+1。需要注意的地方是,“井”的类比和生活中的井非常相似,方块两边的高度不相同的时候遵循“短板效应”,当中间为空格的时候,如果左边方块的高度是4,右边方块的高度是3,那么“井”总和依旧是按照3+2+1来作为计算结果进行返回。“井”的存在对游戏整体局面的影响和空洞一样,同样是致命的,当“井”没有被落下来的方块填充反而是将“井”给覆盖起来的时候,“井”就会变成空洞,从而降低游戏整体局面的“不死性”。

2.7 分析总结

通过对问题的分析,可通过抽象出的以上6个变量,将问题具体化。将以上6个变量相结合且作为变量代入评估函数,就可以对所有方块放置的情况进行打分处理,选出分数最高的一种情况来进行方块的放置。如果有多种情况的分数一样,则优先选择靠近左边落下的方案。

3 程序实现

程序采取的是C语言,基于Dev-C++6.5进行实现。俄罗斯方块AI程序的实现分为两部分:第一部分是最基本的游戏内容的实现,这个部分能够实现最原始的俄罗斯方块的玩法;第二部分就是决策评估部分,通过枚举每一种方式的摆放方式并且结合决策评估函数,直接将方块放置在当前游戏整体局面的最佳位置上面,这一部分也是AI功能的重要部分。

3.1 游戏重要函数实现

3.1.1 游戏边界的打印

游戏边界使用二维数组,通过遍历的方式配合语句printf("◆");进行相应的打印。并且将边界数组所存在的值设置为1,空白的位置的值设置为0。需要注意的是,当方块下落触底的时候,不能将方块位置数组的值也设置为1,这是因为这样就会让墙面和触底的方块没有区别,可能导致消除游戏的边界问题,所以应该将触底的方块位置数组的值设置为2,以便和边界产生区分。

3.1.2 方块的打印以及变化

方块的显示问题使用结构体数组来定义,一共有6种结构体数组,这些结构体数组通过在地图数组的对应位置上打印方块来达到生成方块的目的。方块的变化也是结构体数组,根据方块的类型来进行相应的变化,从而选择最优方案进行下落。

3.1.3 随机方块的生成

随机方块通过随机函数struct block*proBlock(int n)来生成,其中n对应的值是rand()%k+1。这样就可以生成从1到k的数字,这些生成的随机数字对应的正是不同的初始方块。

3.1.4 检测方块的状态

检测方块的状态是指检测这个方块是不是应该被固定在这个位置无法移动。方块无法移动的条件就是四周是边界或者其他的方块,如果方块的四周有其他的方块或者边界,那么这个方块就不能继续向下移动。根据游戏的初始化设定,地图中没有方块的位置的值是0。通过遍历,如果方块处往下一格位置的值是1,那么方块就应该停止下来。

3.1.5 方块满行的消除以及相应分数的增加

当方块在一行排满的时候,需要将这一行方块进行消除。每一次方块下落之后,都从游戏的底部向上面开始遍历。方块位置数组赋值是2,当检查完一行之后,如果统计为2的变量的数值等于方块的宽度,那么就认为这一行为满行;再次遍历这一行,并且在地图数组上打印出空格,代表这一行已经被消除。继续向上遍历,将上面每一行的方块都向下移动一格,从而达到视觉上的方块消除效果。每一次方块满行被消除后,全局变量score加上对应的分数。

3.2 评估函数实现

枚举出所有的情况,模拟每个位置摆放的情况,并评估出该位置对应的价值,找出其中的最大值。若其中有两个相同的最大值,则方案就是从左边到右边依次摆放。评估函数具体实现的程序代码如下。

t1=getLandingHeight(bb);//放置后,滑块距离底部的距离;

t2=getRemoveBlock(bb);//放置后,消掉的行数有多少块是属于这个滑块的;

t3=getRowTransitions(bb);//统计每一行的变换;

t4=getColTransitions(bb);//统计每一列的变换;

t5=getHole(bb);//统计空洞的个数;

t6=getWell(bb);//统计“井”的个数;

return k1*t1+k2*t2+k3*t3+k4*t4+k5*t5+k6*t6;

在评估完所有的情况之后,就可以进行AI的具体实现。具体实现方法就是通过枚举的方式再次模拟方块所有下落的位置,从左到右再次算出评分,如果算出来的评分等于前面算出来的最佳评分,那么方块就摆放在这个位置。

4 通过遗传算法和强化学习的方法选取权重

评估函数为k1*t1+k2*t2+k3*t3+k4*t4+k5*t5+k6*t6,这里ki的权重用于评估当前游戏整体局面的优劣程度,如何获得更加准确的权重是很重要的一件事情。如果想引入新的变量来评估游戏整体局面的优劣程度,那么就需要考虑原本的权重系数是否还适用于新的评估函数,新的权重系数怎样设置才能让消除的行数更多。

本文选择遗传算法(Genetic Algorithm),遗传算法非常广泛地应用于参数的优化问题,对于系数的选择也可以看作参数的优化问题。初始设置一个种群(可能存在的解集),通过层层筛选以及交叉组合、变异等方式优胜劣汰,根据得分来判断系数是否合适,从而不断接近目标解集。比如,初始化1 000个解集,通过得分来判断适应度,选择表现相对良好的解集继续进行交叉组合,设置一点扰动,从而构成下一代的解集继续筛选,如此反复。

强化学习也可以作为优化权重的一个选择。在给定的一个解集中,对其中某个系数进行改动,同时程序获得一个反馈值,这个反馈值可以由分数的改变来体现,再通过反馈值不断地调整权重,以便获得更多的激励,如此往复便可以获得更好的权重分布。

5 结束语

本文采用Pierre Dellacherie算法,实现了AI版本的俄罗斯方块,通过多次的试验,最高消除行数是16万行左右,平均消除行数是12万行左右。还有非常多值得改进的地方:一是程序中有多个地方的复杂度都是n2,有的地方甚至达到了n3,这将会让决策计算消耗大量的时间,如果游戏地图开得更大一些就会导致性能有所下降,以至于出现明显的停顿现象,某些部分可以通过动态规划算法的思想来进行优化。二是算法的评估参数不够准确,评估函数前面的参数不够精准导致不能消除更多的行数,参数的调整涉及深入的算法,需要大量的重复试验来测试。遗传算法和强化学习还有很多的方式都可以获得更加合适的权重,是一个非常值得探讨的问题,笔者会在未来进行更多的调整。

猜你喜欢

行数数组方块
有多少个方块
JAVA稀疏矩阵算法
不一样的方块桥
JAVA玩转数学之二维数组排序
谜题方块
英语专业八级统测改错试题语言特征
玉米超多穗行数基因型通15D969 的 单倍体育种效应
玉米超多穗行数DH系15D969的发现
Excel数组公式在林业多条件求和中的应用
寻找勾股数组的历程