恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Cocos Creator 中国象棋开发:规则引擎、交互手感与AI实战

  • 首页
  • 资讯中心
  • /
  • Cocos Creator 中国象棋开发:规则引擎、交互手感与AI实战

相关资讯

C++ - 变量 2026/10/14 3:57:58
SpringBoot核心知识点深度梳理:自动配置、启动流程与Bean生命周期 2026/10/14 3:57:58
基于SpringBoot+Vue+MySQL的社团管理系统设计与部署实战 2026/10/14 3:57:58

最新资讯

微信小程序云开发超市库存管理系统源码解析与二次开发指南
CentOS 8.0安装Oracle 12cR2全流程指南:兼容性处理与高频报错排查
告别AI应用廉价感:5个可落地的设计决策
数据提取完整实践复盘:从需求拆解到稳定落地全流程
长活动按声音对齐后片尾漂移,起点通过不代表整段同步
Agent-Reach:多智能体通信与路由编排基础设施实践

今日推荐

IBM Verify Identity Access 修复 22 个漏洞:两个 9.8 严重 RCE 无需认证
小区物业管理系统
小型超市仓库管理系统

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Cocos Creator 中国象棋开发:规则引擎、交互手感与AI实战

发布时间:2026/10/14 4:02:58
Cocos Creator 中国象棋开发:规则引擎、交互手感与AI实战 简介一款基于Cocos Creator开发的中国象棋单机游戏项目源码面向游戏开发初学者与算法爱好者提供从棋盘渲染、走子规则到电脑AI的完整实现。项目内置简单、普通、困难三档电脑棋力核心AI采用经典Alpha-Beta剪枝算法困难模式下已有一定实战强度也保留了继续优化空间适合用于学习博弈树搜索与棋力调参。压缩包共730个文件总大小17.89MB以json配置、png贴图、prefab预制体、js脚本和meta资源描述为主其中json负责界面与关卡数据prefab组织界面组件js承载游戏逻辑与AI实现结构清晰易于定位。伴生图片与字体资源保证界面完整可快速导入Cocos Creator运行体验。已有1283人学习这套源码可直观了解单机对弈的完整流程包括走棋反馈、AI决策、胜负判定等环节。对于想掌握Cocos Creator项目组织方式或希望改造、增强AI棋力的读者而言这份项目具备直接的参考价值。1. 中国象棋 Cocos Creator 版开发规则引擎和交互手感是两条主线用 Cocos Creator 做中国象棋很多人最开始都会低估“规则”这一层棋盘图片拖进场景十分钟就摆好了可一旦动起来马能不能往那跳、炮能不能隔子吃、象眼有没有被塞住、对面将帅能不能照面这些规则凑到一起走着走着就翻车。真正要交付一个能玩的版本核心不是画棋盘而是把 90 个交叉点、32 个棋子的走法规则、将军和将死判定、点击选择与回合流转全部用代码立起来。这篇笔记会按落地顺序拆开讲棋盘数据建模、棋子渲染、走法规则、触摸交互、避坑点最后给一个能下完一盘棋的简单电脑对手。适合想拿棋类小项目练手 Cocos Creator 的开发者也适合要在业务里嵌一套双人桌游 demo 的团队参考。2. 棋盘建模与棋子渲染先把 90 个交叉点变成可计算的坐标2.1 棋盘索引约定用 (row, col) 还是 (col, row)必须二选一中国象棋棋盘是 9 列 × 10 行共 90 个交叉点。Cocos Creator 的 2D 坐标里 x 向右、y 向上而人看棋盘的习惯是“第几行、第几列”。这两套东西夹在一起如果不先定死一套索引约定后面的走法规则和触摸判定会一直出问题。我在这类棋类项目里统一用(row, col)row 0 是最上面一排黑方棋子所在行row 9 是最下面一排红方棋子所在行col 0 到 col 8 从左到右。理由很直接车走直线、马走日字本质都是“行方向 列方向”的二维偏移用 (col, row) 写方向表时每次都要做一次心理翻译嵌套数组的下标顺序一旦写混Bug 难查到怀疑人生。棋盘数据我用一个长度为 90 的一维数组索引公式固定为row * COLS col。这个公式看起来简单但它是整个规则引擎的地基后面所有走法生成、触摸点击、AI 搜索都要靠它定位。// chess_rules.ts —— 棋盘常量与坐标换算的最小骨架 export const ROWS 10; export const COLS 9; export const CELL_SIZE 60; // 交叉点间距单位 px按美术资源调整 export type Side red | black; export type PieceKind rook | horse | elephant | guard | king | cannon | pawn; export type Piece { side: Side; kind: PieceKind }; // 棋盘数组里每个元素是一个 Piece 或 null export type Board (Piece | null)[]; export function indexOf(row: number, col: number): number { return row * COLS col; } // 把行列坐标换算成棋盘节点本地坐标 export function posToLocal(row: number, col: number): { x: number; y: number } { return { x: col * CELL_SIZE, y: -row * CELL_SIZE }; }上面代码里我默认棋盘背景图的锚点在左上角节点位置对准左上第一个交叉点。这样(0,0)就在左上角row 向下增长“row 0 是黑方底线”和视觉完全一致。Cocos Creator 的节点坐标是 y 向上的所以 y 取负。如果你要在场景里做一个带四周边距的棋盘就在posToLocal返回值里额外加 offsetX 和 offsetY但初期不要引入这东西先让最小命令跑起来。2.2 棋子预制体与初始布局一个 Prefab 装下红黑两色棋子我用一个预制体解决根节点挂PieceItem组件下面放一个显示圆形底图的 Sprite 子节点和一个显示汉字的 Label 子节点。红黑两色通过切换 SpriteFrame 或者修改 Label 的颜色实现不需要做两套预制体。预制体里真正要存的是side和kind两个字段它们是棋子在数据层的身份标识。节点位置在场景里不预设全部由代码根据行列坐标摆放避免手动拖位置造成数据和显示不一致。初始布局写成一个纯函数返回一个新的 90 格数组。红方底行从 row 9 开始车、马、象、仕、帅、仕、象、马、车炮在 row 7 的 col 1 和 col 7兵在 row 6 的 col 0、2、4、6、8。黑方对应 row 0、2、3棋子种类相同side 换成 black。这个布局是固定死的直接写在代码里比从配置表读取更适合棋类项目因为棋类规则引擎需要一个确定的初始状态来做单元测试。// 创建初始棋盘 export function createInitBoard(): Board { const board: Board new Array(ROWS * COLS).fill(null); const put (row: number, col: number, side: Side, kind: PieceKind) { board[indexOf(row, col)] { side, kind }; }; const backRank: PieceKind[] [rook, horse, elephant, guard, king, guard, elephant, horse, rook]; backRank.forEach((kind, col) { put(9, col, red, kind); // 红方底线 put(0, col, black, kind); // 黑方底线 }); put(7, 1, red, cannon); put(7, 7, red, cannon); put(2, 1, black, cannon); put(2, 7, black, cannon); for (let col 0; col COLS; col 2) { put(6, col, red, pawn); put(3, col, black, pawn); } return board; }put函数把indexOf封装起来读代码的人不用关心一维下标。红方兵放在 row 6黑方兵在 row 3中间隔着一行“河界”这正是中国象棋的典型摆法。这里有一个经验初始布局不要用复制粘贴循环里生成兵的位置能减少不少低级失误。2.3 触摸坐标换算像素点转成 (row, col) 是交互的地基棋盘摆好了下一步是处理点击。Cocos Creator 的触摸事件给到监听器的是一组 UI 坐标要判断玩家点到哪个交叉点必须先把 UI 坐标转成棋盘节点的本地坐标再除以格子间距取整。常见做法是让棋盘根节点监听触摸这样不管点的是棋子、背景还是高亮节点都统一走同一个坐标换算流程不做事件派发代码更可控。// BoardScene.ts —— 触摸坐标转换的核心代码 import { _decorator, Component, Node, UITransform, EventTouch } from cc; const uiPos touchEvent.getUILocation(); const localPos this.boardNode.getComponent(UITransform).convertToNodeSpaceAR(uiPos); const col Math.round(localPos.x / CELL_SIZE); const row Math.round(-localPos.y / CELL_SIZE); if (row 0 || row ROWS || col 0 || col COLS) return;这里有两个坑要提前说明。第一必须用棋盘节点的UITransform来做转换不能用 Canvas 的因为棋盘节点可能有缩放和位移一旦换了参考系坐标就会整体偏移。第二取整用Math.round而不是Math.floor因为棋盘中心在交叉点上点击落在两个交叉点之间的像素时离哪个近就选哪个。如果棋盘图自带边距需要把边距从 localPos 里减掉再除格子间距如果棋盘背景自带缩放适配要先用convertToNodeSpaceAR拿到本地坐标再做行列换算顺序不能反。3. 走法规则从“马走日象走田”到可验证的代码3.1 候选目标生成按棋种走方向而不是遍历棋盘规则引擎最常用的写法不是遍历棋盘上所有棋子而是给定一个棋子和它的位置生成它能走到的目标格集合。这样每次动一个棋子只需要算这一枚棋子的候选点效率高逻辑也清晰。车和炮都需要沿四个方向扫描但差别很大车遇到第一个棋子就停如果是敌方就吃掉炮则要越过第一个挡子把第二个遇到的敌方棋子当炮架吃。// generateMovesFrom —— 生成某位置棋子的全部候选目标 export function canMoveTo(board: Board, fromRow: number, fromCol: number, toRow: number, toCol: number): boolean { // 实际项目中 generating 候选点更好这里用独立函数做合法性校验 const moves generateMovesFrom(board, fromRow, fromCol); return moves.some(([r, c]) r toRow c toCol); } export function generateMovesFrom(board: Board, row: number, col: number): [number, number][] { const piece board[indexOf(row, col)]; if (!piece) return []; const own piece.side; const moves: [number, number][] []; const dirs [[-1, 0], [1, 0], [0, -1], [0, 1]]; // 车直线扫描空位可走第一个敌方棋子可吃 if (piece.kind rook) { for (const [dr, dc] of dirs) { let step 1; while (true) { const nr row dr * step; const nc col dc * step; if (nr 0 || nr ROWS || nc 0 || nc COLS) break; const target board[indexOf(nr, nc)]; if (!target) { moves.push([nr, nc]); } else { if (target.side ! own) moves.push([nr, nc]); break; } step; } } } // 炮空走路径上不能有棋子吃子必须隔一个炮架 if (piece.kind cannon) { for (const [dr, dc] of dirs) { let blocked false; let step 1; while (true) { const nr row dr * step; const nc col dc * step; if (nr 0 || nr ROWS || nc 0 || nc COLS) break; const target board[indexOf(nr, nc)]; if (!blocked) { if (!target) { moves.push([nr, nc]); } else { blocked true; // 遇到第一个挡子切换状态 } } else { if (target) { if (target.side ! own) moves.push([nr, nc]); // 第二个是敌方 break; } } step; } } } // 马、象、士、将、兵的生成逻辑接着往下加 return moves; }炮的生成逻辑是新手最容易错的地方blocked变量只有越过第一个棋子之后才置为 true在没有越过任何棋子的阶段空位都可以落子。等blocked为 true 时遇到的第一个棋子无论是谁都会挡住炮如果是敌方才能吃如果是己方则直接 break。这个“先走棋再吃子”的顺序非常重要写反了就会出现“炮隔着两个子也能吃”的怪现象。3.2 马腿、象眼、将帅照面三种特殊规则要单独处理马和象的特殊性在于“腿”。马走日字本质是先沿直线走一格再沿垂直方向斜走一格第一步所在的点就是马腿。马腿位置被任意一方棋子占住马就不能往那个方向跳。象走田字眼在起点到终点的正中间同样是被任意棋子占住就不能走。// 马腿判断马要先走一步直线再斜跨一步 export function isHorseLegBlocked(board: Board, fromRow: number, fromCol: number, toRow: number, toCol: number): boolean { const dr toRow - fromRow; const dc toCol - fromCol; let legRow fromRow; let legCol fromCol; if (Math.abs(dr) 2) { legRow fromRow dr / 2; legCol fromCol; } else if (Math.abs(dc) 2) { legRow fromRow; legCol fromCol dc / 2; } return board[indexOf(legRow, legCol)] ! null; }马腿判断的核心是找到“第一步”。如果纵向走了两格腿位就在起点和终点中间的那一行如果横向走了两格腿位就在中间那一列。象眼的判断更简单fromRow和toRow的平均值、fromCol和toCol的平均值就是眼位。这两个规则用一句注释就能说清但写错的人非常多因为在马腿方向处理上很容易把腿位算成终点旁边而不是中间点。将帅的照面规则是另一条单独处理的逻辑红帅和黑将如果处在同一列中间不能有任何棋子遮挡否则视为“照面”走成这种局面的那一步是不合法的。常见做法是在isSquareAttacked里递归处理但更稳妥的是在生成将帅走法时显式检查将帅要走到某个位置时如果目标位置与对方将帅在同一列且中间无遮挡就排除这个位置。3.3 将军过滤与将死困毙判定先模拟走棋再校验安全性候选目标生成出来之后不能直接让玩家走还要过滤掉“走了之后自己的将/帅被对方吃掉”的走法。这个过滤最可靠的做法是先把棋子移动到目标格检查自己的将帅是否处于被攻击状态如果是就把这一步从候选里删除最后再还原棋盘。export function generateLegalMoves(board: Board, row: number, col: number): [number, number][] { const piece board[indexOf(row, col)]; if (!piece) return []; const all generateMovesFrom(board, row, col); const legal: [number, number][] []; for (const [toRow, toCol] of all) { applyMove(board, row, col, toRow, toCol); // 模拟走子 if (!isInCheck(board, piece.side)) { legal.push([toRow, toCol]); // 走后安全才合法 } undoMove(board, row, col, toRow, toCol); // 还原 } return legal; }isInCheck的判定思路是找到己方将/帅的位置然后反向验证对方所有棋子是否能攻击到这个点。车看直线炮看炮架马看腿兵看相邻格再加上将帅照面这条特殊规则。这个实现谈不上高效但在双人对战这种每回合最多计算一二十个候选点的场景里完全够用没有必要一上来就做复杂的位棋盘优化。将死和困毙的判定则是对当前玩家生成全部合法走法如果当前玩家被将军但没有任何合法走法就是将死如果当前玩家没有被将军但同样没有合法走法就是困毙。判定逻辑完全复用generateLegalMoves不需要额外写搜索。唯一要注意的是将死判定发生在对方走完棋之后这时候要切换回合视角再查一次。我把这个步骤放在状态机里而不是放到规则引擎里因为规则引擎只回答“有没有合法走法”状态机负责回答“该谁动”。4. 触摸交互与回合流转从点棋到走棋的状态机设计4.1 事件绑定挂在每个棋子上还是统一挂在棋盘上触摸事件有两种绑法。第一种是给每个棋子节点单独绑TOUCH_END优点是定位准确缺点是每个棋子都要单独处理选中、取消、吃子时节点销毁带来的事件解绑问题吃掉的棋子节点一移除事件监听也会一起消失如果节点复用不当很容易出现“点空了棋却还在走”的诡异问题。第二种是把事件统一挂在棋盘根节点上通过坐标换算定位到交叉点再判断这个点上有没有棋子。我更推荐第二种因为代码路径单一不用关心节点生命周期。// BoardScene.ts —— 棋盘根节点监听触摸 onLoad() { this.boardNode.on(Node.EventType.TOUCH_END, this.onBoardTouched, this); } private onBoardTouched(e: EventTouch) { const uiPos e.getUILocation(); const localPos this.boardNode.getComponent(UITransform).convertToNodeSpaceAR(uiPos); const col Math.round(localPos.x / CELL_SIZE); const row Math.round(-localPos.y / CELL_SIZE); if (row 0 || row ROWS || col 0 || col COLS) return; this.onCellTouched(row, col); }统一监听的另一个好处是将来做“悔棋”功能时可以回放走棋记录而不需要关心节点绑定。触摸事件的坐标换算在 2.3 节已经给过这里只是把整套流程串起来。4.2 回合状态机Idle → Selected → Moving状态机是交互层的核心我习惯用三个状态加两个字段来描述turn记录当前回合是红方还是黑方selected记录当前选中的棋子位置。状态只有三种没有选中任何棋子Idle、选中一个己方棋子Selected、正在播放走子动画Moving。Moving 状态下要屏蔽点击避免玩家在动画播放时连续点多个目标导致棋盘数据错乱。private onCellTouched(row: number, col: number) { if (this.isAnimating) return; // 动画播放期间不响应 const piece getPiece(row, col); if (!this.selected) { if (piece piece.side this.turn) { this.selected { row, col }; showHighlight(row, col); } return; } const from this.selected; // 再次点击同一个棋子取消选中 if (from.row row from.col col) { this.selected null; clearHighlight(); return; } // 点击己方另一个棋子切换选中 if (piece piece.side this.turn) { this.selected { row, col }; showHighlight(row, col); return; } // 目标在合法走法内执行移动 const legal generateLegalMoves(this.board, from.row, from.col); const hit legal.some(([r, c]) r row c col); if (hit) { this.doMove(from, { row, col }); } else { this.rejectMove(); // 播放一个轻量提示比如棋子抖动 } }这里有一个交互细节点击己方另一个棋子时是先取消原选中再选中新棋子。如果直接切换而不清理高亮会出现两个高亮同时存在的情况。另外点击对方棋子时如果它不在合法目标里最简单的反馈是抖动而不是弹出文字提示因为玩家更清楚自己走的是什么棋弹出的文字反而打断思路。4.3 走子动画与吃子清理别让数据和显示脱节执行走子时顺序是先更新棋盘数据再根据数据驱动界面。数据永远是唯一真相节点位置只是数据的可视化。我见过很多人先做动画再在动画结束回调里更新数组结果快速操作时动画还没播完数组已经变了等回调回来又把旧数据写回去局面整个错乱。所以正确顺序是同步更新数组、移除被吃棋子节点、把移动棋子的节点直接瞬移到目标交叉点如果有动画只在瞬移之后加一个简单的补间。private doMove(from: {row: number; col: number}, to: {row: number; col: number}) { const captured getPiece(to.row, to.col); if (captured) this.removePieceNode(to.row, to.col); const piece getPiece(from.row, from.col); setPiece(to.row, to.col, piece); setPiece(from.row, from.col, null); const node this.pieceNodeAt(from.row, from.col); const targetPos posToLocal(to.row, to.col); node.setPosition(targetPos.x, targetPos.y); // 需要动画可以套 tween(node).to(0.18, { position: targetPos }) this.switchTurn(); }posToLocal在 2.1 节定义过它保证数据层坐标和节点坐标永远使用同一套换算。这样即便以后换一套棋盘美术资源只要格子间距不变交互层代码不用动。这一点是棋类项目里最值得遵守的习惯数据、坐标、显示三者之间的映射关系只写一次所有模块都通过函数调用而不是各算各的。4.4 回合切换与终局判断走子完成后切换turn到对方然后检查对方是否被将死或困毙。将死和困毙的判定逻辑在第 3.3 节已经实现这里只需要调用一次然后弹出一个对局结束面板。这里有个常见的坑回合切换必须在终局判定之前完成因为判定需要以对方视角生成合法走法。如果把顺序写反会出现“明明已经将死却还能再走一步”的问题。终局面板一般显示“红方胜”或“黑方胜”困毙时显示“和棋”。这个面板不需要做得复杂一个半透明遮罩加一个 Label 就够了重点是落子之后立刻得到结果而不是让玩家自己在混乱的局面里猜测。5. 中国象棋 Cocos Creator 开发避坑马腿、象眼、坐标、照面5.1 马腿和象眼规则明明“写对”了棋子还是乱穿现象黑马被自己底线的兵挡住腿却还能跳过兵去吃红车红象在象眼位置有一颗红兵却照样走田字。原因马腿和象眼的“阻挡点”是起点到终点路径上的固定中间点不是用来凑斜步的终点旁坐标。很多人改了几次方向表结果把腿位算成了“终点回退一格”这在某些朝向看起来对换一个朝向就错。更隐蔽的是马腿判断里同时用了Math.abs(dr) 2和Math.abs(dc) 2两个分支但没写else if导致两个分支同时执行腿位被第二次覆盖。解决把马腿和象眼各自抽成独立的isHorseLegBlocked和isElephantEyeBlocked函数只接收起点和终点内部返回 boolean。写完马上做四组方向测试马从左上、右上、左下、右下跳象走四个角。这一步用不了五分钟但能省掉后面调试 AI 时的半天时间。5.2 触摸坐标对不上组件缩放和锚点是头号嫌疑现象场景分辨率适配之后点击棋盘下边某个交叉点选中的却是偏上一两格的位置窗口拉伸后越偏越离谱。原因触摸监听挂在棋盘节点上但坐标转换用的却是 Canvas 的UITransform或者棋盘节点本身有 scale 缩放而你没把缩放考虑进去。还有一种是棋盘背景图带边距但代码直接用localPos.x / CELL_SIZE没有减掉边距。解决统一用棋盘节点自己的getComponent(UITransform).convertToNodeSpaceAR()做转换不要跨节点拿坐标。如果棋盘有缩放转换之后自然就是本地坐标不需要再手动除以 scale。边距问题则在配置里加offsetX、offsetY两个常量换算时先减偏移再除格子间距。5.3 将帅照面与自将误判两条最容易被遗忘的边界规则现象红方走了一步之后红帅和黑将赤裸裸地处在同一列、中间没有任何棋子系统却判定合法或者玩家主动走了一步把自己将帅送到对方炮口下系统没有拦截。原因将帅照面规则没有写进generateMovesFrom的将帅分支也没有纳入isSquareAttacked的攻击检测。自将过滤依赖generateLegalMoves里的模拟走棋但如果isInCheck没有把照面算作被攻击那自将情况就会被当成合法走法放行。解决把“同列无遮挡”作为一条独立的攻击检测规则加入isSquareAttacked找己方将/帅位置后沿四个方向扫描如果第一个遇到的棋子是对方将/帅且中间无遮挡视为被攻击。再把generateLegalMoves的过滤逻辑当成所有走法的统一出口将帅分支不额外做例外处理这样自将也能同样被拦下来。5.4 兵卒过河前后的方向差异少写一个分支就多出一个 BUG现象红兵到了河界那行开始横着走黑卒却还在按过河前规则只能前进或者反过来双方兵的横向移动判断都错。原因兵卒的规则是“过河前只能向前过河后才能横走”。判断条件写成了“如果 row 在 0 到 4 之间就允许横走”但红黑两方的过河判定方向相反红方 row 越走越小越过河界是 row4黑方 row 越走越大越过河界是 row5。只用一组固定范围判断必然有一方错。解决生成兵卒走法时分两步先按 side 计算前进方向再判断当前是否已经过河。红方过河判断用row 4黑方用row 5横移分支只在过河后追加。把过河判断封装成isCrossedRiver(piece, row)不要散落在多个棋子分支里。6. 给 AI 一个能赢棋的脑子负极大值加 Alpha-Beta 剪枝双人对战跑通之后最自然的下一步是加一个电脑对手。最可靠的方案是用负极大值搜索把局面评估成红方分数轮到黑方时取负这样红方和黑方共用同一个递归函数。评估函数不需要复杂棋子分值加一点点位置奖励就够了。我常用的分值是将 10000、车 900、炮 450、马 400、象 200、士 200、兵 100过河兵加 50。搜索深度设 3 到 4 层普通浏览器里一回合耗时大约几百毫秒已经能让非专业玩家认真起来。const PIECE_SCORE: RecordPieceKind, number { king: 10000, rook: 900, cannon: 450, horse: 400, elephant: 200, guard: 200, pawn: 100, }; function evaluate(board: Board, side: Side): number { let score 0; for (let i 0; i board.length; i) { const p board[i]; if (!p) continue; const base PIECE_SCORE[p.kind]; const row Math.floor(i / COLS); const isPawnAdvanced p.kind pawn (p.side red ? row 4 : row 5); const value base (isPawnAdvanced ? 50 : 0); score p.side side ? value : -value; } return score; } function negamax(board: Board, depth: number, alpha: number, beta: number, side: Side): number { if (depth 0) return evaluate(board, side); const moves getAllLegalMoves(board, side); if (moves.length 0) { return isInCheck(board, side) ? -100000 - depth : 0; // 将死分数优先于困毙 } let best -Infinity; for (const m of moves) { applyMove(board, m.from, m.to); const score -negamax(board, depth - 1, -beta, -alpha, opposite(side)); undoMove(board, m.from, m.to); if (score best) best score; if (best alpha) alpha best; if (alpha beta) break; } return best; }搜索里要传入当前走棋方side评估函数永远从side视角算分递归调用时取负并交换side。Alpha-Beta 剪枝的效果在深度 3 到 4 时已经足够明显不需要做置换表也不需要开局库。真正能让 AI 更耐玩的小技巧是在几个分数相近的走法里随机选一个不要每次都用严格最大步。这样 AI 不会每盘都走同一套套路对手也不容易背板。用到这一步整个中国象棋 Cocos Creator 版的框架就完整了棋盘坐标、棋子渲染、走法规则、交互状态机、避坑经验、AI 搜索六个模块各司其职。我现在的习惯是先写规则模块的纯函数再做场景 UI每加一条规则就顺手补几个断言像马腿、照面这些边界逻辑全都能在改动后一分钟内验证。希望这篇笔记能帮到你让你在把这个小而完整的棋类项目做成自己的实战作品时少走弯路。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号