HarmonyOS7应用《拼棋派对》实战:象棋合法走法、悔棋与对局结束
棋盘能点击以后,真正决定象棋体验的是规则:马不能穿过蹩腿位置,炮吃子需要炮架,挡在将帅之间的棋子也不能随意移开。《拼棋派对》把规则判断放到 GameLogic.ets,页面通过这些函数决定提示、移动和结算。
这一篇接着上一篇的中央红兵操作,讲清合法性判断的两层结构、移动历史,以及当前悔棋实际恢复哪些状态。运行与截图对应API26模拟器上的主应用1.0.19。
本文目录
先检查棋子走法
isChineseChessMoveShapeLegal 检查棋子的移动形状与阻挡。它不能独自代表完整合法性,因为某一步看起来符合棋子的走法,仍可能让己方将帅暴露在攻击线上。
| 棋子 | 几何条件 | 额外条件 |
|---|---|---|
| 车 | 同行或同列 | 路径中没有棋子 |
| 马 | 一边两格、一边一格 | 对应的马腿位置为空 |
| 相、象 | 斜走两格 | 象眼为空且不过河 |
| 仕、士 | 斜走一格 | 位于己方九宫 |
| 帅、将 | 九宫内正交一格 | 包含飞将相关判断 |
| 炮 | 同行或同列 | 移动零阻挡,吃子恰一炮架 |
| 兵、卒 | 向前一格 | 过河后可横走,不可后退 |
判断路径时只统计起点和终点之间的棋子。终点是否为敌方棋子另外判断,不能把目标本身算作炮架。马腿与象眼也应从行列差确定,避免用屏幕方向猜测。
模拟移动,检查己方将帅
isChineseChessMoveLegal 先执行形状检查,通过后复制盘面,把棋子放到目标并清空原点,再检查己方是否被将军。下面是算法流程的等价精简表示,省略了参数保护与具体函数内部实现:
if (!isChineseChessMoveShapeLegal(cells, from, to, owner)) {
return false;
}
const next = cells.slice();
next[to] = next[from];
next[from] = '';
return !isChineseChessOwnerInCheck(next, owner);
模拟盘面不会赋值到页面状态。否则玩家只是查看某枚棋子的可走位置,盘面就可能被判断函数修改。纯规则函数返回结果,页面确认合法后再提交,是防止这类副作用的关键。
当己方正在被将军时,同样的检查可以过滤掉无法解将的目标。页面提示也会变成“只能走解将的棋”。不必为被将军状态另写一套棋子走法,只需要继续验证移动后的局面。
getChineseChessLegalMoves 枚举90个目标调用完整判断,既用于显示合法落点,也参与“是否还有合法步”的检查。这样视觉提示和结束判断共享同一规则来源。
提交一步棋需要保存什么
页面 commitChineseChessMove 保存起点、终点、移动棋子、被吃棋子和行动阵营。记录被吃棋子很重要,只有from和to无法在悔棋时恢复吃子前的局面。
const move: ChineseChessMove = {
from,
to,
piece: movingPiece,
captured: capturedPiece,
player: this.chessCurrent
};
this.chessCells = nextCells;
this.chessHistory = [...this.chessHistory, move].slice(-80);
this.chessMoves += 1;
this.chessSelectedIndex = -1;
当前历史最多保留80手,因此不能把它描述成无限回退。页面展示的最近吃子也只保留一小段列表。它们服务于轻量本地对局,不等同于完整棋谱档案。
提交后先检查胜负,再决定是否切换行动方。已经结束的局面不需要再出现“轮到黑方”之类的正常回合提示;这一顺序可以避免结算文字被回合文字覆盖。
悔棋如何恢复盘面
象棋使用历史数组末尾表示最近一步。先复制历史并pop,再恢复两个位置:
const history = this.chessHistory.slice();
const last = history.pop() as ChineseChessMove;
const cells = this.chessCells.slice();
cells[last.from] = last.piece;
cells[last.to] = last.captured;
this.chessCells = cells;
this.chessHistory = history;
this.chessCurrent = last.player;
this.chessSelectedIndex = -1;
this.chessWinner = '';
this.chessMoves = Math.max(0, this.chessMoves - 1);
恢复行动方应取历史中的player,这比单纯切换红黑更直接。选中状态需要清掉,否则旧落点提示可能还留在新的盘面上。吃子展示也随最后一步撤回。
这里的回退范围有明确边界:当前实现恢复盘面、行动方、手数与相关吃子展示,不恢复双方用时,也不会删除已经写入的战绩。若产品需要比赛级别的完整回退,就需要把时间快照和战绩处理纳入同一次操作,不能仅凭盘面回来了就认为所有数据都已回滚。
区分将军、将死与认输
getChineseChessWinner 检查帅将是否仍然存在。页面还会检查下一方是否有合法走法:没有合法步且被将军,属于将死;没有合法步但未被将军,按当前实现判困毙,由走棋方获胜。
const nextOwner = this.chessCurrent === 'R' ? 'B' : 'R';
const nextInCheck = isChineseChessOwnerInCheck(nextCells, nextOwner);
const nextHasMove = hasChineseChessLegalMove(nextCells, nextOwner);
if (!nextHasMove) {
this.finishChineseChess(this.chessCurrent,
nextInCheck ? '将死' : '困毙');
return;
}
将军本身只是状态提示,仍可继续解将。认输则直接把胜方设置为对方,经过统一结束函数停止计时、播放反馈并写入战绩。当前规则没有在这里实现长将、重复局面和自然限着等完整竞赛裁定,文章中的“合法走法”应理解为本项目已实现的这些移动与自将检查。
实际操作与验收边界
先移动中央红兵58→49,界面显示黑方回合、1手;点击“悔棋”,兵回到58,49重新为空,恢复红方回合、0手。动图展示走兵后的盘面如何通过悔棋恢复。
接着在红方回合点击“认输”,显示黑方获胜;再点击“重开”,恢复初始棋子、红方先行、0手和清空的用时。认输和重开的效果如下。
本次操作验证的是正常走兵、盘面悔棋和认输重开。马腿、象眼、炮架、自陷将军与将死条件是源码分析内容,本轮没有逐项实测。扩展规则时,应把这些边界局面独立列成用例,分别检查盘面是否变化、行动方是否变化和提示是否正确。
源码入口:GameLogic.ets 的 isChineseChessMoveLegal、hasChineseChessLegalMove;Index.ets 的 commitChineseChessMove、undoChineseChess、finishChineseChess。相关开发资料见HarmonyOS官方文档。
更多推荐
所有评论(0)