前段时间很喜欢玩"开心消消乐"这个休闲小游戏, 刚好大学的一位同学也在玩,后来就想着如果让自己来写这个逻辑要怎么写, 经过一天的构思, 找到一种利用深度优先搜索思想来实现的方式.

三消游戏的规则

  1. 一个m x n 的棋盘内, 初始状态下, 填满若干种不同类型的… 暂且叫Hero吧, 类型的数量根据游戏难度不同而不同, 一般有4~5种.
  2. 同种类型的Hero连着 >= 3时消除, 横着竖着都可以消, 斜着的不能消.

本文约定以下5种不同类型的Hero, 对应的值分别为1,2,3,4,5.

下面模拟一个5x5的棋盘, 说明一下规则,
消除前的棋盘:
1 1 2 4 5
5 1 1 1 4
5 2 1 5 4
4 3 3 3 4

消除后的棋盘:
1 1 2 4 5
5 0 0 0 0
5 2 1 5 0
4 0 0 0 0

算法实现

实现语言最早用的是C++, 后来因为要用Html绘制界面, 所以把代码编译成JavaScript的了, 而且因为现在过年回家了, C++的代码在公司, 只有Js的代码可以Po出来了.

算法其实很简单, 写一个函数ScanPos(pos), 这个函数用来扫描pos点的左、上、右、下四个邻近的点,如果某个邻近的点和当前扫描的点pos的类型一样,则递归调用ScanPos(newpos), 继续递归调用ScanPos, 直到所有方向的点都不匹配或者到达边界为止。

用上面那个例子说明一下扫描的过程:
----> x
|
| 坐标方向, 为了直观,原点为1, 1
V
y

假如我们初始扫描的点是(3, 2), HeroType = 1
1 1 2 4 5
5 1 1 1 4
5 2 1 5 4
4 3 3 3 4

Step.1
1 1 2 4 5
5 1 1 1 4
5 2 1 5 4
4 3 3 3 4

Step.2
1 1 2 4 5
5 1 1 1 4
5 2 1 5 4
4 3 3 3 4

Step.3
1 1 2 4 5
5 1 1 1 4
5 2 1 5 4
4 3 3 3 4
这里扫到5后发现5不匹配, 于是开始往上Scan

Step.4
1 1 2 4 5
5 1 1 1 4
5 2 1 5 4
4 3 3 3 4

Step.5
1 1 2 4 5
5 1 1 1 4
5 2 1 5 4
4 3 3 3 4
Scan到1,1这个点后,左、上、右、下都无效,左上到达边界,右边是来时的地方, 所以也不匹配,下边是5,也不匹配, 所以回退到1,2这个点的ScanPos函数,这个点的上、右也都不匹配, 所以继续往回退,…… 中间过程类似,不再描述。最后扫描后的棋盘状态:
1 1 2 4 5
5 1 1 1 4
5 2 1 5 4
4 3 3 3 4

可以发现这样扫描后,第一行和第三行的3个点,并不符合三消规则,但是也都被扫描到了。这就需要把这些点排除掉。

排除不符合的点

这一步做的比较粗暴,直接两层for循环遍历整个棋盘,在内层循环内对每一行Hero进行遍历时, 记录相邻的相同点的个数,然后把每个点的Match次数保存起来,最后只有Match >=3 的才会消除。

以上就是核心算法了,当然还有很多细节需要注意,比如抽象出Cell类,每个Cell有它的状态以及Match次数, 在每次ScanPos时,都要实时更新Cell的状态,等等。

JavaScript代码实现

