616 lines
21 KiB
Python
616 lines
21 KiB
Python
"""
|
||
算法管理器模块
|
||
实现不同的匹配算法(技能匹配、快速匹配等)
|
||
"""
|
||
|
||
import time
|
||
import random
|
||
from typing import Dict, Any, List, Optional
|
||
from collections import defaultdict
|
||
|
||
class AlgorithmManager:
|
||
"""
|
||
算法管理器
|
||
实现不同的匹配算法(技能匹配、快速匹配等)
|
||
"""
|
||
|
||
def __init__(self, plugin):
|
||
"""
|
||
初始化算法管理器
|
||
|
||
Args:
|
||
plugin: 匹配系统插件实例
|
||
"""
|
||
self.plugin = plugin
|
||
self.enabled = False
|
||
self.initialized = False
|
||
|
||
# 算法配置
|
||
self.algorithm_config = {
|
||
"default_algorithm": "skill_based",
|
||
"available_algorithms": ["skill_based", "quick", "team_balanced", "custom"],
|
||
"skill_based_config": {
|
||
"skill_range": 100, # 技能匹配范围
|
||
"max_wait_time": 120.0, # 最大等待时间(秒)
|
||
"expand_range_over_time": True, # 随时间扩展匹配范围
|
||
"range_expansion_rate": 10 # 每秒扩展范围
|
||
},
|
||
"team_balanced_config": {
|
||
"balance_threshold": 0.1, # 平衡阈值
|
||
"max_balance_attempts": 5 # 最大平衡尝试次数
|
||
},
|
||
"quick_match_config": {
|
||
"max_queue_time": 30.0, # 最大排队时间
|
||
"min_players": 2, # 最少玩家数
|
||
"max_players": 8 # 最多玩家数
|
||
}
|
||
}
|
||
|
||
# 算法状态
|
||
self.algorithm_state = {
|
||
"active_algorithm": "skill_based",
|
||
"total_matches": 0,
|
||
"successful_matches": 0,
|
||
"failed_matches": 0,
|
||
"average_match_quality": 0.0
|
||
}
|
||
|
||
# 玩家数据存储
|
||
self.player_data = {} # 玩家技能、统计数据等
|
||
|
||
# 算法统计
|
||
self.algorithm_stats = {
|
||
"algorithms_run": 0,
|
||
"matches_created": 0,
|
||
"players_matched": 0,
|
||
"algorithm_errors": 0
|
||
}
|
||
|
||
# 回调函数
|
||
self.algorithm_callbacks = {
|
||
"algorithm_selected": [],
|
||
"match_quality_calculated": [],
|
||
"algorithm_error": []
|
||
}
|
||
|
||
# 时间戳记录
|
||
self.last_algorithm_run = 0.0
|
||
self.last_stats_reset = 0.0
|
||
|
||
print("✓ 算法管理器已创建")
|
||
|
||
def initialize(self) -> bool:
|
||
"""
|
||
初始化算法管理器
|
||
|
||
Returns:
|
||
是否初始化成功
|
||
"""
|
||
try:
|
||
print("正在初始化算法管理器...")
|
||
|
||
# 注册默认算法
|
||
self._register_default_algorithms()
|
||
|
||
self.initialized = True
|
||
print("✓ 算法管理器初始化完成")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 算法管理器初始化失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def _register_default_algorithms(self):
|
||
"""注册默认算法"""
|
||
try:
|
||
print("✓ 默认算法已注册")
|
||
except Exception as e:
|
||
print(f"✗ 默认算法注册失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
|
||
def enable(self) -> bool:
|
||
"""
|
||
启用算法管理器
|
||
|
||
Returns:
|
||
是否启用成功
|
||
"""
|
||
try:
|
||
if not self.initialized:
|
||
print("✗ 算法管理器未初始化")
|
||
return False
|
||
|
||
self.enabled = True
|
||
print("✓ 算法管理器已启用")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 算法管理器启用失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def disable(self):
|
||
"""禁用算法管理器"""
|
||
try:
|
||
self.enabled = False
|
||
print("✓ 算法管理器已禁用")
|
||
|
||
except Exception as e:
|
||
print(f"✗ 算法管理器禁用失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def finalize(self):
|
||
"""清理算法管理器资源"""
|
||
try:
|
||
# 禁用算法管理器
|
||
if self.enabled:
|
||
self.disable()
|
||
|
||
# 清理回调
|
||
self.algorithm_callbacks.clear()
|
||
|
||
self.initialized = False
|
||
print("✓ 算法管理器资源已清理")
|
||
|
||
except Exception as e:
|
||
print(f"✗ 算法管理器资源清理失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def update(self, dt: float):
|
||
"""
|
||
更新算法管理器状态
|
||
|
||
Args:
|
||
dt: 时间增量(秒)
|
||
"""
|
||
try:
|
||
if not self.enabled:
|
||
return
|
||
|
||
self.last_algorithm_run = time.time()
|
||
|
||
except Exception as e:
|
||
print(f"✗ 算法管理器更新失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def set_active_algorithm(self, algorithm_name: str) -> bool:
|
||
"""
|
||
设置活动算法
|
||
|
||
Args:
|
||
algorithm_name: 算法名称
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
try:
|
||
if algorithm_name not in self.algorithm_config["available_algorithms"]:
|
||
print(f"✗ 不支持的算法: {algorithm_name}")
|
||
return False
|
||
|
||
self.algorithm_state["active_algorithm"] = algorithm_name
|
||
print(f"✓ 活动算法已设置为: {algorithm_name}")
|
||
|
||
# 触发算法选择回调
|
||
self._trigger_algorithm_callback("algorithm_selected", {
|
||
"algorithm": algorithm_name,
|
||
"timestamp": time.time()
|
||
})
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 活动算法设置失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
return False
|
||
|
||
def run_matching_algorithm(self, players: List[Dict[str, Any]], algorithm: str = None) -> Optional[List[List[Dict[str, Any]]]]:
|
||
"""
|
||
运行匹配算法
|
||
|
||
Args:
|
||
players: 玩家列表
|
||
algorithm: 算法名称(可选,默认使用活动算法)
|
||
|
||
Returns:
|
||
匹配结果(玩家分组列表)或None
|
||
"""
|
||
try:
|
||
if not self.enabled:
|
||
return None
|
||
|
||
# 使用指定算法或活动算法
|
||
if algorithm is None:
|
||
algorithm = self.algorithm_state["active_algorithm"]
|
||
|
||
# 验证算法支持
|
||
if algorithm not in self.algorithm_config["available_algorithms"]:
|
||
print(f"✗ 不支持的算法: {algorithm}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
return None
|
||
|
||
self.algorithm_stats["algorithms_run"] += 1
|
||
|
||
# 根据算法类型运行相应算法
|
||
if algorithm == "skill_based":
|
||
result = self._run_skill_based_matching(players)
|
||
elif algorithm == "quick":
|
||
result = self._run_quick_matching(players)
|
||
elif algorithm == "team_balanced":
|
||
result = self._run_team_balanced_matching(players)
|
||
else:
|
||
# 默认使用技能匹配
|
||
result = self._run_skill_based_matching(players)
|
||
|
||
if result:
|
||
self.algorithm_state["successful_matches"] += 1
|
||
self.algorithm_state["total_matches"] += 1
|
||
self.algorithm_stats["matches_created"] += 1
|
||
|
||
# 计算匹配质量
|
||
match_quality = self._calculate_match_quality(result)
|
||
self.algorithm_state["average_match_quality"] = (
|
||
self.algorithm_state["average_match_quality"] * (self.algorithm_state["successful_matches"] - 1) +
|
||
match_quality
|
||
) / self.algorithm_state["successful_matches"]
|
||
|
||
# 触发匹配质量计算回调
|
||
self._trigger_algorithm_callback("match_quality_calculated", {
|
||
"quality": match_quality,
|
||
"algorithm": algorithm,
|
||
"timestamp": time.time()
|
||
})
|
||
|
||
else:
|
||
self.algorithm_state["failed_matches"] += 1
|
||
|
||
return result
|
||
|
||
except Exception as e:
|
||
print(f"✗ 匹配算法运行失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
return None
|
||
|
||
def _run_skill_based_matching(self, players: List[Dict[str, Any]]) -> Optional[List[List[Dict[str, Any]]]]:
|
||
"""
|
||
运行基于技能的匹配算法
|
||
|
||
Args:
|
||
players: 玩家列表
|
||
|
||
Returns:
|
||
匹配结果或None
|
||
"""
|
||
try:
|
||
if not players:
|
||
return None
|
||
|
||
config = self.algorithm_config["skill_based_config"]
|
||
skill_range = config["skill_range"]
|
||
|
||
# 按技能水平排序玩家
|
||
sorted_players = sorted(players, key=lambda p: p.get("skill_level", 0))
|
||
|
||
# 简化实现:将技能相近的玩家分组
|
||
groups = []
|
||
current_group = [sorted_players[0]]
|
||
|
||
for i in range(1, len(sorted_players)):
|
||
current_player = sorted_players[i]
|
||
last_player = current_group[-1]
|
||
|
||
# 检查技能差异是否在范围内
|
||
skill_diff = abs(current_player.get("skill_level", 0) - last_player.get("skill_level", 0))
|
||
|
||
if skill_diff <= skill_range and len(current_group) < 8: # 最多8人一组
|
||
current_group.append(current_player)
|
||
else:
|
||
# 创建新组
|
||
groups.append(current_group)
|
||
current_group = [current_player]
|
||
|
||
# 添加最后一组
|
||
if current_group:
|
||
groups.append(current_group)
|
||
|
||
print(f"✓ 技能匹配完成: 创建了 {len(groups)} 个组")
|
||
return groups
|
||
|
||
except Exception as e:
|
||
print(f"✗ 技能匹配算法失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
return None
|
||
|
||
def _run_quick_matching(self, players: List[Dict[str, Any]]) -> Optional[List[List[Dict[str, Any]]]]:
|
||
"""
|
||
运行快速匹配算法
|
||
|
||
Args:
|
||
players: 玩家列表
|
||
|
||
Returns:
|
||
匹配结果或None
|
||
"""
|
||
try:
|
||
if not players:
|
||
return None
|
||
|
||
config = self.algorithm_config["quick_match_config"]
|
||
min_players = config["min_players"]
|
||
max_players = config["max_players"]
|
||
|
||
# 简化实现:随机分组
|
||
groups = []
|
||
player_pool = players.copy()
|
||
random.shuffle(player_pool)
|
||
|
||
while player_pool:
|
||
group_size = min(random.randint(min_players, max_players), len(player_pool))
|
||
group = player_pool[:group_size]
|
||
groups.append(group)
|
||
player_pool = player_pool[group_size:]
|
||
|
||
print(f"✓ 快速匹配完成: 创建了 {len(groups)} 个组")
|
||
return groups
|
||
|
||
except Exception as e:
|
||
print(f"✗ 快速匹配算法失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
return None
|
||
|
||
def _run_team_balanced_matching(self, players: List[Dict[str, Any]]) -> Optional[List[List[Dict[str, Any]]]]:
|
||
"""
|
||
运行团队平衡匹配算法
|
||
|
||
Args:
|
||
players: 玩家列表
|
||
|
||
Returns:
|
||
匹配结果或None
|
||
"""
|
||
try:
|
||
if not players or len(players) < 2:
|
||
return None
|
||
|
||
# 简化实现:创建两个平衡的团队
|
||
# 按技能排序
|
||
sorted_players = sorted(players, key=lambda p: p.get("skill_level", 0), reverse=True)
|
||
|
||
team1 = []
|
||
team2 = []
|
||
team1_skill = 0
|
||
team2_skill = 0
|
||
|
||
# 贪心算法分配玩家到技能平衡的团队
|
||
for player in sorted_players:
|
||
player_skill = player.get("skill_level", 0)
|
||
|
||
if team1_skill <= team2_skill:
|
||
team1.append(player)
|
||
team1_skill += player_skill
|
||
else:
|
||
team2.append(player)
|
||
team2_skill += player_skill
|
||
|
||
groups = [team1, team2] if team1 and team2 else [sorted_players]
|
||
|
||
print(f"✓ 团队平衡匹配完成: 创建了 {len(groups)} 个组")
|
||
return groups
|
||
|
||
except Exception as e:
|
||
print(f"✗ 团队平衡匹配算法失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
return None
|
||
|
||
def _calculate_match_quality(self, groups: List[List[Dict[str, Any]]]) -> float:
|
||
"""
|
||
计算匹配质量
|
||
|
||
Args:
|
||
groups: 玩家分组
|
||
|
||
Returns:
|
||
匹配质量分数(0-1)
|
||
"""
|
||
try:
|
||
if not groups:
|
||
return 0.0
|
||
|
||
total_quality = 0.0
|
||
group_count = 0
|
||
|
||
for group in groups:
|
||
if len(group) < 2:
|
||
continue
|
||
|
||
# 计算组内技能差异
|
||
skills = [p.get("skill_level", 0) for p in group]
|
||
avg_skill = sum(skills) / len(skills)
|
||
variance = sum((s - avg_skill) ** 2 for s in skills) / len(skills)
|
||
std_dev = variance ** 0.5
|
||
|
||
# 转换为质量分数(标准差越小质量越高)
|
||
max_expected_std = 100.0 # 假设最大预期标准差
|
||
quality = max(0.0, 1.0 - (std_dev / max_expected_std))
|
||
|
||
total_quality += quality
|
||
group_count += 1
|
||
|
||
return total_quality / group_count if group_count > 0 else 0.0
|
||
|
||
except Exception as e:
|
||
print(f"✗ 匹配质量计算失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
return 0.0
|
||
|
||
def update_player_skill(self, player_id: str, skill_level: float, stats: Dict[str, Any] = None):
|
||
"""
|
||
更新玩家技能
|
||
|
||
Args:
|
||
player_id: 玩家ID
|
||
skill_level: 技能等级
|
||
stats: 玩家统计数据
|
||
"""
|
||
try:
|
||
if player_id not in self.player_data:
|
||
self.player_data[player_id] = {
|
||
"skill_history": [],
|
||
"match_stats": {}
|
||
}
|
||
|
||
player_info = self.player_data[player_id]
|
||
player_info["current_skill"] = skill_level
|
||
player_info["skill_history"].append({
|
||
"skill": skill_level,
|
||
"timestamp": time.time()
|
||
})
|
||
|
||
if stats:
|
||
player_info["match_stats"].update(stats)
|
||
|
||
except Exception as e:
|
||
print(f"✗ 玩家技能更新失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
|
||
def get_player_skill(self, player_id: str) -> Optional[float]:
|
||
"""
|
||
获取玩家技能
|
||
|
||
Args:
|
||
player_id: 玩家ID
|
||
|
||
Returns:
|
||
玩家技能等级或None
|
||
"""
|
||
try:
|
||
if player_id in self.player_data:
|
||
return self.player_data[player_id].get("current_skill")
|
||
return None
|
||
except Exception as e:
|
||
print(f"✗ 玩家技能获取失败: {e}")
|
||
self.algorithm_stats["algorithm_errors"] += 1
|
||
return None
|
||
|
||
def get_algorithm_stats(self) -> Dict[str, Any]:
|
||
"""
|
||
获取算法统计信息
|
||
|
||
Returns:
|
||
算法统计字典
|
||
"""
|
||
return {
|
||
"state": self.algorithm_state.copy(),
|
||
"stats": self.algorithm_stats.copy(),
|
||
"config": self.algorithm_config.copy(),
|
||
"tracked_players": len(self.player_data)
|
||
}
|
||
|
||
def reset_stats(self):
|
||
"""重置算法统计信息"""
|
||
try:
|
||
self.algorithm_stats = {
|
||
"algorithms_run": 0,
|
||
"matches_created": 0,
|
||
"players_matched": 0,
|
||
"algorithm_errors": 0
|
||
}
|
||
|
||
self.algorithm_state["total_matches"] = 0
|
||
self.algorithm_state["successful_matches"] = 0
|
||
self.algorithm_state["failed_matches"] = 0
|
||
self.algorithm_state["average_match_quality"] = 0.0
|
||
|
||
print("✓ 算法统计信息已重置")
|
||
except Exception as e:
|
||
print(f"✗ 算法统计信息重置失败: {e}")
|
||
|
||
def set_algorithm_config(self, config: Dict[str, Any]) -> bool:
|
||
"""
|
||
设置算法配置
|
||
|
||
Args:
|
||
config: 算法配置字典
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
try:
|
||
self.algorithm_config.update(config)
|
||
print(f"✓ 算法配置已更新: {self.algorithm_config}")
|
||
return True
|
||
except Exception as e:
|
||
print(f"✗ 算法配置设置失败: {e}")
|
||
return False
|
||
|
||
def get_algorithm_config(self) -> Dict[str, Any]:
|
||
"""
|
||
获取算法配置
|
||
|
||
Returns:
|
||
算法配置字典
|
||
"""
|
||
return self.algorithm_config.copy()
|
||
|
||
def _trigger_algorithm_callback(self, callback_type: str, data: Dict[str, Any]):
|
||
"""
|
||
触发算法回调
|
||
|
||
Args:
|
||
callback_type: 回调类型
|
||
data: 回调数据
|
||
"""
|
||
try:
|
||
if callback_type in self.algorithm_callbacks:
|
||
for callback in self.algorithm_callbacks[callback_type]:
|
||
try:
|
||
callback(data)
|
||
except Exception as e:
|
||
print(f"✗ 算法回调执行失败: {callback_type} - {e}")
|
||
except Exception as e:
|
||
print(f"✗ 算法回调触发失败: {e}")
|
||
|
||
def register_algorithm_callback(self, callback_type: str, callback: callable):
|
||
"""
|
||
注册算法回调
|
||
|
||
Args:
|
||
callback_type: 回调类型
|
||
callback: 回调函数
|
||
"""
|
||
try:
|
||
if callback_type in self.algorithm_callbacks:
|
||
self.algorithm_callbacks[callback_type].append(callback)
|
||
print(f"✓ 算法回调已注册: {callback_type}")
|
||
else:
|
||
print(f"✗ 无效的回调类型: {callback_type}")
|
||
except Exception as e:
|
||
print(f"✗ 算法回调注册失败: {e}")
|
||
|
||
def unregister_algorithm_callback(self, callback_type: str, callback: callable):
|
||
"""
|
||
注销算法回调
|
||
|
||
Args:
|
||
callback_type: 回调类型
|
||
callback: 回调函数
|
||
"""
|
||
try:
|
||
if callback_type in self.algorithm_callbacks:
|
||
if callback in self.algorithm_callbacks[callback_type]:
|
||
self.algorithm_callbacks[callback_type].remove(callback)
|
||
print(f"✓ 算法回调已注销: {callback_type}")
|
||
else:
|
||
print(f"✗ 无效的回调类型: {callback_type}")
|
||
except Exception as e:
|
||
print(f"✗ 算法回调注销失败: {e}") |