2048:把四个方向归一成同一个操作

滑动、合并、计分只需要写一遍,其余三个方向靠转置与反转复用。真正的难点是「每个方块一回合只能合并一次」,写错就得到不该出现的数字。

先归一,再实现

四个方向如果各写一遍滑动逻辑,就会有四份几乎相同、细节略有差异的代码。更省事也更可靠的做法是:只实现「把一行向左滑动并合并」,其余三个方向先做一次棋盘变换,调用同一段逻辑,再变换回来。

向左与向右是反转行;向上与向下是先转置再按左右处理。变换是纯数组操作,不涉及任何游戏规则,因此可以单独验证。

合并规则比滑动难

滑动的第一步是把 0 过滤掉,剩下的数字向左靠拢。真正容易写错的是合并:每个方块在一次移动里最多参与一次合并。

所以 [2, 2, 4] 必须得到 [4, 4],不能得到 [8]——第二个 2 已经被第一个吃掉了,不能再和新合成的 4 合并。同理 [2, 2, 2, 2] 是 [4, 4] 而不是 [8]。

实现上就是扫描时遇到相等的一对就合并,并把下标额外推进一格,跳过被吃掉的那一个:

if (i + 1 < nums.length && nums[i] === nums[i + 1]) {
  const merged = nums[i] * 2;
  out.push(merged);
  gained += merged;
  i += 1; // 跳过被吃掉的那一个
}

这一行 i += 1 是整个游戏最不能删的一行。删掉它,游戏照样能跑,只是在特定盘面下会冒出 8、16 这种不该存在的跳跃,而玩家很难说清是哪一步出的错。

只在棋盘真的动了之后出牌

另一个容易漏掉的规则是:如果一次滑动之后棋盘没有任何变化(比如所有方块都已经贴着那一侧),就不应该生成新方块,也不应该算作一个回合。少了这个判断,玩家按住方向键就能无限刷出方块。

新方块只在空格里选位置,且要能复现。随机数用带种子的 mulberry32,自检里固定种子后,出牌顺序就是确定的。

结束判定

没有空格、且四个方向都无法产生合并,游戏结束。这两个条件缺一不可:棋盘满了但存在相邻等值方块时,游戏仍在继续。

为什么它能被自检覆盖

所有这些规则都写在纯函数里,输入输出都是普通数组,不碰 DOM。自检针对的正是上面这些边界:[2,2,4] 必须得 [4,4]、四个 2 必须得两个 4、无效移动不能生成新方块、固定种子下出牌顺序一致。这类 bug 在界面上看起来只是「数字有点怪」,只有断言能钉住它。

← 返回文章列表

评论

…