HarmonyOS7应用《拼棋派对》实战:1024数字移动与合并算法
向左滑动一行 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、2 | 4、2、0、0 | 4 |
| 2、2、2、2 | 4、4、0、0 | 8 |
| 4、4、8、0 | 8、8、0、0 | 8 |
| 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,重开后使用左右上下箭头移动,观察数字位置、合并分和有效步数,再重开检查状态恢复。
新块位置依赖当时seed,所以动图中的盘面不是固定题目。正文中的行合并表用于说明纯算法,不把它说成此次模拟器每一步的盘面。
本轮验证方向操作与重开,没有玩到1024或无路可走结算。扩展回归时,行合并表适合直接检查纯函数;模拟器则检查手势、按钮、焦点与画面更新。把规则测试和设备测试分开,能更准确定位是合并错误还是输入没有生效。
源码入口:pages/Index.ets 的 move2048、get2048Line、set2048Line、merge2048Line、add2048Tile 与 has2048Move。参考HarmonyOS官方文档。下一篇回到双人玩法,讨论座位朝向与屏幕空间。
更多推荐

所有评论(0)