前言:
  算是"long long ago"的事了, 某著名互联网公司在我校举行了一次"lengend code"的比赛, 其中有一题就是"智能俄罗斯方块". 本着一向甘做分母, 闪耀分子的绿叶精神, 着着实实地打了一份酱油. 这次借学习H5的机会, 再来重温下俄罗斯方块的AI编写
  本系列的文章链接如下:
  1). 需求分析和目标创新
  2). 游戏的基本框架和实现
  这些博文和代码基本是同步的, 并不确定需求是否会改变, 进度是否搁置, 但期翼自己能坚持和实现.

演示&下载:
  该版本依旧较为简陋, 效果如图所示:
  
  其代码下载地址为: http://pan.baidu.com/s/1sjyY7FJ
  下载解压目录结构如下所示:
  
  点击tetris.html, 在浏览器上运行(由于HTML5程序, 最好在Chrome/Firefox上运行).

算法分析:
  核心算法参考了如下博文:
  • 传统规则俄罗斯方块AI技术介绍 
  • 控制台彩色版带AI的『俄罗斯方块』
  本程序也采用改进的Pierre Dellacherie算法(只考虑当前方块).

  其对局面的评估, 采用6项指标:
  1). Landing Height(下落高度): The height where the piece is put (= the height of the column + (the height of the piece / 2))
  2). Rows eliminated(消行数): The number of rows eliminated.
  3). Row Transitions(行变换): The total number of row transitions. A row transition occurs when an empty cell is adjacent to a filled cell on the same row and vice versa.
  4). Column Transitions(列变换): The total number of column transitions. A column transition occurs when an empty cell is adjacent to a filled cell on the same column and vice versa.
  5). Number of Holes(空洞数): A hole is an empty cell that has at least one filled cell above it in the same column.
  6). Well Sums(井数): A well is a succession of empty cells such that their left cells and right cells are both filled.

  对各个指标进行加权求和, 权重系数取自经验值:

1   -4.500158825082766
2   3.4181268101392694
3   -3.2178882868487753
4   -9.348695305445199
5   -7.899265427351652
6   -3.3855972247263626

源码解读:
  代码文件结构如图所示:
  
  • tetris_base.js: 公共的数据结构, 包括方块定义和方块池等
  • tetris_ai.js: 具体定义了AI的核心算法和数据结构.
  • tetris_game.js: 是整个程序的展示和驱动.
  这边主要讲讲tetris_ai.js这个代码文件, 里面有三个重要的类, MoveGenerator, Evaluator, AIStrategy. 
  MoveGenerator用于生成各个可行落点以及对应的路径线路:

