2048合并逻辑拆解:如何避免同一方块连续合并
前两篇实践记录讨论了2048小游戏的部署与作品馆发布。这篇把注意力放回游戏本身:相同数字连续出现时,怎样保证一次移动的合并结果正确?本文提供一个独立的JavaScript行处理函数,并推导边界用例,方便在浏览器小游戏中复用。示例聚焦规则,不依赖DOM或框架。
一、先明确“一次移动”的含义
以向左移动为例,先忽略空格,再按从左到右的顺序配对。相邻的两个相同数字可以合并;本次新生成的方块不能在同一次移动中继续参与合并。于是,[2, 2, 4, 0]向左后是[4, 4, 0, 0],而不是[8, 0, 0, 0]。
这个约束决定了实现方式:扫描输入数字时,每次合并都消费两个输入;生成的结果写进新的数组。让输入和输出分开,可以避免把刚合成的4再次拿去配对。
二、把一行处理成纯函数
约定输入是固定长度的一行,0代表空格,其余值是有效方块。函数返回新行、这次新增的分数,以及这一行是否发生了变化;不修改传入数组。
function mergeLeft(line) {
const values = line.filter(value => value !== 0);
const next = [];
let gained = 0;
for (let i = 0; i < values.length; i++) {
if (i + 1 < values.length && values[i] === values[i + 1]) {
const merged = values[i] * 2;
next.push(merged);
gained += merged;
i++; // 两个输入方块都已消费
} else {
next.push(values[i]);
}
}
while (next.length < line.length) next.push(0);
const moved = next.some((value, index) => value !== line[index]);
return { line: next, gained, moved };
}
第一步filter只保留非零方块。例如[2, 0, 2, 4]变成[2, 2, 4]。第二步扫描values,遇到相等的一对就向next写入两倍数值,并额外推进一次下标。第三步在末尾补零,使结果长度与原行一致。
gained只累计合并生成的数值。例如两个2合成4,新增分数是4;一行发生两次这样的合并,新增分数就是8。仅仅挪动方块不会得分。
三、用边界用例检查规则
下面是按函数逻辑推导的预期结果,可作为后续单元测试的输入和断言:
| 输入 | 向左后的新行 | 新增分数 | 发生变化 |
|---|---|---|---|
| [2, 2, 2, 2] | [4, 4, 0, 0] | 8 | true |
| [2, 2, 4, 0] | [4, 4, 0, 0] | 4 | true |
| [4, 0, 4, 4] | [8, 4, 0, 0] | 8 | true |
| [2, 4, 8, 16] | [2, 4, 8, 16] | 0 | false |
| [0, 0, 0, 0] | [0, 0, 0, 0] | 0 | false |
四个2的用例尤其有用:结果应当是两个4。它同时检查了相邻配对、扫描顺序,以及新方块不能再次合并这三个条件。空行和无法移动的一行,则用来检查moved是否误报。
四、四个方向共用同一条规则
向右时,可以复制并反转原行,调用mergeLeft,再反转返回的新行。上下移动则把棋盘的列按移动方向读成一维数组,处理后写回对应位置。反转前必须复制数组,避免reverse原地修改游戏当前状态。
一张4×4棋盘需要处理四条线。整盘是否移动,应取四条线moved的逻辑或;整盘新增分数,应取四条线gained的和。方向适配只负责坐标映射,配对规则始终由同一个函数处理。
五、合并、随机生成和撤销要分清时机
完成整盘移动后,只有棋盘确实变化,才在空格中生成一个新方块。若按下方向键后棋盘原样不动,就不应生成方块或累计分数。建议先计算完整的新棋盘,再决定是否更新状态和触发随机生成,最后统一渲染。
撤销所需的快照,应当在一次有效移动之前保存,包括棋盘每一行的副本和当前分数。仅复制外层数组会共享内部的行,后续修改可能污染历史快照。最高分是否跟随撤销回退,可以按产品规则单独决定,不要与当前分数混用。
恢复棋盘和分数,可以实现普通的一步撤销;若还希望撤销后再次操作得到完全一致的随机路线,就需要另外保存随机数生成器状态,或记录随机事件。它属于回放能力,需要比普通撤销更完整的状态设计。
六、让输入与界面围绕状态工作
方向键、WASD和触摸手势都可以映射到同一个移动入口。键盘处理应避开正在输入的文本框,并在实际接管游戏方向键时阻止页面滚动。合并计算完成后再更新分数与方块界面,可以减少动画过程中读取到半更新状态的机会。
这个例子的重点是把规则收敛到小而明确的函数:输入一行,输出一行和分数变化。部署与预览解决的是“能访问”,这些边界用例解决的是“行为正确”。两部分都核对过,小游戏的验收结果才更容易复现。
相关实践作品:2048云端单文件小游戏。本文代码是用于讲解的独立示例,表格列出的是逻辑推导结果,未将其表述为自动测试执行回执。本文由AI辅助整理并校对,不包含本机文件、公司内部资料或任何凭据。
- 点赞
- 收藏
- 关注作者
评论(0)