function Grid (cols, rows) {this.cols = cols;this.rows = rows;this.board = new Array();this.lastClickPosX = -1;this.lastClickPosY = -1;this.score = 0;
}Grid.prototype.scanPos = function(scanPosX, scanPosY, parentPosX, parentPosY) {scanPosX = parseInt(scanPosX);scanPosY = parseInt(scanPosY);parentPosX = parseInt(parentPosX);parentPosY = parseInt(parentPosY);//console.log("scanPos1: %d %d %d %d", scanPosX, scanPosY, parentPosX, parentPosY);var parentCell = this.getCellByPos(parentPosX, parentPosY);if (parentCell == null) {console.log("parentCell is null");return;}//leftvar leftPosX = scanPosX - 1;var leftPosY = scanPosY;//console.log("(%d %d) (%d %d)", leftPosX, leftPosY, parentPosX, parentPosY);var leftCell = this.getCellByPos(leftPosX, leftPosY);   //console.log("(%d %d) (%d %d)", leftPosX, leftPosY, parentPosX, parentPosY);//console.log("leftCell:", leftCell);   if (leftCell != null&& (leftPosX != parentPosX || leftPosY != parentPosY)&& leftCell.getState() == CELL_STATE.CS_NORMAL && leftCell.getHeroType() == parentCell.getHeroType()){//console.log("left fuck!");leftCell.updateState(CELL_STATE.CS_SCANED);this.scanPos1(leftPosX, leftPosY, scanPosX, scanPosY);}//console.log("-----scan pos: scanPosX=%d, scanPosY=%d", scanPosX, scanPosY);//topvar topPosX = scanPosX;var topPosY = scanPosY - 1;//console.log("(%d %d) (%d %d)", topPosX, topPosY, parentPosX, parentPosY);var topCell = this.getCellByPos(topPosX, topPosY);//console.log("(%d %d) (%d %d)", topPosX, topPosY, parentPosX, parentPosY);//console.log("topCell:", topCell);if (topCell != null&& (topPosX != parentPosX || topPosY != parentPosY)&& topCell.getState() == CELL_STATE.CS_NORMAL && topCell.getHeroType() == parentCell.getHeroType()){//console.log("top fuck!");topCell.updateState(CELL_STATE.CS_SCANED);this.scanPos1(topPosX, topPosY, scanPosX, scanPosY);}//console.log("-----scan pos: scanPosX=%d, scanPosY=%d", scanPosX, scanPosY);//rightvar rightPosX = scanPosX + 1;var rightPosY = scanPosY;//console.log("(%d %d) (%d %d)", rightPosX, rightPosY, parentPosX, parentPosY);var rightCell = this.getCellByPos(rightPosX, rightPosY);//console.log("(%d %d) (%d %d)", rightPosX, rightPosY, parentPosX, parentPosY);//console.log("rightCell:", rightCell);if (rightCell != null&& (rightPosX != parentPosX || rightPosY != parentPosY)&& rightCell.getState() == CELL_STATE.CS_NORMAL && rightCell.getHeroType() == parentCell.getHeroType()){//console.log("right fuck!");rightCell.updateState(CELL_STATE.CS_SCANED);this.scanPos1(rightPosX, rightPosY, scanPosX, scanPosY);}//console.log("-----scan pos: scanPosX=%d, scanPosY=%d", scanPosX, scanPosY);//downvar downPosX = scanPosX;var downPosY = scanPosY + 1;//console.log("(%d %d) (%d %d)", downPosX, downPosY, parentPosX, parentPosY);var downCell = this.getCellByPos(downPosX, downPosY);//console.log("(%d %d) (%d %d)", downPosX, downPosY, parentPosX, parentPosY);//console.log("downCell:", downCell);if (downCell != null&& (downPosX != parentPosX || downPosY != parentPosY)&& downCell.getState() == CELL_STATE.CS_NORMAL && downCell.getHeroType() == parentCell.getHeroType()){//console.log("down fuck!");downCell.updateState(CELL_STATE.CS_SCANED);this.scanPos1(downPosX, downPosY, scanPosX, scanPosY);}//console.log("-----scan pos: scanPosX=%d, scanPosY=%d", scanPosX, scanPosY);
};function scan_match_three_xsame(xList) {for (var itor = 0; itor < xList.length; itor++) {var vecCell = xList[itor];if (vecCell == null)continue;if (vecCell.length < 3)continue;var start = 0;var matchCount = 1;var i;for (i = 1; i < vecCell.length; i++) {var prevCell = vecCell[i-1];var currCell = vecCell[i];//console.assert(prevCell && currCell, "fatal error");if ( prevCell == null || currCell == null|| prevCell.getPosX() != currCell.getPosX() ) {console.assert(0, "fatal error");return;}if (currCell.getPosY() == prevCell.getPosY() + 1) {matchCount++;//continuous}else if(currCell.getPosY() > prevCell.getPosY()+ 1) {//interruptfor (var j = start; j < i; j++) {console.assert(vecCell[j].getRefXSame() == 0);vecCell[j].setRefXSame(matchCount);}start = i;matchCount = 1;}else {console.log("x-wtf?!: ", prevCell, currCell);console.assert(0, "vec's data is not sorted!");}}//loop end, update cell's reffor (var j = start; j < i; j++) {console.assert(vecCell[j].getRefXSame() == 0);vecCell[j].setRefXSame(matchCount);}}
}

因为代码太多,只贴出部分代码, 如果需要完整代码,请在下面留邮箱。

PS. 家里好冷, 写这篇文章手都快dong掉了


2016.11.16 更新

代码上传到CSDN了, 不要分, 要的自取:)
下载地址: http://download.csdn.net/detail/gneveek/9685122


之前我在cocos2dx的应用商店里(是这么叫吧?还是代码库,反正是cocos2d的一个官方工具内, 类似u3d的asset store), 看到过一完成度更高的三消游戏, 那个是用cocos2dx(c++)写的, 资源都有, 逻辑也很完整, 编译完就可以直接玩的, 具体叫什么名字忘了, 你们要以去搜下, 就搜”三消”就行.

Github上可能也有吧, 也可以找找看
我的这个版本比较Low, 完成度很低, 如果是直接拿来做单估计还要花不少时间完善和改BUG, 如果是C++的项目,强烈推荐上面说的cocos2dx的版本

三消游戏核心逻辑的一种实现相关推荐

  1. 经典三消游戏核心玩法

    游戏引擎 - cocos creator 3.6 项目地址 three-tiles · GitCode 核心逻辑代码 - assets/script/game/GameScene.ts import ...

  2. TypeScript开发的斗地主游戏核心逻辑

    源码地址:https://github.com/tianlanlandelan/poker/tree/master/src/utils,喜欢请点星,谢谢大家. 牌面说明 54张牌,2~10.A.J.Q ...

  3. 简单述三消游戏逻辑:

    讲述三消游戏逻辑: 项目简介:支持六种元素,以及障碍元素,行,列,彩虹特殊道具 核心就是填充算法和一个匹配算法: 填充: 本文采用分步填充(即一次只向下移动一个单位) public bool Fill ...

  4. promise的三种状态_一.Promise中核心逻辑的实现

    首先看一下Promise代码: let promise =new Promise((resolve,reject)=>{resolve('成功');//reject('失败'); }) prom ...

  5. B2B2C模式的三种核心逻辑

    B2B2C模式的出发点,是服务原有B2C模式,促进原有的B端和C端完成更加便利.更加科学的.更加顺畅平滑的交易,然后慢慢地,形成平台式的应用场景服务,将B端和C端都变成平台的客户,从而形成一个新的产业 ...

  6. 三消游戏的简单完整实现

    基本思路:用二维数组实现核心的消除逻辑,然后提供接口给UI展示层. 游戏引擎:Quick-Cocos2dx-Community 3.7.7 Release 思路分析:采用自顶向下的设计风格.从用户点击 ...

  7. Unity实现 ZenMatch玩法(也叫3Tile玩法)的核心逻辑

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 Unity实现 ZenMatch玩法(也有叫3Tile玩法)的核心逻辑 前言 提示:这里可以添加本文要记录的大概内容: ZenMatc ...

  8. 三消游戏查找算法的原理和实现

    本文首发于公众号: 小蚂蚁教你做游戏.欢迎关注领取更多学习做游戏的原创教程资料,每天学点儿游戏开发知识. 嗨!大家好,我是小蚂蚁. 今天这篇文章分享一下三消查找算法的原理和实现,其实三消的机制最早源于 ...

  9. 【游戏开发】基于MMORPG的游戏客户端逻辑效率开发思路

    思考:如何提升游戏逻辑开发效率 提升游戏逻辑开发效率的关键在于:游戏新增或修改功能时,需要尽可能减少程序需要重复开发的代码量. 下面主要考虑以下三种减少编码工作量的方法: <1>.尽可能将 ...

  10. RxSwift学习--核心逻辑再探

    前言 通过上一篇内容RxSwift学习--核心逻辑初探,对RxSwift有了些初步的认知,下面通过源码来看一下RxSwift到底有多骚 RxSwift核心逻辑再探 先把上篇中的例子代码搬过来: //第 ...

最新文章

  1. [GXOI/GZOI2019]与或和
  2. 递归锁,死锁,使用递归锁解决死锁,信号量
  3. 预留在PP模块里面的作用
  4. PAT_B_1053_Java(20分)
  5. Spring-jdbc:JdbcTemplate使用简介
  6. php手册学习怎样,PHP新手之如何学习PHP的讲解
  7. 信用卡号校验java_ES reduce 一行代码解决信用卡号验证问题
  8. 一张图了解互联网产品盈利模式
  9. java 广告插件_徒手创建一个chrome扩展-屏蔽广告插件
  10. 全球最大语音识别公司Nuance的衰落与自我救赎
  11. mate 10android o主题,懒萌可爱皮卡丘全局手机主题-适配华为EMUI10.0
  12. 港科夜闻|香港科大陈启峰教授:人工智能与深度学习交织-从竞赛开始,到无尽钻研...
  13. PHP app登录qq,APP配置QQ登录的详细教程
  14. HIT-ICS大作业-程序人生Hello‘s P2P
  15. 使用SecureCRT时,PageDown键无法使用
  16. 【粗翻】CALIPSO数据用户指南-激光雷达等级1B 4.x数据质量声明
  17. 堪称教科书级别的Android音视频入门进阶学习手册,开源分享
  18. MySQL系列---架构与SQL执行流程详解
  19. 博图V14完美自动化程序案例
  20. [渝粤教育] 中国地质大学 钢结构 复习题 (2)

热门文章

  1. 数据结构练习题【哈夫曼树、图、排序、散列表】
  2. 怎么用python输出百分比_Python 如何输出百分比
  3. 冬幕节成就任务攻略指引
  4. android 格林尼治标准时间的 格式转换 (String To Date) 报错 java.text.ParseException: Unparseable date
  5. 2019-01-01T00:00:00.000Z 这种时间日期类型格式是属于:格林尼治时间
  6. 以天下之力成天下之事,菜鸟成物流抗疫主力军
  7. 城镇化率与世界平均水平相当 质量不高问题突出
  8. 计算机与控制工程学院用英语怎么说,计算机与控制工程学院副院长童向荣老师2017年开学典礼致辞...
  9. 树莓派RaspBerryPi上使用3g模块
  10. 机器学习项目 - 泰坦尼克号乘客生还率