尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

中国象棋联机微信小程序源码实战:规则引擎与WebSocket架构解析

发布时间:2026/9/26 15:55:03

资讯中心
01
ARTICLE

中国象棋联机微信小程序源码实战:规则引擎与WebSocket架构解析

中国象棋联机微信小程序源码实战:规则引擎与WebSocket架构解析
简介这是一份基于微信小程序的中国象棋联机对战完整源码主要面向小程序开发者和棋类游戏初学者演示如何通过局域网实现双人对战。项目可在微信开发者工具中直接打开编译运行包含单机游戏与联机对战的完整逻辑并配有作者撰写的两篇详解文章便于对照学习。压缩包内共38个文件以js逻辑脚本、json配置、wxss样式和wxml页面结构为主另附md说明文档整体仅27KB结构紧凑。目前已有1702人学习下载适合想快速上手微信小程序游戏开发、理解局域网通信与多人对战实现的读者。通过该项目可以了解棋盘绘制、走棋规则、扫码加入房间、局域网数据传输等关键环节并在此基础上扩展出棋力或联网功能。1. 中国象棋联机微信小程序源码先看清三件事再动手搜「中国象棋-联机游戏-微信小程序源码」的人大多是想找一个能跑通前后端的完整项目要么交课程设计要么给产品做原型验证。但把源码拿到手你会发现真正的难点不在「象棋」而在「联机」和「小程序」这两个词上。一套能用的源码通常包含三个层棋盘规则引擎走法、将军、胜负判定、实时通信服务房间、状态同步、断线重连、微信小程序前端棋盘渲染、触摸操作、登录鉴权。本文按这个顺序把每一层怎么做、参数怎么调、坑在哪讲清楚适合正在做微信小程序项目实例的开发者也适合拿 Node 或 Java 课程设计案例源码做参考的人。别急着下载就跑先搞清楚你要改哪里。2. 把中国象棋规则引擎做对二维棋盘、走法生成与胜负判定闭环2.1 棋盘怎么存二维数组编码与「可读性优先」的取舍中国象棋棋盘是 10 行 9 列最直接的表示就是二维数组。常见做法是每个位置放一个整数0 表示空正数表示红方棋子负数表示黑方棋子。这个编码在调试和打印时特别直观服务端校验和前端本地预览用同一份代码出 bug 也容易排查比上来就用 bitboard 位棋盘实际得多。// 10x9 棋盘红方在下第 9 行是红方底线黑方在上 const START_BOARD [ [-1,-2,-3,-4,-5,-4,-3,-2,-1], // 第 0 行黑方车马相仕将士相马车 [ 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0,-6, 0, 0, 0, 0, 0,-6, 0], // 黑方炮 [-7, 0,-7, 0,-7, 0,-7, 0,-7], // 黑方卒 [ 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 7, 0, 7, 0, 7, 0, 7, 0, 7], // 红方兵 [ 0, 6, 0, 0, 0, 0, 0, 6, 0], // 红方炮 [ 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 1, 2, 3, 4, 5, 4, 3, 2, 1], // 红方车马相仕帅仕相马车 ];编码上把双方棋子取相反数判断颜色时piece 0就是红方piece 0就是黑方写走法生成时不用再查表。帅用 5、将用 -5将军判断时直接找 ±5。之所以不把棋子编码成字符串是因为整数做数组下标和乘法判断正负更省事服务端并发校验时性能也更稳。提示有人用 bitboard 表示棋盘查询快、格调高但联机小程序服务端并发量不大二维数组的可读性能省下大量调试时间。除非你打算把引擎做成高性能库否则别在这里过度设计。2.2 走法生成先给每类棋子写伪走法再统一过滤「送将」走法生成是规则引擎的核心。我的做法是把每类棋子的走法写成独立函数返回候选坐标数组之后统一走一遍「走子 → 判断己方帅/将是否被攻击」的过滤。这样比每一步都单独判断规则更不容易漏尤其是马的蹩马腿和炮的翻山这类特殊逻辑。function rookMoves(board, r, c) { const moves []; const dirs [[1,0],[-1,0],[0,1],[0,-1]]; for (const [dr, dc] of dirs) { let nr r dr, nc c dc; while (nr 0 nr 10 nc 0 nc 9 board[nr][nc] 0) { moves.push([nr, nc]); nr dr; nc dc; } if (nr 0 nr 10 nc 0 nc 9 board[nr][nc] * board[r][c] 0) { moves.push([nr, nc]); // 吃子只能吃到对方棋子 } } return moves; }注意board[nr][nc] * board[r][c] 0这个写法它同时判断了「目标格是对方的子」和「目标格不是空」。如果同色就直接跳过因为车不能跳子。炮的走法和吃法要分开移动时走直线空位吃子时必须隔一个炮架。这部分最容易写错炮的「翻山」逻辑建议单独写cannonMoves再准备几个典型局面做单测。马的蹩马腿则要检查马前进方向上的「马腿位」是否有子相/象的塞象眼同理。2.3 将军、困毙、长将与三次重复胜负判定要闭环伪走法生成完后统一做合法性过滤核心是「走完不能送将」function isLegalMove(board, from, to, color) { // 先复制棋盘执行走子再检查己方帅/将是否安全 const next board.map(row row.slice()); next[to[0]][to[1]] next[from[0]][from[1]]; next[from[0]][from[1]] 0; const king color 0 ? 5 : -5; return !isKingAttacked(next, king, -color); }isKingAttacked的实现是遍历对方所有棋子看它们能否攻击到帅/将的位置。这里有个隐藏规则将帅不能直接照面如果同列且中间无子先被「飞将」的一方等效于被攻击。这个判断一定要写进isKingAttacked不然后续会出现「帅对脸还合法」的翻车局面。胜负判定围绕「当前玩家没有合法走法」来算有合法走法但被将军是被将死没有合法走法且没被将军是困毙象棋里算对方胜。在此基础上加上将军时的「应将」提示这套引擎已经能支撑本地双人对弈。长将和三次重复局面属于进阶规则联机模式至少要把「长将判负」做成可配置项不然会遇到无赖用户反复将军拖时间。3. 联机对战核心WebSocket 房间、协议设计与服务端权威校验3.1 选型为什么小程序实时联机优先选 WebSocket 而不是云数据库微信小程序做实时联机市面上有三条路原生 WebSocket 自建服务、微信云开发的实时数据推送watch、第三方即时通信 SDK。棋类对弈对消息时序要求很高每一步必须串行处理「后到的落子覆盖先到的」这种错误不可接受。云开发的watch适合聊天和文档协作但它的推送节奏和排序规则不适合当对弈总线第三方 SDK 功能全但会增大源码体积还要额外学习封装。常见做法是自建一个 WebSocket 房间服务Node.js 或 Java 都行前端用wss://接入。房间维度做状态隔离每个房间一个状态对象// room 结构字段是联机对弈的公共约定 const room { id: room_1234, players: { red: { openid: u1, conn: ws }, black: { openid: u2, conn: ws } }, board: START_BOARD, // 当前棋盘 turn: 1, // 1 红先-1 黑方 step: 0, // 步数序号客户端重试去重用 undoReq: null, // 悔棋请求的发起方角色 status: playing, // playing / finished };我把conn和openid分开存是为了断线重连时只用openid找回玩家身份conn可以直接替换成新连接。如果你把连接对象和身份信息绑死在同一个字段里重连逻辑会非常难写。3.2 消息协议把「下子、悔棋、认输」定义成一张表联机消息用 JSON 文本帧type区分消息类型data携带业务数据。移动消息必须带step序号服务端发现序号小于等于已处理序号就丢弃这是防重复提交的关键。type发送方data 关键字段说明create_room客户端——创建房间返回 roomIdjoin_room客户端roomId加入房间匹配对手move客户端from, to, step上报落子服务端校验后广播undo_ask客户端——发起悔棋请求undo_ack客户端agree对方同意或拒绝resign客户端——认输chat客户端text对局内文字消息sync服务端board, turn, step, status全量状态推送断线重连用这里有个细节悔棋不要直接生效必须走「发起 → 对方确认 → 双方同步」的流程否则手滑点错就能单方面悔棋对局公平性没法保证。超时未确认的悔棋请求我一般设 30 秒自动拒绝。3.3 服务端代码骨架收到 move 后先校验再广播ws.on(message, (raw) { const msg JSON.parse(raw); if (msg.type move) { const p room.players[msg.data.openid]; // 校验轮次当前该谁走 if ((room.turn 0 p.role ! red) || (room.turn 0 p.role ! black)) { return ws.send(JSON.stringify({ type: error, code: NOT_YOUR_TURN })); } // 校验 step 序号丢弃重复落子 if (msg.data.step room.step) return; // 服务端持有权威棋盘用规则引擎校验走法 if (!isLegalMove(room.board, msg.data.from, msg.data.to, room.turn)) return; doMove(room, msg.data); room.step msg.data.step; broadcast(room, { type: sync, data: snapshot(room) }); } });这段代码的逻辑说明服务端把第 2 章的规则引擎复制一份以服务端棋盘为准客户端上报的只是「意图」。step序号由客户端生成从 1 开始递增服务端只接受更大的序号。这样即使客户端网络重试导致同一帧发两次服务端也只会执行一次。snapshot(room)负责把棋盘、轮次、步序序列化成可传输的对象不要在广播时直接把 room 对象JSON.stringify那会带上conn连接对象导致循环引用报错。注意千万不要只在客户端做合法性判断然后直接把结果转发给对手。小程序代码很容易被逆向把规则引擎放服务端是联机公平性的底线也方便后面扩展观战和回放。3.4 断线重连心跳保活 房间保留窗口小程序切后台WebSocket 会被系统挂起甚至断开这是联机象棋最影响体验的问题。做法是服务端每 30 秒向客户端发一个 ping客户端回 pong连续 3 次没回认为该连接死掉把玩家标记为「离线」但保留房间 60 秒。玩家回到前台后小程序用roomId openid token重新连接服务端比对身份后把players里的conn换成新连接并推送一次全量sync。心跳间隔和保留窗口这两个参数要配套心跳 30 秒、三次超时约 90 秒判定离线保留窗口 60 秒意味着玩家有 1 分钟时间切回来重连。窗口太短切出去回个消息房间就没了窗口太长占着服务端内存不释放。4. 微信小程序前端落地canvas 棋盘、触摸落子与请求封装4.1 渲染选型canvas type 2d 更适合象棋view 布局适合课程设计先确认你手上的源码是小程序还是小游戏小游戏走game.js和开放数据域渲染方式完全不同。本文按小程序应用讲用的是小程序里的 canvas 组件。小程序里画棋盘有两种方案用 view 拼格子或者用 canvas 画线画棋子。view 方案布局直观、适合交作业但棋子拖拽和屏幕适配都要自己算iPhone 和 Android 的 rpx 表现也不一致canvas type2d是 Canvas 2D 接口画 9 条竖线、10 条横线、河界和九宫斜线只需一次绘制后续重绘成本也低。const query wx.createSelectorQuery(); query.select(#chessCanvas).fields({ node: true, size: true }).exec((res) { const canvas res[0].node; const dpr wx.getSystemInfoSync().pixelRatio; canvas.width res[0].width * dpr; canvas.height res[0].height * dpr; const ctx canvas.getContext(2d); ctx.scale(dpr, dpr); drawBoard(ctx, cellSize); });ctx.scale(dpr, dpr)必须写在所有绘制之前否则画出来的坐标和触摸坐标对不上。棋盘格大小我按cell res[0].width / 8计算交叉点坐标固定为[col * cell, row * cell]。如果页面用了自定义导航栏棋盘区域高度要用状态栏高度 胶囊按钮高度动态算别写死 375不然不同机型的棋盘会被顶出屏幕或压扁。如果你计划同一套代码后续发 App 或鸿蒙端可以评估用 uniapp 重写渲染层但联机核心逻辑在服务端迁移成本主要在 UI 这一层。4.2 登录与请求封装wx.login 换 openid再换业务 token微信小程序登录的标准链路是wx.login拿 code发给自己的服务端服务端调jscode2session换 openid再签一个业务 token 返回。把请求封装成 Promise统一带 token 和错误处理后面所有联机请求都走这一层。function request(url, data) { return new Promise((resolve, reject) { wx.request({ url: baseUrl url, method: POST, data, header: { Authorization: Bearer getToken() }, success: (res) { if (res.data res.data.code 0) resolve(res.data.data); else reject(new Error(res.data res.data.msg)); }, fail: reject, }); }); }这个封装把错误处理集中到了reject业务代码里不用每个接口都写一遍res.data.code 0的判断。注意小程序 WebSocket 的wx.connectSocket的 header 带不了自定义字段所以 WebSocket 连接时 token 一般放在 query 参数里wss://your.domain/ws?tokenxxxroomIdxxx。服务端校验 query 里的 token 即可。4.3 触摸落子touchstart 记起点touchend 吸附交叉点不要用touchmove实时移动棋子那会让 canvas 每帧重绘低端机上明显卡顿。我一般touchstart记录起点交叉点touchend计算终点交叉点两次都合法才发 move 消息。canvas.addEventListener(touchstart, (e) { const p toCell(e.touches[0].x, e.touches[0].y); const piece board[p.r][p.c]; if (piece * myColor 0) selected p; // 选自己的子 }); canvas.addEventListener(touchend, (e) { if (!selected) return; const p toCell(e.changedTouches[0].x, e.changedTouches[0].y); if (isLegalMove(board, selected, p, myColor)) { sendMove(selected, p, step); } selected null; });本地预校验最大的价值不是防作弊而是省流量和降低服务端压力非法走子直接提示「马腿被蹩」不用等一个网络往返。收到服务端sync后再用服务端棋盘重绘保证双方看到的一致。对刚入门微信小程序开发的读者来说这套「本地即时反馈 服务端权威同步」的组合是最稳妥的联机交互模型。5. 联机小程序避坑记录从真机调试到审核的六个坎5.1 合法域名报错开发工具能跑手机一扫码就懵现象开发工具里勾了「不校验合法域名」玩得欢真机预览时 WebSocket 连接直接被拒报url not in domain list。原因小程序真机环境强制校验 socket 合法域名而且要求域名必须 ICP 备案、必须走 WSS。解决在 mp 后台的「开发管理 → 开发设置 → 服务器域名」里配置 socket 合法域名开发阶段临时用「不校验」选项调试上线前一定配上。域名配置生效有延迟刚改完别急着测等几分钟再扫码。5.2 切后台回来棋盘不动了现象玩家切出去回消息再切回来对方已经走了好几步自己这边界面还停在旧棋盘点哪里都没反应。原因小程序在后台会被挂起WebSocket 连接被系统回收或静默断开onSocketClose有时不触发。解决页面onShow里检查连接readyState不是OPEN就重建连接用roomId openid重连服务端补发一次sync全量棋盘。只重连不心跳掉线还是发现不了两个机制要配套。5.3 落子重复提交一步走了两次现象网络抖动时客户端重发 move服务端收到两条相同消息两步棋变一步棋盘错乱。原因消息没有序号服务端无法区分「重试」和「新走子」。解决客户端每次落子生成单调递增的step服务端只接受step room.step的消息同时在收到sync确认前锁住棋盘不允许连续落子。这个坑在小程序里尤其常见因为wx.sendSocketMessage失败后重试是常规操作。5.4 iPhone 上棋盘发虚、点击错位现象同样的 canvas 代码Android 正常iPhone 上模糊点击位置偏移。原因canvas 的 CSS 尺寸和物理像素不一致触摸坐标拿的是 CSS 像素绘制坐标没做 dpr 换算。解决canvas.width cssWidth * dpr绘制前ctx.scale(dpr, dpr)触摸事件里用原始坐标和 CSS 坐标比较两套坐标系才不会打架。这里最容易踩的坑是在scale之后忘记改回绘制线宽导致 iPhone 上线条粗细不一。5.5 将帅照面判不出来现象双方帅将同列中间空着程序居然认为合法走着走着局势很诡异。原因实现将军检测时只查了「对方棋子能否攻击到帅」没把「帅将同列且中间无子」当成攻击路径。解决在isKingAttacked里加规则帅和将同列时如果中间没有其他棋子则先「照面」的一方视为被攻击此局面非法。这个 bug 藏得很深单测里必须写一组「同列对峙」的用例。5.6 上线审核被要求补充游戏类目现象提交审核提示类目与功能不符要求补充游戏相关资质小程序每年年审时同理会卡。原因小程序平台对「实时对战」类功能审查较严棋牌游戏类目需要对应主体和资质。解决个人开发者做学习演示尽量把联机做成「好友约战 异步落子」或加入人机模式正式发布前先确认主体类型和类目要求不要等审核被拒再改架构。6. 上线前的进阶一步用对局回放和房主迁移把房间做牢象棋联机做到能玩只是第一步真正耐用的房间还需要两个能力对局可回放、房主可迁移。对局回放的实现很轻服务端收到每次合法 move 后把{seq, from, to, fen, ts}追加到房间的只增记录里落库时按room_id seq追加不更新已有记录。前端回放时用setInterval按固定间隔逐条应用落子或者做一个 slider 拖动到指定seq重绘。这样赛后复盘、异常对局排查、版本问题定位都有据可查。我早期只存最终棋盘用户说「我明明赢了」时完全没法核对加了对局记录后这类纠纷基本消失。房主迁移处理的是「创建房间的人中途退出」房间对象里单独存一个hostId房主掉线超过保活窗口时把hostId转给当前仍在线的观战者或另一位玩家并广播一条host_changed消息。不做这个房主一走整个房间就散四个人约好的棋局全泡汤。另外建议在服务端按房间加一个串行队列同房间的消息按到达顺序逐个处理绝不并发修改同一份棋盘。Node 单进程事件循环天然满足这个约束如果用 Java 实现要避免多线程直接改 board否则两个线程同时校验同一局面步序就会乱。我的习惯是每改一个联机规则先跑一遍「双客户端 断网重连 乱序重发」的模拟脚本再上真机。联机程序的问题九成在边界状态不在正常流程。希望你做中国象棋联机小程序时能少踩这些坑一次跑通。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。