SSDUT 小学期大作业,计划用 C++ 完成俄罗斯方块,用 QT 实现用户界面。实现基本功能后有多余时间,就加了 AI 的模块。目前的算法经测试,可以实现 25万 行左右的消除,在改进方块生成随机函数 (BAG7) 后,消除的行数会有大幅增加。

效果预览:

以下是正文:

算法选取

实现 AI 的算法我考虑了两种,在其中权衡:

  • Pierre Dellacherie算法,求出一些参数,基于评估函数实现,每次只考虑当前情况的最优解
  • 搜索,基于DFS,用A*剪枝优化,结合多步的情况

DFS 需要优化的地方很多,假如计算的层数不够、没有高效的剪枝,一不小心容易写成人工智障,时间复杂度也不好。

Pierre Dellacherie算法更加清晰,复杂度更低。虽然不是很符合我的存储结构,但经过修改还是可以很好实现。

方块存储

我存储方块的模式比较简单暴力,直接放在数组矩阵里。共有 7 种方块,方块经过旋转形状不同,也分开存储。因此,共需要存 19 组数据来记录所有的方块。

以 “L” 形方块为例:

            "#3##",        //表示同一种方块的不同旋转"#3##","#33#","####"        "####","333#","3###","####""33##","#3##","#3##","####""##3#","333#","####","####"

网上许多的方案是在确定一个中点(旋转点)之后,给出其他块的相对位置来表示形状,旋转时也是通过位置参数的变化实现。我这里虽然有些暴力但是可以减少很多计算。Pierre Dellacherie算法中的一个参数也需要用到中点这一概念,但很明显我的存储结构中没有维护中点这一概念。但其实也并不需要,我们可以简单修改此参数的获取规则,实现相同的作用。

Pierre Dellacherie算法

该算法只考虑当前,不对未来的情况进行计算。注重的是“不死性”,追求方块的“密集”,有时就算可以一次性消除 3 行,却会使全局方块更加“疏”,算法是不会在这种情况落子的。

每次生成一个方块,便穷举该方块所有旋转的所有落点。一种方块最多有 4 种旋转,并且由于游戏界面是 10 * 20 的,所以对于每个旋转形状,只需要考虑  10 种落点,故需要考虑的情况不多。加上剪枝后,穷举法也变得十分可观。

算法的核心是一个评估函数。对穷举出的每一种下落情况,计算 6 个参数值,用评估函数加权求和得到一个值,该值最大的情况便是目前方块的最优下落位置。

六个参数分别是:

  • Landing height

表示方块下落后高度的参数 (相对于底部的高度) 。很明显,方块下落后越靠下越安全,使整体的方块堆越矮越好。传统的Pierre Dellacherie算法中,该参数是通过“中点”的位置给出,但是我的存储结构中没有这一概念。其实,不需要这样也可以得出。我的做法是求出当方块刚刚触碰到底部时,该方块的最高点和最低点高度的平均值,也可以表示该参数。注意,这里的 “刚刚触碰到底部” 是指方块无法再向下移动,但是还没有消除满行的中间时刻。之所以选这个时间点,是因为有可能在某种情况,方块固定到底部并且满行消除后,整个方块全都被消除,无法得到最高最低点。6 个参数中,只有这个参数选取的是这个时刻,其他 5 个都是指方块固定好,并且满行消除后的情况。如图,消除前,方块最高点和最低点高度分别为5和4,因此Landing height 值为 (5+4)/2 ;

  • Eroded piece cells

 表示当前方块下落后,消除的行数和此方块中参与到行消除的方块数目的乘积,体现的是方块对消除的贡献程度。如图,共消除 1 行,该方块贡献了 3 个单位块,因此Eroded piece cells 值为 1*3 ;

  • Row transitions

每一行中,方块有或无的变化次数之和。从有方块的格子到相邻的空白格计入一次变换。其中,边框算作有方块。注意,不仅仅是计算底部方块堆叠的区域,上方空白区域也要计算。如图,值为(16*2) + 4 + 2 + 6 + 6 ;

  • Colum transitions

