消消乐游戏中,一种巧妙的消除条件判别算法


前言

        本文介绍了一种消消乐游戏的核心规则算法,我已经填了N个标签戳,相信你应该不会因为王者荣耀而搜到这篇文章。我们这里只介绍消消乐中关于三个相同颜色的色块成同一直线(横竖都可)的判别算法。


一、所要解决的问题

        我们对问题进行简化,如图1所示,不同类型的色块我们用不同的数字表示,当满足消除条件时,用0表示需要消除的色块(伏笔),即图2.

图 1
1112
1211
1122
2121
图 2
0002
0211
0122
2121


二、问题分析

        判别关于三个相同颜色的色块成同一直线的核心问题在于并行的搜索行列中存在的连续相同数字,即在进行列(行)搜索时,不能影响行(列)搜索。举个例子,如图1所示,在进行列搜索的过程中发现第一列前三个元素满足消除的条件,如果这时候对第一列第一个元素进行修改,那么原本满足消除条件的第一行前3个元素,将会因为列搜索时将第一列第一个元素改为0,而影响第一行原本的搜索结果。

 三、 解决办法

        那么就分而搜索,并行查找。这也是目前很多方法所用到的。即将原矩阵拷贝成两份,分别进行列搜索,行搜索。如图3,4所示

图 3

图 4

     

接下来核心问题是,如何整合两个搜索结果。大多数解决方法是以其中一个结果为基础,在此基础上去整合另外一个结果。但这通常耗时且效率低,在这我介绍一种巧妙的方法。在这我们先捋一下我们的需求:让列搜索结果知道行搜索结果消除的元素。那么接下来我观察下图3和图4相乘的结果,如图5所示:

图 5
0002^2
02^21^21^2
01^22^22^2
2^21^22^21^2

那如果对非零元素再求根号呢?如图6所示:

图 6
0002
0211
0122
2121

 是不是正是我们想要得到的结果呢?这只是用到了0*N=0的原理,这里N\in R

