HarmonyOS 数独:ArkTS 实现 9×9 棋盘实时行列宫冲突校验
今天在华为云鸿蒙开发实践中心看了一个纯端侧数独小游戏案例:9×9 棋盘、54 道分级题库、填数实时校验、通关进度本地保存,全程不联网。把其中最核心的“实时冲突校验”梳理一下,这也是写一个能玩的数独最先要解决的问题。
1. 棋盘用什么数据结构
用一个 9×9 的二维数组存当前棋盘,0 表示空格;再用一个集合记录冲突格,键写成 “行,列” 的形式:
@State board: number[][] = [] // 9×9,0 = 空格
@State conflictCells: Set<string> = new Set() // 冲突格 "row,col"
@State 是 ArkTS 的状态装饰器,数据一变,声明式 UI 自动重渲染,不用手动刷新界面。棋盘、数字面板、进度卡片都可以拆成 @Builder 构建函数或子组件,用 @Prop 把状态往下传。
2. 冲突校验:只查三处
每次往 (row, col) 填入数字 num,依次查三处:同一行、同一列、所在的 3×3 宫。宫的定位是关键一行代码:
const boxRow = Math.floor(row / 3) * 3
const boxCol = Math.floor(col / 3) * 3
行号 0/1/2 映射到 0,3/4/5 映射到 3,6/7/8 映射到 6,列同理;再从 (boxRow, boxCol) 扫描 9 格即可。行、列、宫都只要线性扫一遍,9×9 的规模下成本可以忽略,完全可以做到每次填数都实时算。
3. 填数时的联动
填数不是只校验当前格,要把结果写回 UI 状态:
function onCellInput(row: number, col: number, num: number) {
board[row][col] = num
if (checkConflict(row, col, num)) {
conflictCells.add(`${row},${col}`) // 标红
} else {
conflictCells.delete(`${row},${col}`) // 清除标记
}
}
注意一个细节:改掉一个格子可能让原来冲突的格子恢复正常,所以更稳妥的做法是每次填数后全盘重算冲突集合,而不是只处理当前格。棋盘只有 81 格,全盘重算的开销完全可以接受,逻辑也更不容易漏。
4. 题库:确定性生成
案例的 54 道题不是手写死数据,而是由一道基准解经确定性变换(mulberry32 种子随机数)生成,同一输入得到同一题库,方便复现和测试。需要说明的是:题目由合法解盲挖空格得到,没有做唯一性校验,同一题可能存在多套合法终盘;通关判定只校验“填满且无行列宫冲突”,不与内置答案逐格比对。
5. 进度持久化:直接读文件
通关进度只存在应用沙箱的 sudoku_progress.json(filesDir 目录)里,用 @ohos.file.fs 读写,卸载即清除。案例里特意选了“每次直接读文件”的方式,而不是依赖跨页面缓存的值,目的是规避“返回首页读到旧进度”的缓存一致性问题——首页返回时重新读一遍文件,进度计数自然就对了。
小结
一个能玩的端侧数独,核心就四件事:二维数组存盘、@State 驱动刷新、行列宫三重实时校验、文件直读做持久化。验证环境是 DevEco Studio,compatibleSdk 6.1.0(23),compileSdk 与 targetSdk 6.1.1(24)。这套“棋盘渲染 + 实时校验 + 本地存档”的组合,照搬到扫雷、华容道、记忆翻牌这类游戏同样成立。
- 点赞
- 收藏
- 关注作者
评论(0)