拼棋派对开发实战系列封面

向左滑动一行 2、0、2、2,结果应该是 4、2、0、0。如果把刚生成的4再次参与本次合并,规则就会失真。1024玩法的核心是一次移动只合并相邻同值的一对,并在移动有效时生成新数字。

《拼棋派对》使用4×4数组表示棋盘,四个方向共用一套行处理。本篇结合方向按钮实操解释算法、分数和结束条件。环境为HarmonyOS7 API26手机模拟器、主应用1.0.19。内部函数沿用2048命名,但实际胜利目标是1024。

本文目录

一维数组与方向转换

numberTiles有16项,0表示空格。index=row*4+col。向左时从每行左端读取,向右反向读取;向上逐列从上读取,向下从下读取。每条线都转换成“朝数组开头移动”的四元素数组。

if (direction === 'left') {
  result.push(tiles[lineIndex * 4 + offset]);
} else if (direction === 'right') {
  result.push(tiles[lineIndex * 4 + (3 - offset)]);
} else if (direction === 'up') {
  result.push(tiles[offset * 4 + lineIndex]);
} else {
  result.push(tiles[(3 - offset) * 4 + lineIndex]);
}

get2048Line读取,set2048Line按同样方向写回。规则函数不需要知道屏幕上的箭头,也不用写四套合并逻辑。方向映射若读写不对称,会出现数字向错误位置回填的问题。

压缩,再成对合并

先去掉0,再从左到右比较相邻数字。相等就输出双倍值、累计分数,索引前进2;不相等输出当前值,前进1。最后补0到长度4。下面是当前项目完整行合并函数:

private merge2048Line(line: number[]): Line2048Result {
  const compact: number[] = [];
  for (let index = 0; index < line.length; index++) {
    if (line[index] > 0) {
      compact.push(line[index]);
    }
  }
  const result: number[] = [];
  let score = 0;
  let index = 0;
  while (index < compact.length) {
    if (index + 1 < compact.length && compact[index] === compact[index + 1]) {
      const value = compact[index] * 2;
      result.push(value);
      score += value;
      index += 2;
    } else {
      result.push(compact[index]);
      index += 1;
    }
  }
  while (result.length < 4) {
    result.push(0);
  }
  return { values: result, score };
}
输入输出本行得分
2、0、2、24、2、0、04
2、2、2、24、4、0、08
4、4、8、08、8、0、08
2、4、8、16不变0

新输出的数字不会被送回compact,因此4+4得到的8不会在同一轮继续和原来的8合并。把结果数组反复压缩合并直到稳定,会错误地让一次滑动连升多级。

只有有效移动才生成新块

move2048处理四条线,把得分相加,再比较新旧盘面。如果数组完全相同,只提示不能移动,不增加步数,也不生成新块。

if (!this.numberTilesChanged(this.numberTiles, next)) {
  this.pulseMiss();
  this.notice = '这个方向不能移动';
  return;
}
const withNewTile = this.add2048Tile(next,
  Math.floor(Date.now() % 100000) + this.numberMoves * 31 + gain);
this.numberTiles = withNewTile;
this.numberScore += gain;
this.numberMoves += 1;

add2048Tile只从空格中选择目标,根据seed生成2或4。当前规则是seed余数为0时生成4,否则2,属于确定性取模选择,不是统计上保证10%概率的随机采样。

移动但没有合并也可以有效。例如一个数字从中间移到边缘,盘面发生变化,步数增加并生成新块,但gain为0。这一区别解释了为什么“走一步”不一定“加分”。

分数、胜利与无路可走

numberScore累计合并产生的数字总值,numberMoves累计有效移动次数。达到最大块1024或以上时立即结束并保存胜利战绩。无空格且没有横向或纵向相等邻居时,判定无可移动并结束。

has2048Move只检查右邻居和下邻居,足以覆盖所有相邻对,不必把同一对重复检查四次。行末不会与下一行开头比较,避免数组连续索引跨行误判。

当前移动入口在numberFinished为true时会调用restart2048。这意味着结束后再按方向也可能直接开始新局,不能写成“结束后方向输入全部无效”。独立重开按钮同样恢复新局,产品文案需要与这一行为一致。

触摸和方向按钮共用入口

棋盘绑定PanGesture,根据横纵偏移判断方向;页面也有四个箭头按钮调用move2048。电脑的WASD或方向键最终进入同一移动入口。

一次手势只在结束时处理,避免拖动过程中重复合并。方向判定要忽略过小位移,并明确斜向滑动取主轴;屏幕坐标与玩法方向都应由入口转换,不放进merge2048Line。

键盘还需要焦点恢复和长按去重,本篇不展开,也不把手机按钮测试当作键盘已验证。后续专门的键盘篇会说明这些状态。

模拟器移动效果

进入数字分类的1024,重开后使用左右上下箭头移动,观察数字位置、合并分和有效步数,再重开检查状态恢复。

1024使用四个方向移动与合并,分数和步数变化后重开

新块位置依赖当时seed,所以动图中的盘面不是固定题目。正文中的行合并表用于说明纯算法,不把它说成此次模拟器每一步的盘面。

本轮验证方向操作与重开,没有玩到1024或无路可走结算。扩展回归时,行合并表适合直接检查纯函数;模拟器则检查手势、按钮、焦点与画面更新。把规则测试和设备测试分开,能更准确定位是合并错误还是输入没有生效。

源码入口:pages/Index.ets 的 move2048、get2048Line、set2048Line、merge2048Line、add2048Tile 与 has2048Move。参考HarmonyOS官方文档。下一篇回到双人玩法,讨论座位朝向与屏幕空间。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