总结

        这是我们团队(河南大学RSBD)举办的编程比赛中的一道题,算法虽然很简单,收获却很大,面对这个问题,很开心我的第一想法不是通过暴力for循环的方式解题,而是相信一定存在规律。

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
消消乐填充算法一种经典的游戏算法,其主要目的是在一个矩阵,将相同的方块进行消除,以获得更高的分数。下面我会提供一个简单的Vue实现消消乐游戏的例子,并讲解其算法原理。 首先,我们需要创建一个包含所有游戏方块的矩阵。我们可以用一个二维数组来表示矩阵,其每个元素代表一个方块。例如: ```javascript data: { matrix: [ [1, 1, 2, 3, 2], [3, 2, 1, 2, 3], [1, 2, 1, 3, 2], [3, 1, 2, 1, 3], [2, 3, 1, 2, 1] ] } ``` 在上述代码,我们创建了一个5x5的矩阵,其每个方块的值为1、2或3。 接下来,我们需要实现一个算法来检查矩阵是否存在相同的方块。我们可以使用深度优先搜索算法来遍历整个矩阵,以找到相邻的相同方块。具体实现代码如下: ```javascript function check(matrix, visited, row, col, value) { if ( row < 0 || row >= matrix.length || col < 0 || col >= matrix[0].length || visited[row][col] || matrix[row][col] !== value ) { return; } visited[row][col] = true; check(matrix, visited, row - 1, col, value); // 上 check(matrix, visited, row + 1, col, value); // 下 check(matrix, visited, row, col - 1, value); // 左 check(matrix, visited, row, col + 1, value); // 右 } ``` 在上述代码,我们使用了一个visited数组来记录已经遍历过的方块,避免重复遍历。然后,我们对当前方块的上下左右进行递归遍历,直到找到所有相邻的相同方块。 接下来,我们需要实现一个算法消除所有相邻的相同方块。我们可以使用一个队列来记录所有需要消除的方块。具体实现代码如下: ```javascript function remove(matrix, visited, row, col, value, queue) { if ( row < 0 || row >= matrix.length || col < 0 || col >= matrix[0].length || visited[row][col] || matrix[row][col] !== value ) { return; } visited[row][col] = true; queue.push({ row, col }); remove(matrix, visited, row - 1, col, value, queue); // 上 remove(matrix, visited, row + 1, col, value, queue); // 下 remove(matrix, visited, row, col - 1, value, queue); // 左 remove(matrix, visited, row, col + 1, value, queue); // 右 } ``` 在上述代码,我们使用了一个queue数组来记录需要消除的方块。然后,我们对当前方块的上下左右进行递归遍历,将所有相邻的相同方块加入到队列。 最后,我们可以实现一个方法来执行整个游戏的主逻辑。具体实现代码如下: ```javascript methods: { play() { const { matrix } = this; const visited = matrix.map(row => row.map(() => false)); for (let row = 0; row < matrix.length; row++) { for (let col = 0; col < matrix[0].length; col++) { if (!visited[row][col]) { const queue = []; const value = matrix[row][col]; remove(matrix, visited, row, col, value, queue); if (queue.length > 1) { for (const { row, col } of queue) { matrix[row][col] = null; } } } } } for (let col = 0; col < matrix[0].length; col++) { const stack = []; for (let row = matrix.length - 1; row >= 0; row--) { if (matrix[row][col] !== null) { stack.push(matrix[row][col]); } } for (let row = matrix.length - 1; row >= 0; row--) { if (stack.length > 0) { matrix[row][col] = stack.pop(); } else { matrix[row][col] = null; } } } } } ``` 在上述代码,我们首先遍历整个矩阵,对每个未遍历过的方块进行消除操作。如果当前方块的相邻方块数量大于1,则将所有相邻方块的值设置为null,标记需要消除。然后,我们通过一个栈来将每列的非null方块向下移动,以填补消除方块留下的空位。 最后,我们只需要在页面上渲染出所有方块,并绑定上点击事件即可。点击方块时,我们执行play方法来更新矩阵,并重新渲染页面。 完整代码如下: ```html <template> <div> <div v-for="(row, i) in matrix" :key="i" class="row"> <div v-for="(value, j) in row" :key="j" class="block" :class="{ selected: selected === `${i}-${j}` }" :style="{ backgroundColor: colors[value] }" @click="onSelect(`${i}-${j}`)" ></div> </div> </div> </template> <script> export default { data: { matrix: [ [1, 1, 2, 3, 2], [3, 2, 1, 2, 3], [1, 2, 1, 3, 2], [3, 1, 2, 1, 3], [2, 3, 1, 2, 1] ], selected: null, colors: { 1: '#f00', 2: '#0f0', 3: '#00f' } }, methods: { onSelect(key) { if (this.selected === key) { this.selected = null; } else if ( this.selected && this.canSwap(this.selected, key) ) { this.swap(this.selected, key); this.play(); this.selected = null; } else { this.selected = key; } }, canSwap(key1, key2) { const [i1, j1] = key1.split('-'); const [i2, j2] = key2.split('-'); const di = Math.abs(i1 - i2); const dj = Math.abs(j1 - j2); return (di === 0 && dj === 1) || (di === 1 && dj === 0); }, swap(key1, key2) { const [i1, j1] = key1.split('-'); const [i2, j2] = key2.split('-'); const temp = this.matrix[i1][j1]; this.matrix[i1][j1] = this.matrix[i2][j2]; this.matrix[i2][j2] = temp; }, play() { const { matrix } = this; const visited = matrix.map(row => row.map(() => false)); for (let row = 0; row < matrix.length; row++) { for (let col = 0; col < matrix[0].length; col++) { if (!visited[row][col]) { const queue = []; const value = matrix[row][col]; remove(matrix, visited, row, col, value, queue); if (queue.length > 1) { for (const { row, col } of queue) { matrix[row][col] = null; } } } } } for (let col = 0; col < matrix[0].length; col++) { const stack = []; for (let row = matrix.length - 1; row >= 0; row--) { if (matrix[row][col] !== null) { stack.push(matrix[row][col]); } } for (let row = matrix.length - 1; row >= 0; row--) { if (stack.length > 0) { matrix[row][col] = stack.pop(); } else { matrix[row][col] = null; } } } } } }; function remove(matrix, visited, row, col, value, queue) { if ( row < 0 || row >= matrix.length || col < 0 || col >= matrix[0].length || visited[row][col] || matrix[row][col] !== value ) { return; } visited[row][col] = true; queue.push({ row, col }); remove(matrix, visited, row - 1, col, value, queue); // 上 remove(matrix, visited, row + 1, col, value, queue); // 下 remove(matrix, visited, row, col - 1, value, queue); // 左 remove(matrix, visited, row, col + 1, value, queue); // 右 } </script> <style> .row { display: flex; } .block { width: 50px; height: 50px; margin: 5px; cursor: pointer; } .selected { border: 2px solid #000; } </style> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值