1379 lines
40 KiB
Markdown
1379 lines
40 KiB
Markdown
# 代码框架总结
|
||
|
||
## 📋 文档概述
|
||
|
||
本文档对进贤麻将子游戏代码框架进行全面总结,包括:
|
||
- **架构总览** - 整体架构设计和分层结构
|
||
- **模块依赖** - 各模块间的依赖关系和调用链
|
||
- **设计模式** - 使用的设计模式和最佳实践
|
||
- **扩展指南** - 如何扩展和定制功能
|
||
- **最佳实践** - 开发规范和注意事项
|
||
|
||
**文档目标**:帮助开发者从宏观角度理解整个代码框架,掌握系统设计思想和扩展方法。
|
||
|
||
---
|
||
|
||
## 🏗️ 整体架构设计
|
||
|
||
### 三层架构模型
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 接口层 (Interface Layer) │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ Export.js │◄────框架────┤ Import.js │ │
|
||
│ │ (框架→游戏) │ │ (游戏→框架) │ │
|
||
│ └──────────────┘ └──────────────┘ │
|
||
│ ↕ ↕ │
|
||
│ 14个框架接口 13个框架接口 │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
↕
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 业务层 (Business Layer) │
|
||
│ ┌──────────────────────────────────────────────┐ │
|
||
│ │ RpcHandler (RPC路由器) │ │
|
||
│ │ • 客户端→服务器RPC调用路由 │ │
|
||
│ │ • 34个RPC方法实现 │ │
|
||
│ └───────┬──────────────────────────────────────┘ │
|
||
│ ↓ │
|
||
│ ┌─────────────────────────────────────────────────────┐ │
|
||
│ │ GameController (游戏流程控制器) │ │
|
||
│ │ • 游戏生命周期管理 (开始、发牌、精牌、结算) │ │
|
||
│ │ • 状态转换协调 │ │
|
||
│ │ • 服务编排 │ │
|
||
│ └───┬─────────────┬─────────────┬────────────────────┘ │
|
||
│ ↓ ↓ ↓ │
|
||
│ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
|
||
│ │Operation │ │Mahjong │ │AI Manager │ │
|
||
│ │Manager │ │Game │ │(AI托管) │ │
|
||
│ │(操作管理)│ │Service │ └─────────────┘ │
|
||
│ └──────────┘ │(胡牌计分)│ │
|
||
│ └──────────┘ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
↕
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 数据层 (Data Layer) │
|
||
│ ┌──────────────────────────────────────────────┐ │
|
||
│ │ GameStateManager (游戏状态管理器) │ │
|
||
│ │ • 游戏状态对象 (gameState) │ │
|
||
│ │ • 状态机管理 (7个游戏阶段) │ │
|
||
│ │ • 玩家状态管理 │ │
|
||
│ │ • 操作历史记录 │ │
|
||
│ └──────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────┐ │
|
||
│ │ 共享模块 (Shared Modules) │ │
|
||
│ │ ┌────────────┐ ┌────────────┐ │ │
|
||
│ │ │MahjongCard │ │Jing │ │ │
|
||
│ │ │(牌对象) │ │Algorithm │ │ │
|
||
│ │ └────────────┘ └────────────┘ │ │
|
||
│ │ ┌────────────┐ ┌────────────┐ │ │
|
||
│ │ │WinDetection│ │Score │ │ │
|
||
│ │ │Factory │ │Calculation │ │ │
|
||
│ │ └────────────┘ └────────────┘ │ │
|
||
│ └──────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────┐ │
|
||
│ │ 配置与常量 (Config & Constants) │ │
|
||
│ │ • RoomConfigUtils (房间配置解析) │ │
|
||
│ │ • GameConstants (12个常量模块) │ │
|
||
│ │ • ErrorMessages (错误消息定义) │ │
|
||
│ └──────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 架构分层职责
|
||
|
||
| 分层 | 职责 | 核心组件 |
|
||
|------|------|----------|
|
||
| **接口层** | 框架与游戏双向通信 | Export.js, Import.js |
|
||
| **业务层** | 游戏逻辑实现和流程控制 | RpcHandler, GameController, Services |
|
||
| **数据层** | 状态管理、数据存储、算法实现 | GameStateManager, Shared Modules, Config |
|
||
|
||
---
|
||
|
||
## 🔗 模块依赖关系
|
||
|
||
### 完整依赖图
|
||
|
||
```
|
||
mod.js (模块入口)
|
||
↓ require
|
||
┌───────────────────────────────────────────────────┐
|
||
│ 接口层加载 │
|
||
│ ├─ export.js ────────────┐ │
|
||
│ └─ import.js │ │
|
||
└───────────────────────────┼───────────────────────┘
|
||
│
|
||
┌───────────────────────────┼───────────────────────┐
|
||
│ 业务层加载 │ │
|
||
│ ├─ RpcHandler.js ────────┤ │
|
||
│ ├─ GameController.js │ │
|
||
│ ├─ OperationManager.js │ │
|
||
│ ├─ MahjongGameService.js │ │
|
||
│ └─ AIManager.js │ │
|
||
└───────────────────────────┼───────────────────────┘
|
||
│
|
||
┌───────────────────────────┼───────────────────────┐
|
||
│ 数据层加载 (被业务层依赖)│ │
|
||
│ ├─ GameStateManager.js ──┘ │
|
||
│ ├─ shared/dataStructures/ │
|
||
│ │ └─ MahjongCard.js │
|
||
│ ├─ shared/algorithms/ │
|
||
│ │ ├─ JingAlgorithm.js │
|
||
│ │ ├─ WinDetectionFactory.js │
|
||
│ │ ├─ ScoreCalculation.js │
|
||
│ │ └─ PatternFactory.js │
|
||
│ ├─ shared/config/ │
|
||
│ │ ├─ RoomConfigUtils.js │
|
||
│ │ └─ RuleConfigParser.js │
|
||
│ └─ shared/constants/ │
|
||
│ └─ index.js (导出12个常量模块) │
|
||
└───────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 核心依赖关系详解
|
||
|
||
#### 1️⃣ 接口层依赖
|
||
|
||
```javascript
|
||
// export.js 依赖
|
||
export.js
|
||
├─ GameController (业务流程控制)
|
||
├─ RoomConfigUtils (配置解析)
|
||
├─ ErrorMessages (错误消息)
|
||
└─ Logger (日志记录)
|
||
|
||
// import.js 依赖
|
||
import.js
|
||
└─ 无内部依赖 (纯粹封装框架接口)
|
||
```
|
||
|
||
#### 2️⃣ 业务层依赖
|
||
|
||
```javascript
|
||
// RpcHandler.js 依赖
|
||
RpcHandler
|
||
├─ GameController (委托业务逻辑)
|
||
├─ OperationManager (操作验证)
|
||
├─ ErrorHandler (错误处理)
|
||
└─ Logger (日志记录)
|
||
|
||
// GameController.js 依赖
|
||
GameController
|
||
├─ GameStateManager (状态管理)
|
||
├─ MahjongGameService (胡牌计分)
|
||
├─ OperationManager (操作管理)
|
||
├─ AIManager (AI托管)
|
||
├─ DiceService (掷骰子)
|
||
├─ CardUtils (牌工具)
|
||
└─ JingAlgorithm (精牌算法)
|
||
|
||
// OperationManager.js 依赖
|
||
OperationManager
|
||
├─ GameStateManager (状态查询)
|
||
├─ WinDetectionFactory (胡牌检测)
|
||
├─ CardUtils (牌型检查)
|
||
└─ GameConstants (常量)
|
||
|
||
// MahjongGameService.js 依赖
|
||
MahjongGameService
|
||
├─ WinDetectionFactory (胡牌检测)
|
||
├─ ScoreCalculation (计分计算)
|
||
├─ JingAlgorithm (精分计算、比精)
|
||
├─ PatternFactory (牌型分析)
|
||
└─ BiJingSystem (比精系统)
|
||
```
|
||
|
||
#### 3️⃣ 数据层依赖
|
||
|
||
```javascript
|
||
// GameStateManager.js 依赖
|
||
GameStateManager
|
||
├─ GameConstants (游戏常量)
|
||
├─ RoomConfigUtils (房间配置)
|
||
└─ ErrorMessages (错误消息)
|
||
|
||
// WinDetectionFactory.js 依赖
|
||
WinDetectionFactory
|
||
├─ MahjongCard (牌对象)
|
||
├─ JingAlgorithm (精牌处理)
|
||
├─ PatternFactory (牌型检测)
|
||
└─ GameConstants (牌型常量)
|
||
|
||
// ScoreCalculation.js 依赖
|
||
ScoreCalculation
|
||
├─ JingAlgorithm (精分计算)
|
||
├─ GameConstants (分数常量)
|
||
└─ PatternFactory (牌型信息)
|
||
|
||
// JingAlgorithm.js 依赖
|
||
JingAlgorithm
|
||
├─ MahjongCard (牌对象操作)
|
||
└─ GameConstants (精牌规则常量)
|
||
|
||
// MahjongCard.js 依赖
|
||
MahjongCard
|
||
└─ 无依赖 (基础数据结构)
|
||
```
|
||
|
||
### 依赖层级图
|
||
|
||
```
|
||
Level 0 (基础层 - 无依赖)
|
||
├─ MahjongCard
|
||
├─ GameConstants
|
||
└─ ErrorMessages
|
||
|
||
Level 1 (算法层 - 依赖Level 0)
|
||
├─ JingAlgorithm (依赖: MahjongCard)
|
||
├─ RoomConfigUtils (依赖: GameConstants)
|
||
├─ PatternFactory (依赖: MahjongCard)
|
||
└─ CardUtils (依赖: MahjongCard)
|
||
|
||
Level 2 (服务层 - 依赖Level 0-1)
|
||
├─ WinDetectionFactory (依赖: MahjongCard, JingAlgorithm, PatternFactory)
|
||
├─ ScoreCalculation (依赖: JingAlgorithm, PatternFactory)
|
||
├─ GameStateManager (依赖: GameConstants, RoomConfigUtils)
|
||
└─ DiceService (依赖: GameConstants)
|
||
|
||
Level 3 (业务层 - 依赖Level 0-2)
|
||
├─ OperationManager (依赖: GameStateManager, WinDetectionFactory)
|
||
├─ MahjongGameService (依赖: WinDetectionFactory, ScoreCalculation, JingAlgorithm)
|
||
└─ AIManager (依赖: OperationManager, GameStateManager)
|
||
|
||
Level 4 (控制层 - 依赖Level 0-3)
|
||
└─ GameController (依赖: GameStateManager, OperationManager, MahjongGameService, AIManager)
|
||
|
||
Level 5 (RPC层 - 依赖Level 0-4)
|
||
└─ RpcHandler (依赖: GameController, OperationManager)
|
||
|
||
Level 6 (接口层 - 依赖Level 0-5)
|
||
└─ export.js (依赖: GameController, RoomConfigUtils)
|
||
```
|
||
|
||
---
|
||
|
||
## 🎨 设计模式应用
|
||
|
||
### 1. 工厂模式 (Factory Pattern)
|
||
|
||
#### 应用场景
|
||
- **MahjongCard创建** - 统一牌对象创建
|
||
- **WinDetectionFactory** - 胡牌检测策略工厂
|
||
- **PatternFactory** - 牌型分析工厂
|
||
|
||
#### 实现示例
|
||
|
||
```javascript
|
||
// MahjongCard.js - 工厂方法
|
||
MahjongCard.create = function(code) {
|
||
return new MahjongCard(code);
|
||
};
|
||
|
||
MahjongCard.createBatch = function(codes) {
|
||
return codes.map(function(code) {
|
||
return MahjongCard.create(code);
|
||
});
|
||
};
|
||
|
||
// WinDetectionFactory.js - 策略工厂
|
||
WinDetectionFactory.detectWin = function(handTiles, jingInfo, gameState) {
|
||
// 根据不同牌型策略检测
|
||
var strategies = [
|
||
this._checkPinghu, // 平胡策略
|
||
this._checkQidui, // 七对策略
|
||
this._checkSipeng, // 四碰策略
|
||
this._checkShisanlan, // 十三烂策略
|
||
this._checkQixingShisanlan // 七星十三烂策略
|
||
];
|
||
|
||
var results = [];
|
||
for (var i = 0; i < strategies.length; i++) {
|
||
var result = strategies[i].call(this, handTiles, jingInfo);
|
||
if (result.canWin) {
|
||
results.push(result);
|
||
}
|
||
}
|
||
|
||
return this._selectBestPattern(results);
|
||
};
|
||
```
|
||
|
||
**优势**:
|
||
- ✅ 统一对象创建接口
|
||
- ✅ 封装复杂创建逻辑
|
||
- ✅ 支持策略扩展
|
||
|
||
### 2. 单例模式 (Singleton Pattern)
|
||
|
||
#### 应用场景
|
||
- **Logger** - 全局日志记录器
|
||
- **ErrorHandler** - 全局错误处理器
|
||
- **Constants** - 常量定义模块
|
||
|
||
#### 实现示例
|
||
|
||
```javascript
|
||
// Logger.js - 单例实现
|
||
var Logger = (function() {
|
||
var instance;
|
||
|
||
function createLogger() {
|
||
var config = {
|
||
level: 'INFO',
|
||
maxHistorySize: 1000,
|
||
enableConsole: true
|
||
};
|
||
|
||
var history = [];
|
||
var statistics = {
|
||
DEBUG: 0,
|
||
INFO: 0,
|
||
WARN: 0,
|
||
ERROR: 0
|
||
};
|
||
|
||
return {
|
||
log: function(level, message, data) {
|
||
// 实现日志记录
|
||
},
|
||
getHistory: function() {
|
||
return history;
|
||
},
|
||
getStatistics: function() {
|
||
return statistics;
|
||
}
|
||
};
|
||
}
|
||
|
||
return {
|
||
getInstance: function() {
|
||
if (!instance) {
|
||
instance = createLogger();
|
||
}
|
||
return instance;
|
||
}
|
||
};
|
||
})();
|
||
|
||
// 使用
|
||
var logger = Logger.getInstance();
|
||
logger.log('INFO', '游戏开始');
|
||
```
|
||
|
||
**优势**:
|
||
- ✅ 全局唯一实例
|
||
- ✅ 统一访问入口
|
||
- ✅ 资源共享
|
||
|
||
### 3. 观察者模式 (Observer Pattern)
|
||
|
||
#### 应用场景
|
||
- **游戏状态变化通知** - GameStateManager通知状态变化
|
||
- **操作事件通知** - OperationManager通知操作完成
|
||
|
||
#### 实现示例
|
||
|
||
```javascript
|
||
// GameStateManager.js - 观察者模式
|
||
GameStateManager.transitionToPhase = function(gameState, newPhase, transitionData) {
|
||
var oldPhase = gameState.phase;
|
||
|
||
// 1. 状态转换
|
||
gameState.previousPhase = oldPhase;
|
||
gameState.phase = newPhase;
|
||
|
||
// 2. 通知观察者
|
||
this._notifyPhaseChange(gameState, oldPhase, newPhase, transitionData);
|
||
|
||
return {
|
||
success: true,
|
||
oldPhase: oldPhase,
|
||
newPhase: newPhase
|
||
};
|
||
};
|
||
|
||
GameStateManager._notifyPhaseChange = function(gameState, oldPhase, newPhase, data) {
|
||
// 触发状态变化事件
|
||
if (typeof gameState.onPhaseChange === 'function') {
|
||
gameState.onPhaseChange({
|
||
oldPhase: oldPhase,
|
||
newPhase: newPhase,
|
||
data: data,
|
||
timestamp: Date.now()
|
||
});
|
||
}
|
||
};
|
||
```
|
||
|
||
**优势**:
|
||
- ✅ 解耦状态变化和业务响应
|
||
- ✅ 支持多个观察者
|
||
- ✅ 灵活的事件处理
|
||
|
||
### 4. 策略模式 (Strategy Pattern)
|
||
|
||
#### 应用场景
|
||
- **胡牌检测** - 不同牌型检测策略
|
||
- **AI决策** - 不同难度AI策略
|
||
|
||
#### 实现示例
|
||
|
||
```javascript
|
||
// AIManager.js - 策略模式
|
||
AIManager.decideOperation = function(gameState, playerId) {
|
||
var aiLevel = this._getAILevel(playerId);
|
||
|
||
// 根据AI等级选择策略
|
||
var strategy;
|
||
switch (aiLevel) {
|
||
case 'easy':
|
||
strategy = this._easyStrategy;
|
||
break;
|
||
case 'medium':
|
||
strategy = this._mediumStrategy;
|
||
break;
|
||
case 'hard':
|
||
strategy = this._hardStrategy;
|
||
break;
|
||
default:
|
||
strategy = this._mediumStrategy;
|
||
}
|
||
|
||
return strategy.call(this, gameState, playerId);
|
||
};
|
||
|
||
// 简单策略 - 随机操作
|
||
AIManager._easyStrategy = function(gameState, playerId) {
|
||
var availableOps = this._getAvailableOperations(gameState, playerId);
|
||
if (availableOps.length === 0) return null;
|
||
|
||
var randomIndex = Math.floor(Math.random() * availableOps.length);
|
||
return availableOps[randomIndex];
|
||
};
|
||
|
||
// 中等策略 - 优先高价值操作
|
||
AIManager._mediumStrategy = function(gameState, playerId) {
|
||
var availableOps = this._getAvailableOperations(gameState, playerId);
|
||
if (availableOps.length === 0) return null;
|
||
|
||
// 按优先级排序: 胡 > 杠 > 碰 > 吃
|
||
availableOps.sort(function(a, b) {
|
||
return a.priority - b.priority;
|
||
});
|
||
|
||
return availableOps[0];
|
||
};
|
||
```
|
||
|
||
**优势**:
|
||
- ✅ 算法独立封装
|
||
- ✅ 易于切换和扩展
|
||
- ✅ 符合开闭原则
|
||
|
||
### 5. 适配器模式 (Adapter Pattern)
|
||
|
||
#### 应用场景
|
||
- **RoomAdapter** - 适配友乐平台房间接口
|
||
- **ConfigAdapter** - 适配不同版本配置格式
|
||
|
||
#### 实现示例
|
||
|
||
```javascript
|
||
// RoomAdapter.js - 适配器模式
|
||
var RoomAdapter = {
|
||
/**
|
||
* 将友乐平台房间对象转换为游戏内部格式
|
||
*/
|
||
adaptRoom: function(o_room) {
|
||
return {
|
||
roomcode: o_room.roomcode,
|
||
roomtype: o_room.roomtype,
|
||
players: this._adaptPlayers(o_room.players),
|
||
config: this._parseRoomConfig(o_room.roomtype)
|
||
};
|
||
},
|
||
|
||
_adaptPlayers: function(platformPlayers) {
|
||
return platformPlayers.map(function(player, index) {
|
||
return {
|
||
id: player.playerid,
|
||
seat: index,
|
||
nickname: player.nickname,
|
||
avatar: player.avatar,
|
||
score: player.gameinfo ? player.gameinfo.score : 0
|
||
};
|
||
});
|
||
},
|
||
|
||
_parseRoomConfig: function(roomtype) {
|
||
return RoomConfigUtils.parse(roomtype);
|
||
}
|
||
};
|
||
```
|
||
|
||
**优势**:
|
||
- ✅ 隔离平台差异
|
||
- ✅ 简化业务逻辑
|
||
- ✅ 提高可移植性
|
||
|
||
### 6. 模板方法模式 (Template Method Pattern)
|
||
|
||
#### 应用场景
|
||
- **GameController.startGame** - 游戏开始流程模板
|
||
- **RpcHandler.handleOperation** - RPC处理流程模板
|
||
|
||
#### 实现示例
|
||
|
||
```javascript
|
||
// RpcHandler.js - 模板方法
|
||
RpcHandler.prototype.handleOperation = function(pack, room, callback) {
|
||
// 模板流程
|
||
|
||
// 1. 前置验证
|
||
var validationResult = this._validateRequest(pack, room);
|
||
if (!validationResult.valid) {
|
||
return callback({ error: validationResult.error });
|
||
}
|
||
|
||
// 2. 权限检查
|
||
var authResult = this._checkPermission(pack, room);
|
||
if (!authResult.authorized) {
|
||
return callback({ error: 'PERMISSION_DENIED' });
|
||
}
|
||
|
||
// 3. 执行操作 (子类实现)
|
||
var result = this._performOperation(pack, room);
|
||
|
||
// 4. 后置处理
|
||
this._afterOperation(pack, room, result);
|
||
|
||
// 5. 返回结果
|
||
callback(result);
|
||
};
|
||
|
||
// 子类实现具体操作
|
||
RpcHandler.prototype._performOperation = function(pack, room) {
|
||
// 由具体RPC方法实现
|
||
throw new Error('_performOperation must be implemented');
|
||
};
|
||
```
|
||
|
||
**优势**:
|
||
- ✅ 统一处理流程
|
||
- ✅ 强制执行规范
|
||
- ✅ 减少重复代码
|
||
|
||
---
|
||
|
||
## 🔧 扩展指南
|
||
|
||
### 1. 添加新的RPC方法
|
||
|
||
#### 步骤1: 在RpcHandler中定义方法
|
||
|
||
```javascript
|
||
// rpc/RpcHandler.js
|
||
/**
|
||
* 新增RPC: 玩家请求换牌 (示例)
|
||
*/
|
||
RpcHandler.prototype.player_swap_card = function(pack, room, callback) {
|
||
var playerId = pack.playerid;
|
||
var cardToSwap = pack.data.card;
|
||
var gameState = room.gameState;
|
||
|
||
// 1. 验证操作合法性
|
||
if (gameState.phase !== 'playing') {
|
||
return callback({
|
||
error: 'INVALID_PHASE',
|
||
message: '当前阶段不允许换牌'
|
||
});
|
||
}
|
||
|
||
// 2. 执行换牌逻辑
|
||
var result = this._performCardSwap(gameState, playerId, cardToSwap);
|
||
|
||
// 3. 广播换牌消息
|
||
if (result.success) {
|
||
room.import.broadcast_operation(room, {
|
||
type: 'card_swap',
|
||
playerId: playerId,
|
||
card: cardToSwap
|
||
});
|
||
}
|
||
|
||
callback(result);
|
||
};
|
||
|
||
RpcHandler.prototype._performCardSwap = function(gameState, playerId, card) {
|
||
// 实现换牌逻辑
|
||
return { success: true };
|
||
};
|
||
```
|
||
|
||
#### 步骤2: 在mod.js中注册RPC方法
|
||
|
||
```javascript
|
||
// mod.js
|
||
mod_jinxianmahjong.player_swap_card = function(pack) {
|
||
return mod_jinxianmahjong.rpcHandler.player_swap_card(
|
||
pack,
|
||
mod_jinxianmahjong.room,
|
||
function(result) {
|
||
return result;
|
||
}
|
||
);
|
||
};
|
||
```
|
||
|
||
#### 步骤3: 客户端调用
|
||
|
||
```javascript
|
||
// 客户端代码
|
||
SendPack({
|
||
cmd: "DoPack",
|
||
gameid: "jinxianmahjong",
|
||
rpc: "player_swap_card",
|
||
data: {
|
||
card: "3m"
|
||
}
|
||
});
|
||
```
|
||
|
||
### 2. 添加新的游戏规则
|
||
|
||
#### 步骤1: 在RoomConfigUtils中定义规则位
|
||
|
||
```javascript
|
||
// shared/config/RoomConfigUtils.js
|
||
RoomConfigUtils.parse = function(roomtype) {
|
||
// ... 现有解析逻辑
|
||
|
||
// 新增规则位解析 (假设使用第14位)
|
||
var enableCardSwap = roomtype.charAt(13) === '1';
|
||
|
||
return {
|
||
// ... 现有配置
|
||
specialRules: {
|
||
enableCardSwap: enableCardSwap, // 新规则
|
||
// ... 其他规则
|
||
}
|
||
};
|
||
};
|
||
```
|
||
|
||
#### 步骤2: 在操作验证中使用规则
|
||
|
||
```javascript
|
||
// game/OperationManager.js
|
||
OperationManager.validateCardSwap = function(gameState, playerId, card) {
|
||
// 检查规则是否启用
|
||
if (!gameState.rules.specialRules.enableCardSwap) {
|
||
return {
|
||
valid: false,
|
||
error: 'CARD_SWAP_DISABLED',
|
||
message: '当前房间规则不允许换牌'
|
||
};
|
||
}
|
||
|
||
// ... 其他验证逻辑
|
||
|
||
return { valid: true };
|
||
};
|
||
```
|
||
|
||
### 3. 添加新的共享算法模块
|
||
|
||
#### 步骤1: 创建算法模块
|
||
|
||
```javascript
|
||
// shared/algorithms/NewAlgorithm.js
|
||
/**
|
||
* 新算法模块 (示例: 牌型强度评估)
|
||
*/
|
||
var NewAlgorithm = {
|
||
/**
|
||
* 评估手牌强度
|
||
* @param {Array<MahjongCard>} handCards - 手牌
|
||
* @param {Object} jingInfo - 精牌信息
|
||
* @returns {Object} 评估结果
|
||
*/
|
||
evaluateHandStrength: function(handCards, jingInfo) {
|
||
var strength = {
|
||
score: 0,
|
||
level: 'low', // low/medium/high
|
||
factors: []
|
||
};
|
||
|
||
// 1. 计算精牌数量
|
||
var jingCount = this._countJingCards(handCards, jingInfo);
|
||
strength.score += jingCount * 10;
|
||
strength.factors.push({ type: 'jing', count: jingCount });
|
||
|
||
// 2. 计算刻子数量
|
||
var keziCount = this._countKezi(handCards);
|
||
strength.score += keziCount * 15;
|
||
strength.factors.push({ type: 'kezi', count: keziCount });
|
||
|
||
// 3. 计算顺子数量
|
||
var shunziCount = this._countShunzi(handCards);
|
||
strength.score += shunziCount * 10;
|
||
strength.factors.push({ type: 'shunzi', count: shunziCount });
|
||
|
||
// 4. 确定等级
|
||
if (strength.score >= 50) {
|
||
strength.level = 'high';
|
||
} else if (strength.score >= 30) {
|
||
strength.level = 'medium';
|
||
}
|
||
|
||
return strength;
|
||
},
|
||
|
||
_countJingCards: function(handCards, jingInfo) {
|
||
var count = 0;
|
||
for (var i = 0; i < handCards.length; i++) {
|
||
if (handCards[i].code === jingInfo.zhengJing ||
|
||
handCards[i].code === jingInfo.fuJing) {
|
||
count++;
|
||
}
|
||
}
|
||
return count;
|
||
},
|
||
|
||
// ... 其他辅助方法
|
||
};
|
||
|
||
// 导出 (Node.js)
|
||
if (typeof module !== 'undefined' && module.exports) {
|
||
module.exports = NewAlgorithm;
|
||
}
|
||
|
||
// 导出 (浏览器)
|
||
if (typeof window !== 'undefined') {
|
||
window.NewAlgorithm = NewAlgorithm;
|
||
}
|
||
```
|
||
|
||
#### 步骤2: 在mod.js中加载模块
|
||
|
||
```javascript
|
||
// mod.js
|
||
function loadGameModules() {
|
||
// ... 现有模块加载
|
||
|
||
// 加载新算法模块
|
||
require('./shared/algorithms/NewAlgorithm.js');
|
||
console.log("[mod_jinxianmahjong] NewAlgorithm.js 加载成功");
|
||
|
||
// ... 继续加载其他模块
|
||
}
|
||
```
|
||
|
||
#### 步骤3: 在业务代码中使用
|
||
|
||
```javascript
|
||
// AIManager.js
|
||
AIManager.decideDiscard = function(gameState, playerId) {
|
||
var player = gameState.players[playerId];
|
||
var handCards = player.handCards;
|
||
|
||
// 使用新算法评估手牌
|
||
var strength = NewAlgorithm.evaluateHandStrength(
|
||
handCards,
|
||
gameState.jingInfo
|
||
);
|
||
|
||
console.log('手牌强度:', strength.level, '分数:', strength.score);
|
||
|
||
// 根据评估结果做决策
|
||
// ...
|
||
};
|
||
```
|
||
|
||
### 4. 添加新的常量模块
|
||
|
||
#### 步骤1: 创建常量文件
|
||
|
||
```javascript
|
||
// shared/constants/NewConstants.js
|
||
/**
|
||
* 新功能常量定义 (示例: 换牌功能)
|
||
*/
|
||
var NewConstants = {
|
||
// 换牌规则
|
||
SWAP_RULES: {
|
||
MAX_SWAPS_PER_ROUND: 3, // 每局最多换牌次数
|
||
SWAP_TIME_LIMIT: 10000, // 换牌时限(毫秒)
|
||
SWAP_COST: 1 // 换牌消耗(房卡)
|
||
},
|
||
|
||
// 换牌状态
|
||
SWAP_STATUS: {
|
||
AVAILABLE: 'available', // 可以换牌
|
||
IN_PROGRESS: 'in_progress', // 换牌中
|
||
COMPLETED: 'completed', // 已完成
|
||
TIMEOUT: 'timeout' // 超时
|
||
}
|
||
};
|
||
|
||
// 导出
|
||
if (typeof module !== 'undefined' && module.exports) {
|
||
module.exports = NewConstants;
|
||
}
|
||
```
|
||
|
||
#### 步骤2: 在constants/index.js中注册
|
||
|
||
```javascript
|
||
// shared/constants/index.js
|
||
var NewConstants = require('./NewConstants.js');
|
||
|
||
module.exports = {
|
||
// ... 现有常量
|
||
NewConstants: NewConstants
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 最佳实践
|
||
|
||
### 1. 代码组织原则
|
||
|
||
#### ✅ 单一职责原则 (SRP)
|
||
|
||
每个模块只负责一个功能:
|
||
|
||
```javascript
|
||
// ❌ 不好的实践: 一个类做太多事情
|
||
var GameManager = {
|
||
dealCards: function() {},
|
||
checkWin: function() {},
|
||
calculateScore: function() {},
|
||
updateDatabase: function() {},
|
||
sendNotification: function() {}
|
||
};
|
||
|
||
// ✅ 好的实践: 职责分离
|
||
var GameController = {
|
||
dealCards: function() {}
|
||
};
|
||
|
||
var WinDetection = {
|
||
checkWin: function() {}
|
||
};
|
||
|
||
var ScoreCalculation = {
|
||
calculateScore: function() {}
|
||
};
|
||
```
|
||
|
||
#### ✅ 开闭原则 (OCP)
|
||
|
||
对扩展开放,对修改关闭:
|
||
|
||
```javascript
|
||
// ✅ 使用策略模式扩展牌型检测
|
||
WinDetectionFactory.registerPattern = function(patternName, checkFunction) {
|
||
this.patterns[patternName] = checkFunction;
|
||
};
|
||
|
||
// 添加新牌型无需修改核心代码
|
||
WinDetectionFactory.registerPattern('custom_pattern', function(handCards) {
|
||
// 自定义牌型检测逻辑
|
||
});
|
||
```
|
||
|
||
#### ✅ 依赖倒置原则 (DIP)
|
||
|
||
依赖抽象而非具体实现:
|
||
|
||
```javascript
|
||
// ✅ 通过接口依赖
|
||
GameController.setGameService = function(gameService) {
|
||
// gameService可以是任何实现了胡牌检测接口的对象
|
||
this.gameService = gameService;
|
||
};
|
||
```
|
||
|
||
### 2. 错误处理规范
|
||
|
||
#### 统一错误返回格式
|
||
|
||
```javascript
|
||
// 所有方法返回统一格式
|
||
function someOperation() {
|
||
try {
|
||
// 操作逻辑
|
||
return {
|
||
success: true,
|
||
data: result
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
success: false,
|
||
error: error.code || 'UNKNOWN_ERROR',
|
||
message: error.message,
|
||
details: error.details
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 使用ErrorHandler统一处理
|
||
|
||
```javascript
|
||
// 使用ErrorHandler记录和处理错误
|
||
var ErrorHandler = require('./shared/utils/ErrorHandler.js');
|
||
|
||
function riskyOperation() {
|
||
try {
|
||
// ... 可能出错的代码
|
||
} catch (error) {
|
||
ErrorHandler.handle(error, {
|
||
context: 'riskyOperation',
|
||
severity: 'high',
|
||
additionalInfo: { /* ... */ }
|
||
});
|
||
|
||
return {
|
||
success: false,
|
||
error: 'OPERATION_FAILED'
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 日志记录规范
|
||
|
||
#### 关键节点记录日志
|
||
|
||
```javascript
|
||
var Logger = require('./shared/utils/Logger.js');
|
||
|
||
function startGame(room) {
|
||
Logger.info('游戏开始', {
|
||
roomcode: room.roomcode,
|
||
playerCount: room.playerCount,
|
||
timestamp: Date.now()
|
||
});
|
||
|
||
try {
|
||
// 游戏逻辑
|
||
Logger.debug('发牌完成', { cards: dealedCards });
|
||
Logger.info('精牌确定', { jingCard: jingCard });
|
||
} catch (error) {
|
||
Logger.error('游戏开始失败', {
|
||
error: error.message,
|
||
stack: error.stack
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 日志级别使用规范
|
||
|
||
| 级别 | 使用场景 | 示例 |
|
||
|------|---------|------|
|
||
| **TRACE** | 最详细的调试信息 | 每次牌型检测的详细步骤 |
|
||
| **DEBUG** | 开发调试信息 | 中间变量值、函数调用参数 |
|
||
| **INFO** | 重要业务流程节点 | 游戏开始、发牌完成、胡牌 |
|
||
| **WARN** | 潜在问题警告 | 配置缺失使用默认值、性能警告 |
|
||
| **ERROR** | 错误但程序可继续 | 玩家操作失败、数据验证失败 |
|
||
| **FATAL** | 致命错误需要停止 | 数据库连接失败、核心模块加载失败 |
|
||
|
||
### 4. 性能优化建议
|
||
|
||
#### ✅ 避免重复计算
|
||
|
||
```javascript
|
||
// ❌ 不好: 重复计算
|
||
function checkWin(handCards, jingInfo) {
|
||
var jingCards = getJingCards(jingInfo); // 每次调用都计算
|
||
// ...
|
||
}
|
||
|
||
// ✅ 好: 缓存计算结果
|
||
GameStateManager.createGameState = function(roomcode, roomtype, players) {
|
||
var gameState = {
|
||
// ...
|
||
jingInfo: null,
|
||
_jingCardsCache: null // 缓存精牌列表
|
||
};
|
||
|
||
// 添加getter缓存精牌列表
|
||
Object.defineProperty(gameState, 'jingCards', {
|
||
get: function() {
|
||
if (!this._jingCardsCache && this.jingInfo) {
|
||
this._jingCardsCache = getJingCards(this.jingInfo);
|
||
}
|
||
return this._jingCardsCache;
|
||
}
|
||
});
|
||
|
||
return gameState;
|
||
};
|
||
```
|
||
|
||
#### ✅ 减少对象创建
|
||
|
||
```javascript
|
||
// ❌ 不好: 频繁创建临时对象
|
||
for (var i = 0; i < 1000; i++) {
|
||
var result = {
|
||
success: true,
|
||
data: someData
|
||
};
|
||
process(result);
|
||
}
|
||
|
||
// ✅ 好: 复用对象
|
||
var result = { success: false, data: null };
|
||
for (var i = 0; i < 1000; i++) {
|
||
result.success = true;
|
||
result.data = someData;
|
||
process(result);
|
||
}
|
||
```
|
||
|
||
#### ✅ 使用对象池
|
||
|
||
```javascript
|
||
// MahjongCard对象池
|
||
var MahjongCardPool = {
|
||
pool: [],
|
||
maxSize: 200,
|
||
|
||
acquire: function(code) {
|
||
var card = this.pool.pop();
|
||
if (card) {
|
||
card.code = code;
|
||
card.reset();
|
||
return card;
|
||
}
|
||
return new MahjongCard(code);
|
||
},
|
||
|
||
release: function(card) {
|
||
if (this.pool.length < this.maxSize) {
|
||
card.reset();
|
||
this.pool.push(card);
|
||
}
|
||
}
|
||
};
|
||
```
|
||
|
||
### 5. 测试规范
|
||
|
||
#### 单元测试结构
|
||
|
||
```javascript
|
||
// tests/units/test_module_name.js
|
||
console.log('\n========================================');
|
||
console.log('模块名称 单元测试');
|
||
console.log('========================================\n');
|
||
|
||
// 加载模块
|
||
var ModuleName = require('../../path/to/ModuleName.js');
|
||
|
||
// 测试用例1: 基础功能
|
||
console.log('【测试1】基础功能测试\n');
|
||
var result1 = ModuleName.someFunction(input1);
|
||
if (result1.success) {
|
||
console.log('✅ 测试通过');
|
||
} else {
|
||
console.log('❌ 测试失败:', result1.error);
|
||
}
|
||
|
||
// 测试用例2: 边界条件
|
||
console.log('\n【测试2】边界条件测试\n');
|
||
// ...
|
||
|
||
// 测试用例3: 错误处理
|
||
console.log('\n【测试3】错误处理测试\n');
|
||
// ...
|
||
|
||
console.log('\n========================================');
|
||
console.log('测试完成');
|
||
console.log('========================================\n');
|
||
```
|
||
|
||
### 6. 命名规范
|
||
|
||
#### 变量命名
|
||
|
||
```javascript
|
||
// ✅ 好的命名: 清晰表达意图
|
||
var handCards = []; // 手牌
|
||
var jingInfo = {}; // 精牌信息
|
||
var isWinDetected = false; // 是否检测到胡牌
|
||
var totalScore = 0; // 总分数
|
||
|
||
// ❌ 不好的命名: 含义不明
|
||
var hc = [];
|
||
var ji = {};
|
||
var flag = false;
|
||
var num = 0;
|
||
```
|
||
|
||
#### 函数命名
|
||
|
||
```javascript
|
||
// ✅ 好的命名: 动词+名词,清晰描述功能
|
||
function dealCards(players, dealer) {}
|
||
function checkWinPattern(handCards) {}
|
||
function calculateJingScore(jingCards) {}
|
||
function validateOperation(operation) {}
|
||
|
||
// ❌ 不好的命名
|
||
function deal() {}
|
||
function check() {}
|
||
function calc() {}
|
||
function validate() {}
|
||
```
|
||
|
||
#### 常量命名
|
||
|
||
```javascript
|
||
// ✅ 大写字母+下划线
|
||
var MAX_PLAYERS = 4;
|
||
var DEFAULT_HAND_SIZE = 13;
|
||
var JING_BASE_SCORE = 2;
|
||
|
||
// ❌ 小写或驼峰
|
||
var maxPlayers = 4;
|
||
var defaultHandSize = 13;
|
||
```
|
||
|
||
---
|
||
|
||
## 🚨 常见问题与解决方案
|
||
|
||
### 问题1: 模块加载顺序错误
|
||
|
||
**症状**: 提示"XXX is not defined"或"Cannot read property of undefined"
|
||
|
||
**原因**: 依赖的模块还未加载就使用了
|
||
|
||
**解决方案**:
|
||
```javascript
|
||
// 检查mod.js中的加载顺序
|
||
// 确保被依赖的模块先加载
|
||
|
||
// ✅ 正确顺序
|
||
require('./shared/dataStructures/MahjongCard.js'); // 基础
|
||
require('./shared/algorithms/JingAlgorithm.js'); // 依赖MahjongCard
|
||
require('./shared/algorithms/WinDetectionFactory.js'); // 依赖JingAlgorithm
|
||
|
||
// ❌ 错误顺序
|
||
require('./shared/algorithms/WinDetectionFactory.js'); // 依赖未加载!
|
||
require('./shared/algorithms/JingAlgorithm.js');
|
||
require('./shared/dataStructures/MahjongCard.js');
|
||
```
|
||
|
||
### 问题2: 状态不一致
|
||
|
||
**症状**: gameState中的数据与预期不符,操作验证失败
|
||
|
||
**原因**: 多处修改gameState但未同步更新
|
||
|
||
**解决方案**:
|
||
```javascript
|
||
// ✅ 使用GameStateManager统一管理状态
|
||
GameStateManager.updatePlayerState(gameState, playerId, {
|
||
handCards: newHandCards,
|
||
score: newScore
|
||
});
|
||
|
||
// ❌ 直接修改可能导致不一致
|
||
gameState.players[playerId].handCards = newHandCards;
|
||
gameState.players[playerId].score = newScore; // 可能忘记更新其他相关字段
|
||
```
|
||
|
||
### 问题3: 精牌计算错误
|
||
|
||
**症状**: 精分计算结果与预期不符
|
||
|
||
**原因**: 没有正确识别精牌或计算逻辑错误
|
||
|
||
**解决方案**:
|
||
```javascript
|
||
// ✅ 使用JingAlgorithm统一处理
|
||
var jingScore = JingAlgorithm.calculateJingScore(
|
||
handCards,
|
||
jingInfo,
|
||
{
|
||
includeZhengJing: true,
|
||
includeFuJing: true,
|
||
excludeUsedCards: true // 排除已用于组合的精牌
|
||
}
|
||
);
|
||
|
||
// ❌ 手动计算容易出错
|
||
var jingScore = 0;
|
||
for (var i = 0; i < handCards.length; i++) {
|
||
if (handCards[i].code === jingInfo.zhengJing) {
|
||
jingScore += 2;
|
||
}
|
||
// 可能遗漏副精、杠精等特殊情况
|
||
}
|
||
```
|
||
|
||
### 问题4: 内存泄漏
|
||
|
||
**症状**: 服务器运行一段时间后内存持续增长
|
||
|
||
**原因**: 游戏结束后未清理gameState或事件监听器
|
||
|
||
**解决方案**:
|
||
```javascript
|
||
// ✅ 游戏结束后清理
|
||
GameController.endGame = function(room) {
|
||
// 1. 保存游戏记录
|
||
this._saveGameRecord(room.gameState);
|
||
|
||
// 2. 清理事件监听器
|
||
room.gameState.onPhaseChange = null;
|
||
room.gameState.onOperationComplete = null;
|
||
|
||
// 3. 清理定时器
|
||
if (room.gameState.operationTimer) {
|
||
clearTimeout(room.gameState.operationTimer);
|
||
}
|
||
|
||
// 4. 释放gameState引用
|
||
room.gameState = null;
|
||
};
|
||
```
|
||
|
||
### 问题5: RPC调用超时
|
||
|
||
**症状**: 客户端请求超时,无响应
|
||
|
||
**原因**: 服务端处理时间过长或callback未调用
|
||
|
||
**解决方案**:
|
||
```javascript
|
||
// ✅ 确保所有分支都调用callback
|
||
RpcHandler.prototype.player_operation = function(pack, room, callback) {
|
||
try {
|
||
// 验证
|
||
if (!this._validate(pack)) {
|
||
return callback({ error: 'INVALID_REQUEST' }); // 及时返回
|
||
}
|
||
|
||
// 处理
|
||
var result = this._process(pack, room);
|
||
|
||
// 返回结果
|
||
callback(result);
|
||
|
||
} catch (error) {
|
||
// 错误处理也要调用callback
|
||
callback({ error: 'INTERNAL_ERROR', message: error.message });
|
||
}
|
||
};
|
||
|
||
// ❌ 可能遗漏callback导致超时
|
||
RpcHandler.prototype.player_operation = function(pack, room, callback) {
|
||
if (!this._validate(pack)) {
|
||
return; // 忘记调用callback!
|
||
}
|
||
// ...
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 相关文档链接
|
||
|
||
- **上一篇**: [08-游戏流程概述.md](08-游戏流程概述.md) - 完整游戏流程
|
||
- **下一篇**: [10-快速入门指南.md](../development/10-快速入门指南.md) - 开发入门指南
|
||
- **参考文档**:
|
||
- [00-框架基础概述.md](../framework/00-框架基础概述.md) - 框架基础
|
||
- [04-游戏核心服务.md](../core/04-游戏核心服务.md) - 核心服务详解
|
||
- [05-共享代码模块.md](../core/05-共享代码模块.md) - 算法模块详解
|
||
|
||
---
|
||
|
||
## 📝 附录
|
||
|
||
### A. 模块清单
|
||
|
||
完整的模块列表和文件路径:
|
||
|
||
| 模块分类 | 模块名称 | 文件路径 |
|
||
|---------|---------|----------|
|
||
| **入口** | mod.js | `/server/games2/jinxianmahjong/mod.js` |
|
||
| **接口** | Export | `/server/games2/jinxianmahjong/export.js` |
|
||
| **接口** | Import | `/server/games2/jinxianmahjong/import.js` |
|
||
| **RPC** | RpcHandler | `/server/games2/jinxianmahjong/rpc/RpcHandler.js` |
|
||
| **控制** | GameController | `/server/games2/jinxianmahjong/game/GameController.js` |
|
||
| **服务** | OperationManager | `/server/games2/jinxianmahjong/game/OperationManager.js` |
|
||
| **服务** | MahjongGameService | `/server/games2/jinxianmahjong/game/MahjongGameService.js` |
|
||
| **服务** | AIManager | `/server/games2/jinxianmahjong/game/AIManager.js` |
|
||
| **数据** | GameStateManager | `/server/games2/jinxianmahjong/shared/dataStructures/GameStateManager.js` |
|
||
| **数据** | MahjongCard | `/server/games2/jinxianmahjong/shared/dataStructures/MahjongCard.js` |
|
||
| **算法** | JingAlgorithm | `/server/games2/jinxianmahjong/shared/algorithms/JingAlgorithm.js` |
|
||
| **算法** | WinDetectionFactory | `/server/games2/jinxianmahjong/shared/algorithms/WinDetectionFactory.js` |
|
||
| **算法** | ScoreCalculation | `/server/games2/jinxianmahjong/shared/algorithms/ScoreCalculation.js` |
|
||
| **算法** | PatternFactory | `/server/games2/jinxianmahjong/shared/algorithms/PatternFactory.js` |
|
||
| **配置** | RoomConfigUtils | `/server/games2/jinxianmahjong/shared/config/RoomConfigUtils.js` |
|
||
| **配置** | RuleConfigParser | `/server/games2/jinxianmahjong/shared/config/RuleConfigParser.js` |
|
||
| **常量** | Constants (12个模块) | `/server/games2/jinxianmahjong/shared/constants/` |
|
||
| **工具** | Logger | `/server/games2/jinxianmahjong/shared/utils/Logger.js` |
|
||
| **工具** | ErrorHandler | `/server/games2/jinxianmahjong/shared/utils/ErrorHandler.js` |
|
||
|
||
### B. 接口清单
|
||
|
||
#### Export接口 (14个)
|
||
|
||
1. `makewar` - 创建房间
|
||
2. `get_needroomcard` - 获取所需房卡数
|
||
3. `get_smallroundnumber` - 获取小局数
|
||
4. `get_roomtype` - 获取房间类型
|
||
5. `get_deskinfo` - 获取牌桌信息
|
||
6. `smallround_prepare` - 小局准备
|
||
7. `check_canhuprocess` - 检查胡牌过程
|
||
8. `check_canhupai` - 检查能否胡牌
|
||
9. `get_hu_describe` - 获取胡牌描述
|
||
10. `check_baopai` - 检查报牌
|
||
11. `check_baopai_process` - 检查报牌过程
|
||
12. `check_canhupai_fanpai` - 检查翻牌胡牌
|
||
13. `check_operate_fanpai` - 检查翻牌操作
|
||
14. `recieve_match_result` - 接收比赛结果
|
||
|
||
#### Import接口 (13个)
|
||
|
||
1. `smallround_finish` - 小局结束
|
||
2. `bigroom_finish` - 大局结束
|
||
3. `sendcards` - 发牌
|
||
4. `send_eachother_operate` - 发送玩家间操作
|
||
5. `send_hupai_broadcast` - 广播胡牌
|
||
6. `send_gangpai_broadcast` - 广播杠牌
|
||
7. `send_message` - 发送消息
|
||
8. `save_grade` - 保存战绩
|
||
9. `random` - 随机数
|
||
10. `get_trusteeship_level` - 获取托管等级
|
||
11. `get_ip_address` - 获取IP地址
|
||
12. `get_match_config` - 获取比赛配置
|
||
13. `finish_gametask` - 完成游戏任务
|
||
|
||
### C. 关键算法复杂度
|
||
|
||
| 算法 | 时间复杂度 | 空间复杂度 | 说明 |
|
||
|------|-----------|-----------|------|
|
||
| **平胡检测** | O(n²) | O(n) | n为手牌数量,需要尝试多种组合 |
|
||
| **七对检测** | O(n) | O(n) | 线性扫描即可 |
|
||
| **精分计算** | O(n) | O(1) | 遍历手牌一次 |
|
||
| **比精算法** | O(n) | O(n) | 需要构建牌型组合 |
|
||
| **操作优先级** | O(m log m) | O(m) | m为可用操作数量,需要排序 |
|
||
|
||
---
|
||
|
||
**文档版本**: v1.0
|
||
**最后更新**: 2025年10月15日
|
||
**维护者**: 进贤麻将开发团队
|