# AnimalChess **Repository Path**: crohns/animal-chess ## Basic Information - **Project Name**: AnimalChess - **Description**: Animal Chess Demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-07 - **Last Updated**: 2025-12-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 斗兽棋游戏 WebSocket 服务器 基于 Node.js + TypeScript + Express + WebSocket 的斗兽棋游戏服务器,支持经典模式和暗棋模式。 ## 🚀 快速开始 ### 安装依赖 ```bash npm install ``` ### 启动服务器 ```bash # 开发模式(自动重启) npm run dev # 生产模式 npm start ``` 服务器将在 `ws://localhost:3000` 启动。 ## 📋 项目结构 ``` express-ts/ ├── src/ │ ├── controllers/ │ │ ├── AuthController.ts # 认证控制器 │ │ └── WebSocketController.ts # WebSocket 控制器 │ ├── game/ │ │ ├── GameLogic.ts # 游戏逻辑 │ │ ├── GameManager.ts # 游戏管理器 │ │ ├── GameRules.ts # 经典模式规则 │ │ └── AnQiGameRules.ts # 暗棋模式规则 │ ├── models/ │ │ ├── Animal.ts # 棋子模型 │ │ ├── Board.ts # 棋盘模型 │ │ ├── Game.ts # 游戏模型 │ │ └── User.ts # 用户模型 │ ├── routes/ │ │ ├── authRoutes.ts # 认证路由 │ │ ├── homeRoutes.ts # 首页路由 │ │ └── index.ts # 路由入口 │ ├── services/ │ │ ├── DatabaseService.ts # 数据库服务 │ │ ├── HuaweiAuthService.ts # 华为认证服务 │ │ ├── JwtService.ts # JWT服务 │ │ └── UserService.ts # 用户服务 │ ├── utils/ │ │ └── gameUtils.ts # 工具函数 │ └── index.ts # 服务器入口 └── README.md ``` ## 🎮 功能特性 - ✅ **双模式支持** - 经典模式(7×9)和暗棋模式(4×4) - ✅ **自动匹配** - 玩家加入后自动匹配对手 - ✅ **实时对战** - WebSocket 实时通信 - ✅ **回合计时** - 每回合30秒限制,超时自动判负 - ✅ **断线处理** - 断线立即判负 - ✅ **悔棋功能** - 需要对手同意,每局3次 - ✅ **统一协议** - 极简的8种消息类型 - ✅ **Express框架** - 使用Express处理HTTP请求和路由 --- ## 📡 WebSocket 通信协议 ### 协议总览 **总共 8 种消息:6 种客户端发送 + 2 种服务端发送** --- ## 📤 客户端 → 服务器(6种) ### 1. `join` - 加入游戏/匹配 **消息格式:** ```json { "type": "join", "data": { "id": "player_123", "playerName": "玩家名称", "avatar": "🐱", "gameMode": "normal" | "hidden" } } ``` **参数说明:** - `id`: 玩家唯一标识 - `playerName`: 玩家显示名称 - `avatar`: 玩家头像(emoji或URL) - `gameMode`: 游戏模式 - `"normal"` - 经典模式(7×9棋盘) - `"hidden"` 或 `"anqi"` - 暗棋模式(4×4棋盘) --- ### 2. `move` - 移动棋子 **消息格式:** ```json { "type": "move", "data": { "gameId": "game_abc123", "fromX": 0, "fromY": 2, "toX": 0, "toY": 3 } } ``` **坐标系统:** 左上角为 (0, 0),X 轴向右,Y 轴向下 --- ### 3. `flip` - 翻开棋子(仅暗棋模式) **消息格式:** ```json { "type": "flip", "data": { "gameId": "game_abc123", "x": 1, "y": 2 } } ``` --- ### 4. `surrender` - 认输 **消息格式:** ```json { "type": "surrender", "data": { "gameId": "game_abc123" } } ``` --- ### 5. `undo_request` - 请求悔棋 **消息格式:** ```json { "type": "undo_request", "data": { "gameId": "game_abc123" } } ``` **限制:** - 每局每人限制 **3次** 悔棋 - 只能在**自己的回合**请求 - **30秒**未回应自动拒绝 --- ### 6. `undo_response` - 回应悔棋请求 **消息格式:** ```json { "type": "undo_response", "data": { "gameId": "game_abc123", "accepted": true } } ``` --- ## 📥 服务器 → 客户端(2种) ### 1. `game_state` - 游戏状态(统一消息) **用途:** 通知游戏状态的所有变化 **消息格式:** ```json { "type": "game_state", "data": { "gameId": "game_abc123", "state": "playing", "action": "move", "actor": "me", "currentPlayer": "blue", "board": [...], "details": {...}, "stats": {...}, "timer": {...} } } ``` #### 核心字段 - `state`: 游戏状态 - `"waiting"` - 等待匹配 - `"matched"` - 匹配成功 - `"playing"` - 游戏进行中 - `"finished"` - 游戏结束 - `action`: 具体动作 - `"join"`, `"match"`, `"game_start"` - `"move"`, `"capture"`, `"flip"` - `"undo_request"`, `"undo_success"`, `"undo_rejected"` - `"game_over"` 等 - `actor`: 动作发起者 - `"me"` - 我自己 - `"opponent"` - 对手 - `"system"` - 系统 - `board`: 棋盘上的所有棋子数组 ```json [ { "id": 0, "type": 8, // 1-8对应动物等级 "side": "blue", "x": 0, "y": 0, "isRevealed": true // 暗棋模式:是否已翻开 } ] ``` - `timer`: 回合计时(30秒限制) ```json { "turnStartTime": 1699999999999, // 毫秒时间戳 "turnTimeLimit": 30, // 秒 "remainingTime": 25000 // 毫秒 } ``` #### 常见的 state + action 组合 | state | action | 说明 | |-------|--------|------| | `waiting` | `join` | 等待对手加入 | | `matched` | `match` | 匹配成功 | | `playing` | `game_start` | 游戏开始 | | `playing` | `move` | 移动棋子 | | `playing` | `capture` | 吃掉对方棋子 | | `playing` | `flip` | 翻开棋子 | | `playing` | `undo_request` | 收到/发送悔棋请求 | | `playing` | `undo_success` | 悔棋成功 | | `finished` | `game_over` | 游戏结束 | --- ### 2. `error` - 错误消息 **消息格式:** ```json { "type": "error", "data": { "code": "INVALID_MOVE", "message": "无效的移动" } } ``` **常见错误码:** - `PARSE_ERROR` - 消息解析失败 - `UNKNOWN_MESSAGE_TYPE` - 未知消息类型 - `GAME_NOT_FOUND` - 游戏不存在 - `NOT_YOUR_TURN` - 不是你的回合 - `INVALID_MOVE` - 无效的移动 - `NO_UNDO_LEFT` - 悔棋次数已用完 --- ## 🎮 游戏结束原因 当 `state="finished"` 且 `action="game_over"` 时,`details.reason` 包含: | reason | message | 说明 | |--------|---------|------| | `all_captured` | 对方所有棋子被吃 | 暗棋:一方全灭 | | `den_captured` | 占领对方兽穴 | 经典:进入兽穴 | | `surrender` | 对手认输 | 主动认输 | | `disconnect` | 对手离开游戏 | 断线/退出 | | `timeout` | 对手超时未操作 | 30秒未操作 | | `no_capture_draw` | 10回合无吃子,平局 | 暗棋:平局 | --- ## ⚡ 特殊机制 ### 断线处理 - 玩家断线 → **立即判负**,对手获胜 - 不等待,不给重连时间 ### 回合超时 - 每回合限时 **30秒** - 超时未操作 → 自动判负 ### 计时器 - 服务端记录 `turnStartTime` - 客户端根据 `turnStartTime` 和 `turnTimeLimit` 自己倒计时显示 - 每次操作后计时器重置 ### 悔棋功能 - 需要对手同意 - 每局每人限制 **3次** - 只能在**自己的回合**请求 - **30秒**未回应自动拒绝 --- ## 📱 客户端实现示例 ### 核心代码(15行) ```typescript // 1. 建立连接 const ws = new WebSocket('ws://localhost:3000'); // 2. 设置消息处理 ws.onmessage = (event) => { const message = JSON.parse(event.data); if (message.type === 'game_state') { handleGameState(message.data); } else if (message.type === 'error') { handleError(message.data); } }; // 3. 统一的游戏状态处理 function handleGameState(data) { // 更新UI this.gameState = data.state; // ⚠️ 重要:始终使用服务器发送的 board 数据(不要用 || 保留旧数据) // 特别是悔棋成功后,必须使用新的 board 数据 if (data.board && Array.isArray(data.board)) { this.board = data.board; // 直接替换,不要用 || this.board } this.currentPlayer = data.currentPlayer; // 启动倒计时(每次收到 playing 状态都重置) if (data.state === 'playing' && data.timer) { this.startCountdown(data.timer.remainingTime / 1000); } // 显示提示 if (data.details?.message) { this.showToast(data.details.message); } // 游戏结束 if (data.state === 'finished') { this.showResult(data.details); } // 特殊处理:悔棋成功后强制更新棋盘 if (data.action === 'undo_success') { console.log('悔棋成功,更新棋盘:', data.board.length, '个棋子'); this.forceUpdateBoard(data.board); // 强制更新UI } } // 4. 发送操作 function sendMove(from, to) { ws.send(JSON.stringify({ type: 'move', data: { gameId: this.gameId, fromX: from.x, fromY: from.y, toX: to.x, toY: to.y } })); } ``` --- ## 🎯 游戏规则(简要) ### 经典模式(7×9棋盘) - **棋盘**:7列×9行,包含河流、陷阱、兽穴 - **棋子**:每方8个,等级1-8(鼠-象) - **移动**:上下左右,可跳河(狮虎),陷阱削弱 - **吃子**:高等级吃低等级,鼠可吃象,象不能吃鼠 - **胜利**:进入对方兽穴或吃光对方棋子 ### 暗棋模式(4×4棋盘) - **棋盘**:4×4,16个格子 - **棋子**:每方8个,随机分布,初始未翻开 - **操作**:翻棋或移动(每回合一次) - **移动**:上下左右一格 - **吃子**:三种结果 - 正常吃子(高吃低) - 被吃(低被高吃) - 同归于尽(同等级或特殊规则) - **胜利**:多种条件(全灭、无子、无移动、10回合无吃子等) --- ## 🔧 开发说明 ### 核心类 - **WebSocketController**: 处理 WebSocket 连接和消息路由 - **GameManager**: 管理玩家匹配队列和游戏实例 - **GameLogic**: 实现游戏核心逻辑(创建游戏、执行移动、悔棋) - **GameRules**: 验证经典模式规则 - **AnQiGameRules**: 验证暗棋模式规则 ### 自动清理 服务器会自动清理: - 超过 1 小时未活动的已结束游戏 - 断线玩家的游戏状态 ### 日志输出 服务器会输出详细的操作日志: ``` [连接] 新客户端连接 (ws_abc123) [接收] join (ws_abc123) [加入] 玩家 玩家1 请求加入,模式: normal [匹配] 游戏 game_abc123 创建成功 [移动] 游戏 game_abc123: (0,2) -> (0,3) [游戏结束] 玩家 玩家1 认输,对手 玩家2 获胜 ``` --- ## 📊 协议设计原则 1. **统一** - 一个 `game_state` 消息处理所有游戏状态变化 2. **简洁** - 只有 8 种消息类型 3. **可靠** - 服务端权威,客户端只负责显示 4. **高效** - 无心跳,减少网络开销 --- ## 🚀 生产环境部署 ### 使用 PM2 管理进程 ```bash npm install -g pm2 pm2 start src/index.ts --name animal-chess-server --interpreter tsx pm2 startup pm2 save ``` ### 使用 Nginx 反向代理 ```nginx server { listen 80; server_name your-domain.com; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; } } ``` --- ## 📦 技术栈 - **Node.js** - 运行环境 - **TypeScript** - 类型安全 - **Express** - Web框架 - **ws** - WebSocket 库 - **tsx** - TypeScript 执行器 --- ## 📝 许可证 MIT --- ## 作者 小然