HarmonyOS7应用拼棋派对开发实战系列统一封面

上一篇完成了两张牌的合并。真实答题中,玩家还会算错一步、改变思路、使用提示,或者完成一轮后重新开始。这些操作决定24点是否适合持续练习。

这一篇从一次具体撤销开始:把48除以2得到24之后,点击撤销,页面应该回到48与2,并恢复刚才的选牌和除法状态。只恢复数字数组还不够,等待执行的操作也属于历史。

本文目录

撤销记录需要保存什么

项目中的历史模型位于 Index.ets:

interface TwentyFourHistoryState {
  slots: TwentyFourSlot[];
  selected: number;
  operator: string;
}

牌槽保存数值、表达式与可用状态,selected 保存左操作数,operator 保存待执行运算。三者一起描述一次操作前的局面。

例如“选择48、选择除号、选择2”的最后一步执行前,快照包含48和2、已选中的48,以及除号。撤销到这个时间点,玩家就可以直接重新选择右操作数。

pushTwentyFourHistory 先克隆牌槽,再序列化快照;单人模式将历史限制在12项以内:

const state: TwentyFourHistoryState = {
  slots: cloneTwentyFourSlots(slots),
  selected,
  operator
};
const encoded = JSON.stringify(state);
this.single24History = [...this.single24History, encoded].slice(-12);

这是原函数中的单人分支节选。克隆避免后续修改影响旧快照,序列化进一步固定了历史内容,限制长度则避免连续操作导致数组无界增长。

项目也允许未选运算符时交换牌位,因此历史不仅包含运算,还包含换位。讲撤销时需要覆盖所有会改变牌局的动作。

恢复数据与刷新牌面

单人撤销的完整处理如下:

private undoSingle24(): void {
  if (this.single24History.length === 0) {
    this.notice = '暂无可撤销步骤';
    return;
  }
  const last = this.single24History[this.single24History.length - 1];
  const state = JSON.parse(last) as TwentyFourHistoryState;
  this.single24Slots = cloneTwentyFourSlots(state.slots);
  this.refreshTwentyFourSlotsSnapshot('single', 'A', this.single24Slots);
  this.single24Selected = state.selected;
  this.single24PendingOp = state.operator;
  this.single24History = this.single24History.slice(0, this.single24History.length - 1);
  this.twentyFourRenderVersion += 1;
  this.notice = '已撤销一步';
}

先处理空历史,再恢复牌槽和操作状态,移除已经使用的快照,并刷新显示。refreshTwentyFourSlotsSnapshot 会同步各牌显示字段和表达式,保证恢复后的数字、牌面文字与可用数量一致。

撤销24恢复48与2,再次计算并提交下一题

得到24→撤销恢复48与2→再次得到24→提交。撤销时,左操作数和除法高亮一起恢复。

当前点击流程在尝试计算前保存历史,因此除零失败也可能留下未改变局面的快照。如果后续希望撤销只覆盖有效改动,可以在计算成功后提交历史,同时为换位保留单独的入栈位置。这个改进涉及事件顺序,应配合除零与连续撤销验证。

提示次数属于整轮训练

提示读取当前题目的 hint 与 solution:

private showSingle24Hint(): void {
  const question = this.currentSingle24Question();
  if (this.single24Hints <= 0) {
    this.notice = `参考:${question.solution}`;
    return;
  }
  this.single24Hints -= 1;
  this.notice = `${question.hint} · 剩余${this.single24Hints}`;
}

新一轮开始时有3次提示。进入下一题不会重新补充次数,提示预算属于整轮训练。

次数耗尽后,当前实现会显示参考解法。因此“剩余0”表示步骤提示已经用完,而不是按钮彻底失效。这种规则需要让界面文案与实际行为一致,避免读者按代码误写成“提示耗尽后禁止使用”。

点击提示后显示题目思路,剩余提示从3变成2

展开屏中,数字牌与工具分区排列。点击提示后,表达式区显示题目思路,提示数减少到2。

提示可以告诉玩家如何拆解题目,例如两个7先做49,再减1。它保留思考空间,又能在困难时给出下一步方向。

提交后怎样进入下一题

单人训练每轮5题。答对后增加分数,若尚未到第五题,就更新轮次并加载新题:

const nextRound = this.single24Round + 1;
this.single24Round = nextRound;
this.loadSingle24Question(nextRound);
this.notice = `正确,进入第 ${this.single24Round}/5 题`;

加载新题时,重新创建牌槽、清空选择和运算符,并把撤销历史设为空。这样上一题的操作不会被撤销进新题。

计时、累计答对数与剩余提示保持整轮含义。把所有状态都放到“加载题目”里重置,会让用户每题重新得到3次提示,也会丢失累计成绩。

提交正确后进入第二题,累计答对数为1

新题已经刷新,答对数仍然保存为1。

完成一轮再重新开始

第五题答对后,finishSingle24 停止训练计时与游戏音乐,清理选中状态,生成战绩。结果记录包含难度、5题成绩和用时。

完成五题24点训练,查看成绩并再来一轮

五题依次答对,累计成绩推进到5分;训练结束后点击再来,题号、得分和提示预算恢复为新一轮状态。

重新开始则通过 restartSingle24 统一恢复轮次1、得分0、提示3和用时0,并载入第一题。清空当前题与重新开始整轮是两个操作:清空只恢复当前题数字,保留整轮成绩和题号;结束后的重开才重置整轮。

操作牌面与历史轮次与成绩提示预算
撤销恢复上一步保持保持
清空本题恢复当前题、清空历史保持保持
答对下一题新题、清空历史轮次推进、分数增加保持
再来一轮第一题、清空历史回到1、得分0恢复3

在模拟器里,先检查撤销能否回到完整操作状态,再检查跨题后的历史是否清空,最后走完5题查看结果与重开。这样验证的是同一个状态模型在不同阶段是否仍然一致。

后续扩展双人模式时,历史、选择和提示类状态需要明确归属。下一篇会继续区分双方独立牌局与共同题号,防止一个人的操作影响另一个人。

相关源码:pages/Index.ets 的 pushTwentyFourHistory、undoSingle24、showSingle24Hint、loadSingle24Question、restartSingle24 与 finishSingle24。

Logo

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

更多推荐