HarmonyOS 小游戏《对战五子棋》开发第33篇 - AI异步落子与setTimeout

让AI"思考"一下再落子------异步交互的体验设计

截图如下:

AI落子的时序问题

如果AI在玩家落子后立即响应,用户体验会很奇怪------感觉不像在"对战"。通过setTimeout添加延迟,让AI有"思考"的时间。

玩家落子触发AI

typescript 复制代码
private onCellClick(row: number, col: number): void {
  if (this.result !== GameResult.PLAYING || this.aiThinking) return;
  if (this.currentPlayer !== BLACK) return;  // 只有玩家(黑方)能点击

  const success = this.engine.placePiece(row, col, BLACK);
  if (!success) return;

  this.refreshBoardData();

  if (this.result !== GameResult.PLAYING) {
    this.handleGameEnd();
    return;
  }

  // AI回合
  this.aiThinking = true;
  this.aiTimerId = setTimeout(() => {
    this.makeAIMove();
  }, 400);
}

aiThinking状态

typescript 复制代码
@State aiThinking: boolean = false;

aiThinking有双重作用:

  1. UI展示:显示"AI思考中..."和LoadingProgress动画
  2. 交互锁:AI思考时禁止玩家点击棋盘
typescript 复制代码
// 交互锁
if (this.result !== GameResult.PLAYING || this.aiThinking) return;

// UI展示
Text(this.getStatusText())  // 返回 "AI思考中..."
if (this.aiThinking) {
  LoadingProgress().width(16).height(16).color('#FA8C16')
}

// 按钮状态
Button('重新开始')
  .enabled(!this.aiThinking)  // AI思考时禁用

makeAIMove实现

typescript 复制代码
private makeAIMove(): void {
  this.aiTimerId = -1;  // 清除定时器ID
  
  if (this.result !== GameResult.PLAYING) {
    this.aiThinking = false;
    return;
  }

  // AI计算最佳落子
  const move = this.aiPlayer.getMove(this.engine.board);
  
  // 引擎执行落子
  this.engine.placePiece(move.row, move.col, WHITE);
  
  // 解除思考状态
  this.aiThinking = false;
  
  // 刷新UI
  this.refreshBoardData();

  // 检查游戏结束
  if (this.result !== GameResult.PLAYING) {
    this.handleGameEnd();
  }
}

setTimeout的作用

typescript 复制代码
this.aiTimerId = setTimeout(() => {
  this.makeAIMove();
}, 400);

400ms延迟的设计考量

  1. 用户体验:给AI"思考"的感觉
  2. 避免UI卡顿:困难模式的Minimax搜索需要200-500ms,setTimeout让搜索在下一个事件循环中执行
  3. 可取消:保存timerId用于清理

定时器清理

typescript 复制代码
aboutToDisappear(): void {
  if (this.aiTimerId !== -1) {
    clearTimeout(this.aiTimerId);
    this.aiTimerId = -1;
  }
}

private backToLevelSelect(): void {
  if (this.aiTimerId !== -1) {
    clearTimeout(this.aiTimerId);
    this.aiTimerId = -1;
  }
  this.inGame = false;
  // ...
}

清理时机

  1. 页面销毁时(aboutToDisappear)
  2. 返回选关时(backToLevelSelect)

为什么要清理? 如果不清理,定时器在页面销毁后仍会执行makeAIMove,可能访问已销毁的组件状态,导致错误。

AI落子的完整流程

复制代码
玩家点击落子
    ↓
engine.placePiece(玩家黑棋)
    ↓
游戏结束? → 是 → handleGameEnd()
    ↓ 否
aiThinking = true
    ↓
setTimeout(400ms)
    ↓
makeAIMove()
    ↓
aiPlayer.getMove(board) → 计算最佳位置
    ↓
engine.placePiece(AI白棋)
    ↓
aiThinking = false
    ↓
refreshBoardData()
    ↓
游戏结束? → 是 → handleGameEnd()
    ↓ 否
等待玩家下一手

状态文字的变化

typescript 复制代码
private getStatusText(): string {
  if (this.result === GameResult.BLACK_WIN) return '你赢了!';
  if (this.result === GameResult.WHITE_WIN) return 'AI获胜!';
  if (this.result === GameResult.DRAW) return '平局!';
  if (this.aiThinking) return 'AI思考中...';
  return '你的回合';
}

状态文字随游戏进程动态变化:

复制代码
你的回合 → AI思考中... → 你的回合 → ... → 你赢了!/AI获胜!

LoadingProgress组件

typescript 复制代码
if (this.aiThinking) {
  LoadingProgress()
    .width(16)
    .height(16)
    .margin({ left: 8 })
    .color('#FA8C16')  // 橙色加载圈
}

LoadingProgress是ArkUI内置的加载动画组件------一个旋转的圆圈,在AI思考时显示。

总结

AI异步落子的设计要点:

  1. setTimeout延迟:400ms模拟AI"思考"
  2. aiThinking状态:UI展示+交互锁双重作用
  3. LoadingProgress:内置加载动画
  4. 定时器清理:页面销毁/返回时清除
  5. 流程清晰:玩家落子→延迟→AI落子→刷新

这种异步设计让对战体验自然流畅,避免了"AI瞬间响应"的违和感。

相关推荐
OceanBase数据库官方博客2 小时前
OceanBase DataPilot AIP:Ontology 承载AI能力面的另一条路
人工智能·oceanbase
笨鸟先飞,勤能补拙3 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
一次旅行4 小时前
AI 前沿日报 | 2026年07月31日
人工智能
2601_963749104 小时前
标题:越华环保集团|面向美丽河湖项目的数字化污水治理云边协同采集架构设计
人工智能
沐籽李4 小时前
从溶剂可及表面积SASA理解抗体结构与工程改造
人工智能·药物设计·aidd·sasa
智慧物业老杨4 小时前
物业如何做好预算管理?落地架构逻辑
人工智能·架构
微学AI4 小时前
一根针指向所有方向:挂谷猜想对 LLM Agent 技能-记忆架构的启示
开发语言·人工智能·架构·挂谷猜想
城管不管4 小时前
ReAct、Plan-and-Execute、Reflection 三大智能 Agent 范式核心区别
java·人工智能·算法·spring·ai·动态规划
boppu4 小时前
布草特殊污渍去渍剂的种类及作用
大数据·人工智能
AIsoft_86884 小时前
会议录音转文字与AI纪要工具推荐:免费额度与核心功能对比指南
人工智能