列的变换次数,和 Row transitions 行的变换一个道理,类比一下便可。如图为(4*4) + (2*6) ;

  • Number of Holes

空洞的数量。空洞指的是,每列中某个方块下面没有方块的空白位置,该空白可能由 1 个单位或多个单位组成,但只要没有被方块隔断,都只算一个空洞。注意,空洞的计算以列为单位,若不同列的相邻空格连在一起,不可以将它们算作同一个空洞。如图,空洞有4个 ;

  • Well Sum

井的和。井指的是某一列中,两边都有方块的连续空格,边框算作有方块。若一个井由 1 个方块组成,则为 1 ;若由连续 3 个组成,则和为 1 + 2 + 3 的连加。如果一个井的顶部有方块的存在,则该“井”同时也是“空洞”。如图,“井”有 3 个,连加和为1 + (1+2) + (1+2) ;

图片示例 :

评估函数如下 (首字母简写):

带入权重值后的评估函数详见代码

权重经验值:

A1:   -4.500158825082766 
A2:   3.4181268101392694 
A3 :  -3.2178882868487753 
A4 : -9.348695305445199 
A5 : -7.899265427351652 
A6 : -3.3855972247263626 

代码:

//比较赶时间,粗糙,希望别喷代码风格QAQ
#include <iostream>
using namespace std;class AI{
public:AI(char MAP[100][100], int);int LandingHeight;int RowsEliminated;int RowTransitions();int ColumnTransitions();int NumberofHoles();int WellSums();int PierreDellacherie();bool collisionJudge(int, int);void addTomap(int, int);void AIgo(int &X, int &Y,int &shape);private:int maxValue;char map[22][12];                        //用来操作char copyMap[22][12];                    //记录传入情况char cur[4][4];int type;//可以传出
};AI :: AI(char MAP[100][100], int TYPE) {     //获得当前局势type = TYPE;//传入方块的值for(int i = 0;i < 22; i++)for(int j = 0;j < 12; j++) {if (MAP[i][j] != '#')copyMap[i][j] = MAP[i][j];else copyMap[i][j]=0;//空格赋值为0}maxValue = -0x3f3f3f;
}//碰撞检测只需要检测向下移动时的状态(由于变形是被枚举,左右移动不是用户操作的,一开始一定水平先移动到最优位置),用传入地图
bool AI :: collisionJudge(int x ,int y) { for(int i=0; i<4; i++)for(int j=0; j<4; j++){int tx = x+i;int ty = y+j;if(cur[i][j] && (tx <= 0 || tx >= 21 || ty <= 0 || ty >= 11))return false;if(cur[i][j] && copyMap[tx][ty])            //有方块重合return false;}return true;
}void AI :: addTomap(int x, int y) {for(int i=0; i<4; i++)for(int j=0; j<4; j++) {int tx = x + i;int ty = y + j;if(tx<1 || tx > 20 || ty < 1 || ty > 10)    //界外不用考虑,剪枝continue;if(cur[i][j])map[tx][ty] = cur[i][j];}int maxHeight  = 21 - x;bool flag = true;for(int i=0; i<4 && flag; i++)for(int j=0; j<4; j++) {if (cur[i][j]){maxHeight -= i;flag = false; break;}}int minHeight = 18 - x;flag = true;for(int i=3; i>=0 && flag; i--)for(int j=0; j<4; j++) {if (cur[i][j]){minHeight += (3-i);flag = false; break;}}LandingHeight = (maxHeight + minHeight)/2;int cnt =0, sum=0;for(int i=20; i>=1; i--){bool flag = false;for(int j=1; j<=10; j++)if(!map[i][j]){flag = true;break;}if(flag)continue;cnt++;                                    //行数for(int ii = 1; ii <= 10; ii ++)if (map[i][ii] == '#') sum ++;        //计算被消去个数for(int j=i; j>1; j--)for(int k=1; k<=10; k++)map[j][k] = map[j-1][k];i++;}RowsEliminated = cnt * sum;
}int AI :: RowTransitions() {int cnt = 0;for(int i = 1; i <= 20; i ++)for(int j = 0; j <= 10; j++) {if (!(map[i][j]&&map[i][j+1]) && (map[i][j]||map[i][j+1]))cnt ++;}return cnt;
}int AI :: ColumnTransitions() {int cnt = 0;for(int j = 1; j <= 10; j ++)for(int i = 0; i <= 20; i++) {if (!(map[i][j]&&map[i+1][j]) && (map[i][j]||map[i+1][j]))cnt ++;}return cnt;
}int AI :: NumberofHoles() {int cnt = 0;bool flag;for(int j = 1; j <= 10; j ++){flag = false;for(int i = 1; i <= 20; i++) {if (map[i][j]) flag = true;elseif (flag == true) {cnt ++;flag = false;}}}return cnt;
}int AI :: WellSums() {int sum = 0,cnt = 0;int well[21] = {0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105,120, 136, 153, 171, 190, 210};        //井的打表for(int j = 1; j <= 10; j ++)for(int i = 1; i <= 20; i++) {if(!map[i][j] && map[i][j-1] && map[i][j+1]) {cnt ++;}else {sum += well[cnt];cnt = 0;}}return sum;
}void AI :: AIgo(int &X, int &Y,int &shape) {int Next[19]={1,0,3,2,5,6,7,4,9,10,11,8,13,14,15,12,17,16,18};int bag[7]={2,2,4,4,4,2,1};  //有几种变形int bagID[19]={0,0,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,6};//type对应char b[19][5][5]={                            //存方块的数据,先不贴代码了,需要的话联系我的邮箱:originum@126.com   };for(int t = 0; t < bag[bagID[type]]; t++) {     //不同变形枚举type = Next[type];for (int i = 0; i < 4;i ++)                 //得到枚举的方块for (int j = 0; j < 4;j ++) {if (b[type][i][j] == '#')cur[i][j] = 0;elsecur[i][j] = '#';                //特殊标记。此时形状无意义}    for (int i = 1; i <= 10; i++) {if (collisionJudge(1,i)) {              //当在第一行都判段为碰撞时,就是出界,不用考虑,理论上也不考虑方块逼近到最高处的可能性int j = 1;while (collisionJudge(++j,i));      //当没触底碰撞时,向下移动j --;for(int ii = 0;ii < 22; ii++)       //更新for(int jj = 0;jj < 12; jj++)map[ii][jj] = copyMap[ii][jj];  //把块加入mapaddTomap(j, i);int value = PierreDellacherie();if (value > maxValue) {X = j;Y = i;shape = type;maxValue = value;}}}}
}int AI :: PierreDellacherie() {  int score =-45*LandingHeight + 34*RowsEliminated -32*RowTransitions() -93*ColumnTransitions() -79*NumberofHoles() -34*WellSums();return score;
}

请求源代码的人很多,我就放到GitHub上了,请见:https://github.com/Originum/AI-Tetris

这是我从前初学C++时的作业,欢迎完善。

俄罗斯方块人工智能 [ AI ]相关推荐

  1. ai俄罗斯方块java_俄罗斯方块进阶--AI俄罗斯方块

    原标题:俄罗斯方块进阶--AI俄罗斯方块 人工智能大火的今天,如果还是自己玩俄罗斯方块未免显得太LOW,为什么不对游戏升级,让机器自己去玩俄罗斯方块呢?有了这个想法之后利用周六周日两天的时间去搜集了大 ...

  2. 人工智能AI Boosting HMC Memory Chip

    人工智能AI Boosting HMC Memory Chip Innosilicon的AI Boosting HMC存储芯片适用于高速,高带宽和高性能存储领域,例如AI边缘,数据中心,自动化等. H ...

  3. 中国人工智能AI框架自主研发

    中国人工智能AI框架自主研发 中国AI界争相构建AI开源框架的背后,技术和业务层面的考量因素当然重要,但也不应忽视国家层面的政策支持.对于AI基础设施的建设,中国政府在<新一代人工智能发展规划& ...

  4. 年终总结:2021年五大人工智能(AI)和机器学习(ML)发展趋势

    明年,人工智能技术将在企业运营中更加根深蒂固. 人工智能(AI)和机器学习(ML)一直在改变着我们的世界,但是2020年给这两个改变游戏规则的人带来了新的机遇和紧迫性-预计2021年会有更大的发展. ...

  5. 巨变的时代、人工智能AI带来颠覆性的影响、技术十大趋势

    https://www.toutiao.com/a6626981566553784835/ 导读:我们身处一个巨变的时代,毋庸置疑,人工智能 (AI) 已经成为科技前沿之一,将给许多行业带来颠覆性的影 ...

  6. 为何人工智能(AI)首选Python?如何转行Python人工智能?

    一.为何人工智能(AI)首选Python? 读完这篇文章你就知道了.我们看谷歌的TensorFlow基本上所有的代码都是C++和Python,其他语言一般只有几千行 .如果讲运行速度的部分,用C++, ...

  7. 数据挖掘(data mining),机器学习(machine learning),和人工智能(AI)的区别是什么? 数据科学(data science)和商业分析(business analytics

    数据挖掘(data mining),机器学习(machine learning),和人工智能(AI)的区别是什么? 数据科学(data science)和商业分析(business analytics ...

  8. 人工智能ai 学习_学习代理| 人工智能

    人工智能ai 学习 Learning is an important part of human behavior. It is the first step in the development p ...

  9. 人工智能ai 学习_人工智能中学习代理的要素

    人工智能ai 学习 As already discussed, the Learning agents have the capability to improve their knowledge b ...

最新文章

  1. 西南大学计算机英语统考时间,西南大学2018年9月全国计算机等级考试报名通知...
  2. 荣发护肤护甲增强配方 Hair, Skin and Nails Plus 100 tablets
  3. DataSet Filter用法
  4. 【TCP/IP】一张图带你读懂TCP/IP协议
  5. 响应式网站——第二话(技术实现)
  6. jdbc mysql myeclipse_关于JDBC连接MySQL的问题,我一直解决不了(Myeclipse 环境下)
  7. NOI数学:狄利克雷(Dirichlet)卷积
  8. Leetcode1025.除数博弈
  9. Computing Platform------系统平台及其系列
  10. 金蝶k/3 K3云之家消息查询发送是否成功SQL语句
  11. 解决Win7 64位安装 Microsoft .NET Framework 4 失败的情况
  12. 2020-12-23 PMP 群内练习题 - 光环
  13. 金蝶连服务器显示演示版,金蝶正版和金蝶演示版的区别
  14. easy WeChat 配置 企业微信代开发模板回调URL(PHP版本)
  15. 数字转中文(大写,小写)数字,金额
  16. 6to4隧道实验配置
  17. 用Java编写的双色球摇奖系统
  18. 百度地图 坐标偏移、覆盖物偏移解决方案
  19. 事和书的感悟_李孟_新浪博客
  20. Win10玩魔兽争霸不能全屏显示的设置教程

热门文章

  1. 中国牛奶市场竞争态势分析及未来发展前景预测报告2022-2028年版
  2. 摄影_焦点、对焦、对焦点
  3. Windows下DOS窗口修改编码
  4. 华为实验28-WAN接入配置
  5. 从事游戏开发需要什么技能
  6. 教你如何提取Window系统蓝屏日志
  7. 只有程序员才能看懂的16张高端漫画
  8. 达梦DMFLDR使用介绍
  9. 穆利堂[推荐]WxCSM客户服务整体解决方案-河南郑州房地产客户服务管理系统软件-穆穆-movno1
  10. 【程序员情商up】说话的智慧