/** @brief    走法生成器*/
function MoveGenerator() {
}MoveGenerator.prototype.generate = function(tetrisUnit, shape) {var keymapFunc = function(x, y, idx) {return "" + x + ":" + y + ":" + idx;}var moveMapFunc = function(step) {return {x:step.x, y:step.y, idx:step.idx};}var results = [];var boards = tetrisUnit.boards;var rownum = tetrisUnit.row;var colnum = tetrisUnit.col;var shapeArrs = shape.shapes;var occupy = {}var actionQueues = [];actionQueues.push({x:shape.x, y:shape.y, idx:shape.idx, prev:-1});occupy[keymapFunc(shape.x, shape.y, shape.idx)] = true;var head = 0;while ( head < actionQueues.length )  {var step = actionQueues[head];// 1). 向左移动一步var tx = step.x - 1;var ty = step.y;var tidx = step.idx;if ( tetrisUnit.checkAvailable(tx, ty, shapeArrs[tidx]) ) {var key = keymapFunc(tx, ty, tidx);if ( !occupy.hasOwnProperty(key) ) {actionQueues.push({x:tx, y:ty, idx:tidx, prev:head});occupy[key] = true;}}// 2). 向右移动一步tx = step.x + 1;ty = step.y;tidx = step.idx;if ( tetrisUnit.checkAvailable(tx, ty, shapeArrs[tidx]) ) {var key = keymapFunc(tx, ty, tidx);if ( !occupy.hasOwnProperty(key) ) {actionQueues.push({x:tx, y:ty, idx:tidx, prev:head});occupy[key] = true;}}// 3). 旋转一步tx = step.x;ty = step.y;tidx = (step.idx + 1) % 4;if ( tetrisUnit.checkAvailable(tx, ty, shapeArrs[tidx]) ) {var key = keymapFunc(tx, ty, tidx);if ( !occupy.hasOwnProperty(key) ) {actionQueues.push({x:tx, y:ty, idx:tidx, prev:head});occupy[key] = true;}}// 4). 向下移动一步tx = step.x;ty = step.y + 1;tidx = step.idx;if ( tetrisUnit.checkAvailable(tx, ty, shapeArrs[tidx]) ) {var key = keymapFunc(tx, ty, tidx);if ( !occupy.hasOwnProperty(key) ) {actionQueues.push({x:tx, y:ty, idx:tidx, prev:head});occupy[key] = true;}} else {// *) 若不能向下了, 则为方块的一个终结节点.var tmpMoves = [];tmpMoves.push(moveMapFunc(step));var tprev = step.prev;while ( tprev != -1 ) {tmpMoves.push(moveMapFunc(actionQueues[tprev]));tprev = actionQueues[tprev].prev;}tmpMoves.reverse();results.push({last:step, moves:tmpMoves});}head++;}return results;}

Evaluator类, 则把之前的评估因素整合起来:

function Evaluator() {
}Evaluator.prototype.evaluate = function(boards) {
}function PierreDellacherieEvaluator() {
}PierreDellacherieEvaluator.prototype = new Evaluator();
PierreDellacherieEvaluator.prototype.constructor = PierreDellacherieEvaluator;PierreDellacherieEvaluator.prototype.evaluate = function(boards, shape) {return (-4.500158825082766) * landingHeight(boards, shape)          // 下落高度+ (3.4181268101392694) * rowsEliminated(boards, shape)      // 消行个数+ (-3.2178882868487753) * rowTransitions(boards)            // 行变换+ (-9.348695305445199) * colTransitions(boards)             // 列变化+ (-7.899265427351652) * emptyHoles(boards)                 // 空洞个数+ (-3.3855972247263626) * wellNums(boards);                 // 井数
}

AIStrategy整合了落地生成器评估函数, 用于具体决策最优的那个落地点, 以及行动路线.

function AIStrategy() {this.generator = new MoveGenerator();this.evalutor = new PierreDellacherieEvaluator();
}/** @brief 作出最优的策略* @return  {dest:{x:{x}, y:{y}, idx:{idx}}, [{action_list}]}*/AIStrategy.prototype.makeBestDecision = function(tetrisUnit, shape) {var bestMove = null;var bestScore = -1000000;// 1) 生成所有可行的落点, 以及对应的路径线路var allMoves = this.generator.generate(tetrisUnit, shape);// 2) 遍历每个可行的落点, 选取最优的局面落点for ( var i = 0; i < allMoves.length; i++ ) {var step = allMoves[i].last;var shapeArrs = shape.shapes;var bkBoards = tetrisUnit.applyAction2Data(step.x, step.y, shapeArrs[step.idx]);// 2.1) 对每个潜在局面进行评估var tscore = this.evalutor.evaluate(bkBoards, {x:step.x, y:step.y, shapeArr:shapeArrs[step.idx]});// 2.2) 选取更新最好的落点和路径线路if ( bestMove === null || tscore > bestScore ) {bestScore = tscore;bestMove = allMoves[i].moves;}}// 3) 返回最优可行落点, 及其路径线路return {score:bestScore, action_moves:bestMove};} 

注: 该代码注释, 诠释了决策函数的整个流程.

效果评估:
  该AI算法的效果不错, 在演示模式下, 跑了一晚上, 依旧好好的活着. 这也满足了之前想要的需求和功能.

总结:
  该算法的权重系数采用了经验值. 当然了, 也可以借助模拟退火算法来学习参数, 不过由于游戏本身的不确定性/偶然性影响, 收敛的效果并非如预期那么好. 有机会再讲讲. 
  无论怎么样, 该AI可以扮演一个合格的"麻烦制造者", 让游戏充满趣味和挑战性. 勿忘初心, let's go!!!

http://www.cnblogs.com/mumuxinfei/p/4587325.html

H5版俄罗斯方块(3)---游戏的AI算法相关推荐

  1. H5版俄罗斯方块(4)---火拼对战的雏形

    前言: 勿忘初心, 本系列的目标是实现一款类似QQ"火拼系列"的人机对战版俄罗斯方块. 在完成了基本游戏框架和AI的算法探索后, 让我们来尝试一下人机大战雏形编写. 本系列的文章链 ...

  2. H5版俄罗斯方块(5)---需求演进和产品迭代

    前言: 产品的形态是不断迭代的, 从粗糙到精致, 从简易到立体. 有了最初的技术积累和时间思考后, 终于明确了该游戏的方向. 我想说的是: 技术不是重点, 产品/用户体验才是核心议题.  结合朋友的游 ...

  3. 《游戏学习》Java版俄罗斯方块小游戏源码实战

    [Java版俄罗斯方块]     增加保存配置信息到文件的功能,声音设置.显示设置.关卡选择等配置信息在修改后将会保存在jar包同级目录下(以jar相同的文件名+.cfg后缀保存) [菜单选项]    ...

  4. H5版俄罗斯方块游戏开发:需求分析和框架实现

    俄罗斯方块和五子棋一样,规则简单,上手容易.几乎每个开发者,都会在其青春年华时,签下"xx到此一游".犹记得大一老师在布置大程作业的时候提过:"什么都可以写,唯一不能写的 ...

  5. H5版俄罗斯方块(1)---需求分析和目标创新

    前言: 俄罗斯方块和五子棋一样, 规则简单, 上手容易. 几乎每个开发者, 都会在其青春年华时, 签下"xx到此一游". 犹记得大一老师在布置大程作业的时候提过: "什么 ...

  6. 前端实现双人联机版俄罗斯方块小游戏2(实现双人联机)

    基于websocket实现双人联机俄罗斯方块游戏,逻辑思路整理如下 思路整理 1.游戏开始,双方都收到start消息,同时调用start方法 2.start方法中,调用game.init方法,同时发送 ...

  7. Cocos Creator 3D下TypeScript实现H5版3D投篮游戏!

    效果预览 ​ 获取代码 关注公众号,发送[3d篮球]获取代码.  游戏介绍 ● 点击屏幕,根据按住屏幕的时间,进行蓄力,时间越短,发出去的力越小,时间越长,发出去的力越大,超过了最大力,再次从最小里开 ...

  8. 俄罗斯方块java机器人_H5版俄罗斯方块(4)---火拼对战的雏形

    前言: 勿忘初心, 本系列的目标是实现一款类似QQ"火拼系列"的人机对战版俄罗斯方块. 在完成了基本游戏框架和AI的算法探索后, 让我们来尝试一下人机大战雏形编写. 本系列的文章链 ...

  9. java五子棋AI算法人机对战(春物彩羽版可下载试玩PC端)

    五子棋AI算法 前言: 坐标西安,写于疫情封城期间.改进了之前写的基于极大极小值策略AI五子棋游戏,是用java实现的,采用了java老旧的jframe窗体和绘图类.写好之后整理成了这篇博客. 游戏采 ...

最新文章

  1. JavaScript 面向对象实战思想
  2. 微信支付 统一下单 字段 body 为中文时 报【签名错误】解决方案(C# SDK)
  3. HTML5实现手势屏幕解锁
  4. (30个原生js挑战)原生js实现钟表
  5. 【Python学习系列十一】Python实现决策树实现C4.5(信息增益率)
  6. 使用BizTalk Server常见问题处理
  7. Android中Handler的使用方法——在子线程中更新界面
  8. Android 系统(189)---Android Handler:这是一份 全面、详细的Handler机制 学习攻略
  9. 英伟达:今年显卡将继续供不应求 尽量保证供应普通玩家
  10. matlab学习笔记(4)
  11. 可视化编辑json数据——json editor
  12. Maven的下载安装和配置阿里云镜像教程
  13. 第三阶段应用层——1.2 数码相册—字符编码
  14. 模块sys, os, glob, pickle, subprocess常见用法
  15. 对象数据如何转化成数组
  16. 小程序源码:修复登录接口版最新知识付费变现小程序源码下载-独立后台版本
  17. 聚类算法:K-means算法
  18. mybatis实战:一、mybatis入门(配置、一些问题的解决)
  19. 小试ildasm,ilasm,ilspy
  20. oracle课程设计图书销售系统,oracle课程设计图书管理系统

热门文章

  1. C#中的delegate和event (转)
  2. SQL触发器 常用语句
  3. 1682. [HAOI2014]贴海报
  4. iptables 垫脚石之 NAT DNAT SNAT 代理 深度理解
  5. #翻译#将像素绘制到屏幕上
  6. sed 和 awk 正则表达式
  7. Linux学习之CentOS(十三)--CentOS6.4下Mysql数据库的安装与配置
  8. 递归存储过程中使用cursor
  9. android adb 进程端口号被占解决方法
  10. ArcGIS JavaScript在线编辑