2048合并逻辑拆解:如何避免同一方块连续合并

举报
yd_251117598 发表于 2026/10/25 20:46:31 2026/10/25
【摘要】 通过独立的JavaScript纯函数拆解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辅助整理并校对,不包含本机文件、公司内部资料或任何凭据。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。