使用外置配置文件,代码优化
This commit is contained in:
parent
078fadb033
commit
1053ea7697
120
README.md
120
README.md
@ -51,6 +51,16 @@ pip install -r requirements.txt
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 查看帮助信息
|
||||
|
||||
运行以下命令查看主程序的帮助信息:
|
||||
|
||||
```bash
|
||||
python main.py --help
|
||||
```
|
||||
|
||||
输出结果会显示所有可用的子命令和选项。
|
||||
|
||||
### 数据提供模块演示
|
||||
|
||||
运行以下命令启动数据提供模块的演示程序:
|
||||
@ -83,7 +93,7 @@ python main.py recommend
|
||||
运行以下命令启动评分系统的演示程序:
|
||||
|
||||
```bash
|
||||
python src/scoring_demo.py
|
||||
python main.py scoring
|
||||
```
|
||||
|
||||
演示程序将展示以下功能:
|
||||
@ -93,6 +103,12 @@ python src/scoring_demo.py
|
||||
- 比较不同权重设置下的评分差异
|
||||
- 动态修改配置文件并重新加载
|
||||
|
||||
您还可以指定自定义配置文件:
|
||||
|
||||
```bash
|
||||
python main.py scoring --config path/to/custom_config.yaml
|
||||
```
|
||||
|
||||
### 命令行界面
|
||||
|
||||
运行以下命令启动交互式命令行界面:
|
||||
@ -112,6 +128,13 @@ python main.py cli
|
||||
python main.py cli --population 8 --results 5 --level-weight 1.2 --count-weight 0.7 --cost-weight 0.2
|
||||
```
|
||||
|
||||
参数说明:
|
||||
- `--population`:阵容人口数量,范围1-10,默认为9
|
||||
- `--results`:推荐结果数量,默认为3
|
||||
- `--level-weight`:羁绊等级权重,默认为1.0
|
||||
- `--count-weight`:羁绊数量权重,默认为0.5
|
||||
- `--cost-weight`:棋子费用权重,默认为0.1
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
@ -140,7 +163,8 @@ TFT-Strategist/
|
||||
│ ├── __init__.py
|
||||
│ ├── data_provider_demo.py # 数据提供模块演示
|
||||
│ ├── recommendation_demo.py # 阵容推荐模块演示
|
||||
│ └── scoring_demo.py # 评分系统演示
|
||||
│ ├── scoring_demo.py # 评分系统演示
|
||||
│ └── test_scoring.py # 评分测试模块
|
||||
├── tests/ # 测试代码
|
||||
│ ├── test_data_provider.py # 数据提供模块测试
|
||||
│ ├── test_recommendation.py # 阵容推荐模块测试
|
||||
@ -250,75 +274,93 @@ for team in teams:
|
||||
|
||||
## 阵容评分模块详解
|
||||
|
||||
阵容评分模块提供了对阵容进行综合评分的功能,考虑多个因素。
|
||||
阵容评分模块是系统的核心评估组件,负责为推荐的阵容提供量化的评分指标。
|
||||
|
||||
### 主要组件
|
||||
|
||||
1. **TeamScorer**: 阵容评分系统
|
||||
1. **TeamScorer**: 阵容评分器
|
||||
- 综合考虑羁绊数量、等级、棋子费用等因素
|
||||
- 提供可配置的评分权重
|
||||
- 多维度评估阵容强度
|
||||
- 支持自定义权重配置
|
||||
- 提供灵活的评分API
|
||||
|
||||
2. **ScoringConfig**: 评分配置类
|
||||
- 定义各项评分权重
|
||||
- 支持自定义评分策略
|
||||
- 定义评分所需的各项权重参数
|
||||
- 提供默认配置值
|
||||
- 支持通过外部配置文件进行定制
|
||||
|
||||
3. **ConfigLoader**: 配置加载器
|
||||
- 从外部YAML或JSON文件加载自定义权重设置
|
||||
- 支持修改和重新加载配置
|
||||
- 从YAML或JSON文件加载评分配置
|
||||
- 支持热重载配置
|
||||
- 提供丰富的配置访问API
|
||||
|
||||
### 评分因素
|
||||
### 配置文件详解
|
||||
|
||||
1. **羁绊等级评分**:基于羁绊等级和额外激活的羁绊单位数量
|
||||
2. **羁绊种类数量评分**:基于激活的羁绊种类数量
|
||||
3. **棋子费用评分**:基于阵容中棋子的费用分布
|
||||
4. **自定义权重评分**:基于配置文件中设置的特定羁绊和棋子权重
|
||||
|
||||
### 自定义权重配置
|
||||
|
||||
你可以通过编辑 `data/weights_config.yaml` 文件来自定义评分权重:
|
||||
系统支持通过YAML配置文件(data/weights_config.yaml)来自定义评分权重:
|
||||
|
||||
```yaml
|
||||
# 基础权重配置
|
||||
base_weights:
|
||||
synergy_level_weight: 1.0 # 羁绊等级权重
|
||||
synergy_count_weight: 0.5 # 羁绊种类数量权重
|
||||
chess_cost_weight: 0.1 # 棋子费用权重
|
||||
synergy_level_weight: 1.0 # 羁绊等级权重
|
||||
synergy_count_weight: 0.5 # 羁绊数量权重
|
||||
chess_cost_weight: 0.1 # 棋子费用权重
|
||||
|
||||
# 羁绊权重配置(值越大,该羁绊在评分中的权重越高)
|
||||
# 羁绊权重配置
|
||||
synergy_weights:
|
||||
重装战士: 1.5
|
||||
重装战士: 1.5 # 特定羁绊权重
|
||||
魔法师: 1.2
|
||||
# 可以添加更多羁绊...
|
||||
# 其他羁绊...
|
||||
|
||||
# 棋子权重配置(值越大,该棋子在评分中的权重越高)
|
||||
# 棋子权重配置
|
||||
chess_weights:
|
||||
亚索: 1.5
|
||||
亚索: 1.5 # 特定棋子权重
|
||||
艾希: 1.2
|
||||
# 可以添加更多棋子...
|
||||
# 其他棋子...
|
||||
|
||||
# 羁绊等级权重
|
||||
synergy_level_weights:
|
||||
'1': 1.0 # 1级羁绊权重
|
||||
'2': 1.2 # 2级羁绊权重
|
||||
# 其他等级...
|
||||
|
||||
# 棋子费用权重
|
||||
cost_weights:
|
||||
'1': 1.0 # 1费棋子权重
|
||||
'2': 1.2 # 2费棋子权重
|
||||
# 其他费用...
|
||||
```
|
||||
|
||||
配置文件中的权重设置将直接影响阵容评分结果。权重值越大,对应的因素在评分中的影响越大。
|
||||
|
||||
### 使用示例
|
||||
|
||||
```python
|
||||
from src.scoring import TeamScorer
|
||||
from src.data_provider import DataQueryAPI
|
||||
from src.recommendation import RecommendationEngine
|
||||
|
||||
# 使用默认权重初始化评分器
|
||||
# 初始化评分器(自动加载配置文件)
|
||||
scorer = TeamScorer()
|
||||
|
||||
# 从配置文件加载自定义权重
|
||||
scorer_with_config = TeamScorer(config_path="data/weights_config.yaml")
|
||||
# 或者指定配置文件路径
|
||||
scorer = TeamScorer(config_path="path/to/custom_config.yaml")
|
||||
|
||||
# 动态设置特定羁绊的权重
|
||||
scorer.set_synergy_weight("狙神", 2.0)
|
||||
scorer.set_synergy_weight("刺客", 1.8)
|
||||
# 动态修改权重
|
||||
scorer.set_synergy_weight("忍者", 2.0) # 提高"忍者"羁绊的权重
|
||||
scorer.set_chess_weight("劫", 1.8) # 提高"劫"的权重
|
||||
|
||||
# 动态设置特定棋子的权重
|
||||
scorer.set_chess_weight("艾希", 2.0)
|
||||
scorer.set_chess_weight("薇恩", 1.8)
|
||||
# 重新加载配置文件
|
||||
scorer.reload_config()
|
||||
|
||||
# 评分阵容
|
||||
score = scorer.score_team(team)
|
||||
# 为阵容评分
|
||||
api = DataQueryAPI()
|
||||
engine = RecommendationEngine(api)
|
||||
teams = engine.recommend_team(population=8)
|
||||
|
||||
for team in teams:
|
||||
score = scorer.score_team(team)
|
||||
print(f"阵容评分: {score}")
|
||||
print(f"棋子: {[chess['displayName'] for chess in team.chess_list]}")
|
||||
print(f"羁绊: {team.synergy_levels}")
|
||||
```
|
||||
|
||||
## 接口模块详解
|
||||
|
95
main.py
95
main.py
@ -4,52 +4,83 @@
|
||||
"""
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
from src.data_provider_demo import main as data_provider_demo
|
||||
from src.recommendation_demo import main as recommendation_demo
|
||||
from src.interface.cli import main as cli_main
|
||||
from src.scoring.scoring_system import TeamScorer
|
||||
from src.test_scoring import main as test_scoring
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger("TFT-Strategist")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 创建参数解析器
|
||||
parser = argparse.ArgumentParser(description='云顶之弈阵容推荐器')
|
||||
subparsers = parser.add_subparsers(dest='command', help='命令')
|
||||
"""主程序入口"""
|
||||
parser = argparse.ArgumentParser(description="云顶之弈阵容推荐器")
|
||||
|
||||
# 数据提供模块命令
|
||||
data_parser = subparsers.add_parser('data', help='运行数据提供模块演示')
|
||||
# 添加子命令
|
||||
subparsers = parser.add_subparsers(dest="command", help="可用命令")
|
||||
|
||||
# 阵容推荐模块命令
|
||||
recommend_parser = subparsers.add_parser('recommend', help='运行阵容推荐模块演示')
|
||||
# 数据提供模块演示
|
||||
data_parser = subparsers.add_parser("data", help="数据提供模块演示")
|
||||
|
||||
# 命令行界面命令
|
||||
cli_parser = subparsers.add_parser('cli', help='运行命令行界面')
|
||||
# 阵容推荐模块演示
|
||||
recommend_parser = subparsers.add_parser("recommend", help="阵容推荐模块演示")
|
||||
|
||||
# 解析参数
|
||||
# 命令行界面
|
||||
cli_parser = subparsers.add_parser("cli", help="交互式命令行界面")
|
||||
cli_parser.add_argument("--population", type=int, help="阵容人口数量")
|
||||
cli_parser.add_argument("--results", type=int, help="推荐结果数量")
|
||||
cli_parser.add_argument("--level-weight", type=float, help="羁绊等级权重")
|
||||
cli_parser.add_argument("--count-weight", type=float, help="羁绊数量权重")
|
||||
cli_parser.add_argument("--cost-weight", type=float, help="棋子费用权重")
|
||||
|
||||
# 评分模块测试 (新增)
|
||||
scoring_parser = subparsers.add_parser("scoring", help="评分模块测试")
|
||||
scoring_parser.add_argument("--config", type=str, help="指定配置文件路径")
|
||||
|
||||
# 解析命令行参数
|
||||
args = parser.parse_args()
|
||||
|
||||
# 根据命令执行相应的功能
|
||||
if args.command == 'data':
|
||||
# 运行数据提供模块演示
|
||||
data_provider_demo()
|
||||
elif args.command == 'recommend':
|
||||
# 运行阵容推荐模块演示
|
||||
recommendation_demo()
|
||||
elif args.command == 'cli':
|
||||
# 运行命令行界面
|
||||
return cli_main()
|
||||
else:
|
||||
# 默认运行数据提供模块演示
|
||||
print_usage(parser)
|
||||
print("\n默认运行数据提供模块演示...\n")
|
||||
# 根据子命令执行相应功能
|
||||
if args.command == "data":
|
||||
logger.info("启动数据提供模块演示")
|
||||
# 调用数据提供模块演示函数
|
||||
data_provider_demo()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def print_usage(parser):
|
||||
"""打印使用帮助"""
|
||||
parser.print_help()
|
||||
|
||||
elif args.command == "recommend":
|
||||
logger.info("启动阵容推荐模块演示")
|
||||
# 调用阵容推荐模块演示函数
|
||||
recommendation_demo()
|
||||
|
||||
elif args.command == "cli":
|
||||
logger.info("启动交互式命令行界面")
|
||||
# 将命令行参数传递给cli_main函数
|
||||
return cli_main(
|
||||
population=args.population,
|
||||
results=args.results,
|
||||
level_weight=args.level_weight,
|
||||
count_weight=args.count_weight,
|
||||
cost_weight=args.cost_weight
|
||||
)
|
||||
|
||||
elif args.command == "scoring":
|
||||
logger.info("启动评分模块测试")
|
||||
if args.config:
|
||||
# 如果指定了配置文件路径,使用该路径进行测试
|
||||
test_scoring(config_path=args.config)
|
||||
else:
|
||||
# 否则使用默认配置文件
|
||||
test_scoring()
|
||||
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -34,8 +34,17 @@ class TFTCommandLine:
|
||||
self.recommendation_engine = RecommendationEngine(api=self.api)
|
||||
self.scorer = TeamScorer()
|
||||
|
||||
def run(self):
|
||||
"""运行命令行界面"""
|
||||
def run(self, population=None, results=None, level_weight=None, count_weight=None, cost_weight=None):
|
||||
"""
|
||||
运行命令行界面
|
||||
|
||||
Args:
|
||||
population (int, optional): 阵容人口数量
|
||||
results (int, optional): 推荐结果数量
|
||||
level_weight (float, optional): 羁绊等级权重
|
||||
count_weight (float, optional): 羁绊数量权重
|
||||
cost_weight (float, optional): 棋子费用权重
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description='云顶之弈阵容推荐器命令行界面', add_help=False)
|
||||
parser.add_argument('--population', type=int, default=9, help='阵容人口数量,默认为9')
|
||||
parser.add_argument('--results', type=int, default=3, help='返回结果数量,默认为3')
|
||||
@ -46,16 +55,21 @@ class TFTCommandLine:
|
||||
# 解析参数,但忽略未知的参数,以便与主程序的参数解析兼容
|
||||
args, _ = parser.parse_known_args()
|
||||
|
||||
# 优先使用传入的参数,其次使用命令行参数
|
||||
population = population or args.population
|
||||
max_results = results or args.results
|
||||
level_weight = level_weight or args.level_weight
|
||||
count_weight = count_weight or args.count_weight
|
||||
cost_weight = cost_weight or args.cost_weight
|
||||
|
||||
# 自定义评分权重
|
||||
self.scorer.customize_scoring(
|
||||
synergy_level_weight=args.level_weight,
|
||||
synergy_count_weight=args.count_weight,
|
||||
chess_cost_weight=args.cost_weight
|
||||
synergy_level_weight=level_weight,
|
||||
synergy_count_weight=count_weight,
|
||||
chess_cost_weight=cost_weight
|
||||
)
|
||||
|
||||
# 初始化阵容参数
|
||||
population = args.population
|
||||
max_results = args.results
|
||||
required_synergies = []
|
||||
required_chess = []
|
||||
|
||||
@ -178,11 +192,29 @@ class TFTCommandLine:
|
||||
print("\n------------------------")
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
def main(population=None, results=None, level_weight=None, count_weight=None, cost_weight=None):
|
||||
"""
|
||||
主函数
|
||||
|
||||
Args:
|
||||
population (int, optional): 阵容人口数量
|
||||
results (int, optional): 推荐结果数量
|
||||
level_weight (float, optional): 羁绊等级权重
|
||||
count_weight (float, optional): 羁绊数量权重
|
||||
cost_weight (float, optional): 棋子费用权重
|
||||
|
||||
Returns:
|
||||
int: 退出码
|
||||
"""
|
||||
cli = TFTCommandLine()
|
||||
try:
|
||||
cli.run()
|
||||
cli.run(
|
||||
population=population,
|
||||
results=results,
|
||||
level_weight=level_weight,
|
||||
count_weight=count_weight,
|
||||
cost_weight=cost_weight
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print("\n程序已退出")
|
||||
except Exception as e:
|
||||
|
@ -85,6 +85,24 @@ class ConfigLoader:
|
||||
Dict[str, float]: 基础权重参数到权重值的映射
|
||||
"""
|
||||
return self.config_data.get('base_weights', {})
|
||||
|
||||
def get_synergy_level_weights(self) -> Dict[str, float]:
|
||||
"""
|
||||
获取羁绊等级权重配置
|
||||
|
||||
Returns:
|
||||
Dict[str, float]: 羁绊等级到权重的映射
|
||||
"""
|
||||
return self.config_data.get('synergy_level_weights', {})
|
||||
|
||||
def get_cost_weights(self) -> Dict[str, float]:
|
||||
"""
|
||||
获取棋子费用权重配置
|
||||
|
||||
Returns:
|
||||
Dict[str, float]: 棋子费用到权重的映射
|
||||
"""
|
||||
return self.config_data.get('cost_weights', {})
|
||||
|
||||
def create_default_config(config_path: str) -> None:
|
||||
"""
|
||||
@ -110,6 +128,25 @@ def create_default_config(config_path: str) -> None:
|
||||
'艾希': 1.2,
|
||||
'璐璐': 1.3,
|
||||
'金克斯': 1.4
|
||||
},
|
||||
'synergy_level_weights': {
|
||||
'1': 1.0,
|
||||
'2': 1.2,
|
||||
'3': 1.5,
|
||||
'4': 1.8,
|
||||
'5': 2.0,
|
||||
'6': 2.3,
|
||||
'7': 2.6,
|
||||
'8': 3.0,
|
||||
'9': 3.3,
|
||||
'10': 3.5
|
||||
},
|
||||
'cost_weights': {
|
||||
'1': 1.0,
|
||||
'2': 1.2,
|
||||
'3': 1.5,
|
||||
'4': 1.8,
|
||||
'5': 2.0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,6 +130,20 @@ class TeamScorer:
|
||||
if chess_weights:
|
||||
self.config.chess_weights.update(chess_weights)
|
||||
logger.info(f"从配置文件加载棋子权重: {len(chess_weights)}个")
|
||||
|
||||
# 加载羁绊等级权重倍数 (使用新方法)
|
||||
synergy_level_weights = loader.get_synergy_level_weights()
|
||||
if synergy_level_weights:
|
||||
# 将字符串键转换为整数键
|
||||
level_multipliers = {int(k): float(v) for k, v in synergy_level_weights.items()}
|
||||
self.config.level_multipliers = level_multipliers
|
||||
logger.info(f"从配置文件加载羁绊等级权重倍数: {len(level_multipliers)}个")
|
||||
|
||||
# 加载棋子费用权重倍数 (使用新方法)
|
||||
cost_weights = loader.get_cost_weights()
|
||||
if cost_weights:
|
||||
self.config.cost_multipliers = cost_weights
|
||||
logger.info(f"从配置文件加载棋子费用权重倍数: {len(cost_weights)}个")
|
||||
|
||||
def score_team(self, team) -> float:
|
||||
"""
|
||||
|
79
src/test_scoring.py
Normal file
79
src/test_scoring.py
Normal file
@ -0,0 +1,79 @@
|
||||
"""
|
||||
测试评分系统的配置加载功能
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, List
|
||||
|
||||
# 添加项目根目录到Python路径
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from src.scoring.scoring_system import TeamScorer, ScoringConfig
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger("TFT-Strategist-TestScoring")
|
||||
|
||||
@dataclass
|
||||
class MockTeam:
|
||||
"""模拟的阵容类,用于测试"""
|
||||
chess_list: List[Dict]
|
||||
synergy_levels: Dict
|
||||
|
||||
def main(config_path=None):
|
||||
"""主函数"""
|
||||
logger.info("开始测试评分系统...")
|
||||
|
||||
# 创建评分器实例
|
||||
scorer = TeamScorer(config_path=config_path)
|
||||
|
||||
# 打印当前配置
|
||||
logger.info("基础权重:")
|
||||
logger.info(f" 羁绊等级权重: {scorer.config.synergy_level_weight}")
|
||||
logger.info(f" 羁绊数量权重: {scorer.config.synergy_count_weight}")
|
||||
logger.info(f" 棋子费用权重: {scorer.config.chess_cost_weight}")
|
||||
|
||||
logger.info("羁绊等级倍数:")
|
||||
for level, multiplier in sorted(scorer.config.level_multipliers.items()):
|
||||
logger.info(f" 等级 {level}: {multiplier}")
|
||||
|
||||
logger.info("棋子费用倍数:")
|
||||
for cost, multiplier in sorted(scorer.config.cost_multipliers.items()):
|
||||
logger.info(f" 费用 {cost}: {multiplier}")
|
||||
|
||||
logger.info("羁绊自定义权重 (前5个):")
|
||||
for name, weight in list(scorer.config.synergy_weights.items())[:5]:
|
||||
logger.info(f" {name}: {weight}")
|
||||
|
||||
logger.info("棋子自定义权重 (前5个):")
|
||||
for name, weight in list(scorer.config.chess_weights.items())[:5]:
|
||||
logger.info(f" {name}: {weight}")
|
||||
|
||||
# 测试评分函数
|
||||
mock_team = MockTeam(
|
||||
chess_list=[
|
||||
{"displayName": "佛耶戈", "price": "4"},
|
||||
{"displayName": "厄加特", "price": "5"},
|
||||
{"displayName": "盖伦", "price": "3"},
|
||||
{"displayName": "扎克", "price": "3"}
|
||||
],
|
||||
synergy_levels={
|
||||
"job": [
|
||||
{"name": "重装战士", "level": 2, "count": 2},
|
||||
{"name": "斗士", "level": 2, "count": 2}
|
||||
],
|
||||
"race": [
|
||||
{"name": "鳄霸", "level": 1, "count": 1}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
score = scorer.score_team(mock_team)
|
||||
logger.info(f"模拟阵容评分: {score}")
|
||||
|
||||
logger.info("测试完成!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user