资讯动态

在华为云码道 CodeArts 上从零开发飞行棋小游戏

发布时间:2026/10/14 14:07:09 来源:尧图企业网站定制
[一键开通华为云码道 CodeArts 代码智能体]Developer Events_Developer Alliance-Huawei Cloud用原生 JS Canvas 从零开发飞行棋大战华为云码道 CodeArts 开发复盘 本文记录使用华为云码道CodeArts代码智能体辅助开发一款纯前端飞行棋游戏的完整过程。零框架依赖HTML5 Canvas 绘制棋盘原生 JavaScript 实现游戏逻辑与 AI 对手最终产出可直接开源的模块化项目。一、项目背景与目标1.1 为什么做飞行棋飞行棋Aeroplane Chess / Ludo是一款承载童年记忆的经典棋盘游戏。市面上虽有众多飞行棋 App但作为开发者亲手从零实现一版完整规则的游戏是对逻辑思维和工程能力的双重锻炼。本项目目标- 纯前端实现HTML CSS JS零框架零依赖浏览器打开即玩- 完整规则起飞、移动、撞击、归航、胜负判定全部覆盖- 人机对战玩家执红方3 个 AI 对手自动决策- 开源就绪模块化拆分MIT 许可证可直接推送至 GitHub/Gitee1.2 开发方式本项目由 华为云码道CodeArts代码智能体*辅助开发。从需求梳理、棋盘坐标设计、核心逻辑编写到 AI 策略实现全程人机协作完成。以下为完整复盘。二、需求分析2.1 核心规则梳理飞行棋的规则看似简单细节却不少。逐一梳理如下规则模块要点实现关键起飞掷骰得 6或 2/4/6才能将飞机移至起点takeoffRule 数组控制可起飞点数连掷掷出 6 可再掷一次consecutiveSix 计数器连掷限制连续 3 次 6 取消本轮防止无限循环移动沿外圈顺时针飞行掷几点走几步getStepsTraveled() 计算已走步数撞击落点有敌方飞机则撞回机场checkCollision() 遍历检测归航走完外圈 52 格进入 6 格归航道state 从 track 切换到 home胜利4 架飞机全到终点checkWin() 判定2.2 状态机设计游戏整体流程遵循 IDLE → PLAYING → GAMEOVER 的状态机闭环每架飞机也有独立的状态机如下图显示。2.3 起飞规则可配置考虑到不同玩家对起飞规则有不同偏好在开始界面提供了两种规则选择- 经典规则仅掷出 6 点可起飞- 简化规则掷出 2、4、6 均可起飞三、架构设计3.1 棋盘坐标设计棋盘采用 14×14 网格每格 36pxCanvas 尺寸 540×540。这是整个项目最核心的设计决策。外圈 52 格顺时针排列四方起点与归航入口方起点index起点坐标归航入口index红方47(0,5)46黄方7(7,0)6蓝方21(13,8)20绿方33(6,13)32 归航入口 (起点 - 1) mod 52归航道每方 6 格从外圈通向中心。红黄共享终点 (6,6)蓝绿共享终点 (7,7)。3.2 文件结构设计项目采用模块化拆分各文件职责单一feixingqi/ ├── index.html # 主页面HTML 结构 ├── css/ │ └── style.css # 界面样式与动画 ├── js/ │ ├── config.js # 棋盘配置数据常量、坐标 │ ├── game.js # 游戏核心逻辑状态、移动、撞击、胜负 │ ├── ai.js # AI 决策逻辑 │ ├── render.js # Canvas 绘制逻辑 │ ├── ui.js # UI 面板更新状态、日志、排名 │ └── main.js # 主入口游戏流程、事件绑定 ├── README.md # 项目说明 ├── DEVELOPMENT.md # 开发过程文档 ├── LICENSE # MIT 许可证 └── .gitignore # Git 忽略规则3.3 数据结构// 玩家 player { color: red, // 颜色标识 isAI: false, // 是否AI pieces: [piece×4] // 4架飞机 } // 飞机 piece { id: 0, // 编号 0-3 state: base, // base/track/home/finish pos: 0, // base:机场位0-3 / track:外圈位0-51 homePos: 0 // home:归航道位0-5 }四、核心代码实现4.1 棋盘配置config.js将棋盘所有坐标定义为常量实现数据与逻辑分离const TRACK [ [0,0],[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,0],[12,0],[13,0], [13,1],[13,2],[13,3],[13,4],[13,5],[13,6],[13,7],[13,8],[13,9],[13,10],[13,11],[13,12],[13,13], [12,3],[11,13],[10,13],[9,13],[8,13],[7,13],[6,13],[5,13],[4,13],[3,13],[2,13],[1,13],[0,13], [0,12],[0,11],[0,10],[0,9],[0,8],[0,7],[0,6],[0,5],[0,4],[0,3],[0,2],[0,1] ]; const START_IDX { red: 47, yellow: 7, blue: 21, green: 33 }; const HOME_PATH { red: [[1,6],[2,6],[3,6],[4,6],[5,6],[6,6]], yellow:[[6,1],[6,2],[6,3],[6,4],[6,5],[6,6]], blue: [[12,7],[11,7],[10,7],[9,7],[8,7],[7,7]], green: [[7,12],[7,11],[7,10],[7,9],[7,8],[7,7]] };4.2 已走步数计算game.js这是整个移动逻辑的基石。由于四方起点不同需要用模运算计算飞机从起点出发已走的步数function getStepsTraveled(color, trackPos) { const start START_IDX[color]; return (trackPos - start TRACK_LEN) % TRACK_LEN; }4.3 可移动判定根据飞机当前状态和骰子点数判断是否可移动function canMovePiece(player, piece, dice) { if (piece.state finish) return false; if (piece.state base) return takeoffRule.includes(dice); if (piece.state track) { const steps getStepsTraveled(player.color, piece.pos); const newSteps steps dice; if (newSteps TRACK_LEN) return true; const homePos newSteps - TRACK_LEN; return homePos HOME_LEN - 1; } if (piece.state home) { return piece.homePos dice HOME_LEN - 1; } return false; }4.4 移动与状态切换移动逻辑是状态机的核心处理 base→track、track→home、home→finish 三种切换function movePiece(player, piece, dice) { if (piece.state base) { piece.state track; piece.pos START_IDX[player.color]; return { type: takeoff }; } if (piece.state track) { const steps getStepsTraveled(player.color, piece.pos); const newSteps steps dice; if (newSteps TRACK_LEN) { piece.pos (START_IDX[player.color] newSteps) % TRACK_LEN; return { type: track }; } else { const homePos newSteps - TRACK_LEN; if (homePos HOME_LEN - 1) { piece.state finish; return { type: finish }; } else { piece.state home; piece.homePos homePos; return { type: home }; } } } // ... home 状态处理 }4.5 撞击判定移动后若飞机在外圈检查同格是否有敌方飞机有则送回机场function checkCollision(player, piece) { if (piece.state ! track) return null; const hit []; for (const other of players) { if (other.color player.color) continue; for (const op of other.pieces) { if (op.state track op.pos piece.pos) { hit.push({ player: other, piece: op }); } } } for (const h of hit) { h.piece.state base; h.piece.pos findFreeBaseSlot(h.player); } return hit.length 0 ? hit : null; }4.6 AI 决策策略AI 采用三级优先级决策树ai.jsfunction aiChoosePiece(player, pieces, dice) { // 1. 优先起飞 const takeoff pieces.filter(p p.state base); if (takeoff.length 0) return takeoff[0]; // 2. 优先撞击 for (const p of pieces) { if (p.state track) { const newPos (START_IDX[player.color] newSteps) % TRACK_LEN; for (const other of players) { if (other.color player.color) continue; if (other.pieces.some(op op.state track op.pos newPos)) { return p; } } } } // 3. 优先推进最前的飞机 const scored pieces.map(p ({ p, score: p.state track ? getStepsTraveled(player.color, p.pos) : p.state home ? TRACK_LEN p.homePos : 0 })); scored.sort((a, b) b.score - a.score); return scored[0].p; }策略说明1. 起飞优先 — 机场有飞机且可起飞时优先起飞增加场上兵力2. 撞击优先 — 移动后可撞击敌方飞机时优先选择削弱对手3. 推进优先 — 否则移动最接近终点的飞机加速获胜五、难点攻克5.1 难点一棋盘坐标映射问题14×14 网格上外圈 52 格的坐标需要手动定义且不能出错。四方起点、归航入口、归航道、机场位置都需要精确对应。解决将所有坐标在 config.js 中集中定义为常量并验证- 外圈 52 格坐标唯一无重复- 四方起点等间距分布间隔 13 格- 归航入口 (起点 - 1) mod 52- 归航道从入口向中心延伸 6 格5.2 难点二外圈到归航道的切换问题飞机走完外圈 52 格后需要进入归航道但走完的判定因起点不同而异。解决用 getStepsTraveled() 统一计算已走步数当 newSteps ≥ 52 时进入归航道homePos newSteps - 52。若 homePos 5 则到达终点。const steps getStepsTraveled(player.color, piece.pos); const newSteps steps dice; if (newSteps TRACK_LEN) { // 仍在外圈 piece.pos (START_IDX[player.color] newSteps) % TRACK_LEN; } else { // 进入归航道 const homePos newSteps - TRACK_LEN; // ... }5.3 难点三连续掷 6 的循环处理问题掷出 6 可再掷一次若一直掷 6 会无限循环。解决用 consecutiveSix 计数器连续 3 次 6 取消本轮if (diceValue 6) { consecutiveSix; if (consecutiveSix 3) { addLog(连续三次6取消本轮); consecutiveSix 0; nextTurn(); return; } // 再掷一次 }5.4 难点四Canvas 绘制层次问题中心装饰区域会覆盖归航道格子。解决调整绘制顺序先画中心背景再画归航道在上面绘制层次从底到顶1. 背景填充2. 中心装饰区域3. 外圈格子 标记4. 归航道格子 ← 在中心装饰之上5. 终点标记6. 机场区域7. 中心文字 ← 最后画确保可见8. 飞机高亮 飞机图形5.5 难点五多机同格显示问题多架飞机停在同一格时互相重叠。解决用 getOffset() 函数按圆形均匀分布偏移function getOffset(idx, total) { if (total 1) return [0, 0]; const r 6; const angle (idx / total) * Math.PI * 2 - Math.PI / 2; return [Math.cos(angle) * r, Math.sin(angle) * r]; }六、测试与验证6.1 静态验证通过 Python 脚本验证 JS 文件括号平衡# 验证所有 JS 文件的圆括号、花括号、方括号数量平衡 python -c import os base C:/Users/Admin/Desktop/feixingqi js_files [js/config.js,js/game.js,js/ai.js,js/render.js,js/ui.js,js/main.js] for jf in js_files: with open(os.path.join(base, jf), r, encodingutf-8) as f: s f.read() print(f{jf}: paren{s.count(\(\)s.count(\)\)} fbrace{s.count(\{\)s.count(\}\)} fbracket{s.count(\[\)s.count(\]\)}) 结果所有文件括号平衡 ✓6.2 逻辑验证测试场景预期结果状态机场掷非 6跳过回合✓机场掷 6起飞到起点✓机场掷 2简化规则起飞到起点✓外圈移动正确前进 N 步✓进入归航从入口转入归航道✓归航道移动向终点前进✓到达终点state 变为 finish✓撞击敌机敌机回机场✓连掷 3 次 6取消第三次✓4 架到终点游戏结束✓AI 自动决策优先起飞→撞击→推进✓6.3 浏览器实测通过本地 HTTP 服务器在浏览器中实测完整对局python -m http.server 8765 # 访问 http://localhost:8765/index.html七、效果展示7.1 开始界面开始界面提供起飞规则选择- 经典规则仅 6 点起飞- 简化规则2/4/6 均可起飞7.2 游戏界面- 左侧540×540 Canvas 棋盘14×14 网格- 右侧游戏状态、骰子、玩家列表、战报日志- 可动飞机有白色虚线高亮环- 飞机用圆形机身 三角机头 螺旋桨自绘7.3 结束界面获胜时- 庆祝图标 绿色光晕边框- 60 片彩带飘落动画- 恭喜你获胜 最终排名你排第一高亮失败时- 鼓励图标 橙红色边框- 再接再厉 显示获胜方 排名列表排名按到达终点的飞机数排序平局按总进度排序。八、总结与展望8.1 项目成果指标数据代码文件6 个 JS 1 个 CSS 1 个 HTML代码总量~18KB JS ~4KB CSS依赖零依赖纯原生实现规则覆盖起飞 / 移动 / 撞击 / 归航 / 胜负 全覆盖AI 策略三级优先决策树开源就绪MIT 许可证 README 开发文档8.2 开发体会1. 坐标设计是基石棋盘游戏的核心在于坐标系统的设计。花时间在 config.js 中把所有坐标定义清楚后续逻辑实现事半功倍。2. 状态机驱动开发无论游戏整体流程还是单架飞机的生命周期状态机都让逻辑清晰、边界明确。base → track → home → finish 四状态覆盖了所有场景。3. 数据与逻辑分离将棋盘坐标数据和游戏规则逻辑分别放在 config.js 和 game.js修改规则时不碰数据修改布局时不碰逻辑。4. AI 不需要很复杂三级优先决策树起飞→撞击→推进虽简单但已能提供有挑战性的对战体验。复杂度与效果之间需要平衡。5. CodeArts 辅助提效从需求梳理到代码生成华为云码道 CodeArts 代码智能体全程辅助。尤其在棋盘坐标计算、状态机设计等需要精确推理的部分AI 的逻辑推演能力显著提升了开发效率。8.3 后续展望- [ ] 增加音效掷骰子、移动、撞击、获胜- [ ] 支持本地多人模式2-4 人同设备对战- [ ] 增加难度选择AI 策略优化- [ ] 添加游戏回放功能- [ ] 移动端适配响应式布局 触摸操作本项目已托管到 AtomGit 公开仓库feixingqi:本项目基于华为云码道 CodeArts 开发是一款 PythonPygame 飞行棋小游戏。支持 2~4 人同屏对战、AI 托管、中央摇骰动画、逐格移动、音效与退出确认代码托管于 CodeArts 仓库经流水线自动构建测试实现云端可追溯的 DevSecOps 研发流程。 - AtomGit 版权声明本文为原创内容转载请注明出处。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