实现权重配置管理功能,支持通过命令行参数指定配置文件路径,新增权重调整功能,优化评分系统和推荐引擎的初始化,更新Web界面以显示和调整权重设置。
This commit is contained in:
parent
0362a9c3ab
commit
5d4c52fba4
@ -1,121 +1,81 @@
|
|||||||
# 云顶之弈阵容评分权重配置文件
|
|
||||||
|
|
||||||
# 基础权重配置
|
|
||||||
base_weights:
|
base_weights:
|
||||||
synergy_level_weight: 1.0
|
|
||||||
synergy_count_weight: 0.5
|
|
||||||
chess_cost_weight: 0.1
|
chess_cost_weight: 0.1
|
||||||
|
synergy_count_weight: 0.5
|
||||||
# 羁绊权重配置(值越大,该羁绊在评分中的权重越高)
|
synergy_level_weight: 1.0
|
||||||
synergy_weights:
|
chess_weights:
|
||||||
# 职业羁绊
|
R-080T: 0
|
||||||
人造人: 1.4
|
T-43X: 0
|
||||||
召唤物: 0.7
|
T-URR37: 0
|
||||||
堡垒卫士: 1.4
|
乐芙兰: 1
|
||||||
强袭射手: 1.4
|
伊莉丝: 1
|
||||||
战略分析师: 1.1
|
佛耶戈: 1
|
||||||
斗士: 1.5
|
俄洛伊: 1
|
||||||
杀手: 1.4
|
克格莫: 1
|
||||||
裁决使: 1.4
|
加里奥: 1
|
||||||
超频战士: 1.6
|
劫: 1
|
||||||
迅捷射手: 1.5
|
千珏: 1
|
||||||
重装战士: 1.5
|
厄加特: 1
|
||||||
高级工程师: 1.5
|
厄斐琉斯: 1
|
||||||
|
厄运小姐: 1
|
||||||
# 特质羁绊
|
古拉加斯: 1
|
||||||
圣灵使者: 1.5
|
可酷伯: 1
|
||||||
幻灵战队: 1.4
|
吉格斯: 1
|
||||||
弑魂者: 1.6
|
嘉文四世: 1
|
||||||
战地机甲: 1.2
|
塞拉斯: 1
|
||||||
执事: 1.3
|
奈德丽: 1
|
||||||
源计划: 1.5
|
妮蔻: 1
|
||||||
病毒魔人: 1.0
|
婕拉: 1
|
||||||
福牛守护者: 1.2
|
安妮: 1
|
||||||
网络之神: 1.6
|
崔斯特: 1
|
||||||
街头恶魔: 1.4
|
布兰德: 1
|
||||||
赛博老大: 1.3
|
布隆: 1
|
||||||
辛迪加: 1.3
|
希瓦娜: 1
|
||||||
魔装机神: 1.1
|
德莱厄斯: 1
|
||||||
鳄霸: 1.6
|
德莱文: 1
|
||||||
|
悠米: 1
|
||||||
# 棋子费用等级权重(费用越高权重越大)
|
扎克: 1
|
||||||
|
拉亚斯特: 1
|
||||||
|
斯卡纳: 1
|
||||||
|
格雷福斯: 1
|
||||||
|
波比: 1
|
||||||
|
泽丽: 1
|
||||||
|
烬: 1
|
||||||
|
瑟庄妮: 1
|
||||||
|
盖伦: 1
|
||||||
|
科加斯: 1
|
||||||
|
纳亚菲利: 1
|
||||||
|
维迦: 1
|
||||||
|
艾克: 1
|
||||||
|
莎弥拉: 1
|
||||||
|
莫德凯撒: 1
|
||||||
|
莫甘娜: 1
|
||||||
|
萨勒芬妮: 1
|
||||||
|
萨科: 1
|
||||||
|
蒙多医生: 1
|
||||||
|
蔚: 1
|
||||||
|
蕾欧娜: 1
|
||||||
|
薇古丝: 1
|
||||||
|
薇恩: 1
|
||||||
|
费德提克: 1
|
||||||
|
贾克斯: 1
|
||||||
|
赛娜: 1
|
||||||
|
金克丝: 1
|
||||||
|
阿利斯塔: 1
|
||||||
|
阿萝拉: 1
|
||||||
|
雷克顿: 1
|
||||||
|
雷恩加尔: 1
|
||||||
|
霞: 1
|
||||||
|
韦鲁斯: 1
|
||||||
|
魔像: 0
|
||||||
cost_weights:
|
cost_weights:
|
||||||
'1': 1.0
|
'1': 1.0
|
||||||
'2': 1.2
|
'2': 1.2
|
||||||
'3': 1.5
|
'3': 1.5
|
||||||
'4': 1.8
|
'4': 1.8
|
||||||
'5': 2.0
|
'5': 2.0
|
||||||
|
|
||||||
# 棋子权重配置(值越大,该棋子在评分中的权重越高)
|
|
||||||
chess_weights:
|
|
||||||
R-080T: 0.8
|
|
||||||
T-43X: 0.8
|
|
||||||
T-URR37: 0.8
|
|
||||||
乐芙兰: 1.1
|
|
||||||
伊莉丝: 1.2
|
|
||||||
佛耶戈: 1.7
|
|
||||||
俄洛伊: 1.1
|
|
||||||
克格莫: 1.0
|
|
||||||
加里奥: 1.2
|
|
||||||
劫: 1.3
|
|
||||||
千珏: 1.0
|
|
||||||
厄加特: 1.4
|
|
||||||
厄斐琉斯: 1.3
|
|
||||||
厄运小姐: 1.3
|
|
||||||
古拉加斯: 1.2
|
|
||||||
可酷伯: 1.4
|
|
||||||
吉格斯: 1.3
|
|
||||||
嘉文四世: 1.2
|
|
||||||
塞拉斯: 1.0
|
|
||||||
奈德丽: 1.0
|
|
||||||
妮蔻: 1.3
|
|
||||||
婕拉: 1.0
|
|
||||||
安妮: 1.3
|
|
||||||
崔斯特: 1.1
|
|
||||||
布兰德: 1.5
|
|
||||||
布隆: 1.2
|
|
||||||
希瓦娜: 1.1
|
|
||||||
德莱厄斯: 1.1
|
|
||||||
德莱文: 1.2
|
|
||||||
悠米: 1.2
|
|
||||||
扎克: 1.7
|
|
||||||
拉亚斯特: 1.1
|
|
||||||
斯卡纳: 1.1
|
|
||||||
格雷福斯: 1.1
|
|
||||||
波比: 1.0
|
|
||||||
泽丽: 1.3
|
|
||||||
烬: 1.1
|
|
||||||
瑟庄妮: 1.3
|
|
||||||
盖伦: 1.7
|
|
||||||
科加斯: 1.3
|
|
||||||
纳亚菲利: 1.1
|
|
||||||
维迦: 1.1
|
|
||||||
艾克: 1.1
|
|
||||||
莎弥拉: 1.4
|
|
||||||
莫德凯撒: 1.2
|
|
||||||
莫甘娜: 1.0
|
|
||||||
萨勒芬妮: 1.0
|
|
||||||
萨科: 1.0
|
|
||||||
蒙多医生: 1.0
|
|
||||||
蔚: 1.0
|
|
||||||
蕾欧娜: 1.3
|
|
||||||
薇古丝: 1.3
|
|
||||||
薇恩: 1.5
|
|
||||||
费德提克: 1.2
|
|
||||||
贾克斯: 1.0
|
|
||||||
赛娜: 1.5
|
|
||||||
金克丝: 1.5
|
|
||||||
阿利斯塔: 1.0
|
|
||||||
阿萝拉: 1.7
|
|
||||||
雷克顿: 1.4
|
|
||||||
雷恩加尔: 1.2
|
|
||||||
霞: 1.3
|
|
||||||
韦鲁斯: 1.2
|
|
||||||
魔像: 0.8
|
|
||||||
|
|
||||||
# 羁绊等级权重(不同等级的羁绊权重不同)
|
|
||||||
synergy_level_weights:
|
synergy_level_weights:
|
||||||
'1': 1.0
|
'1': 1.0
|
||||||
|
'10': 3.5
|
||||||
'2': 1.2
|
'2': 1.2
|
||||||
'3': 1.5
|
'3': 1.5
|
||||||
'4': 1.8
|
'4': 1.8
|
||||||
@ -124,4 +84,30 @@ synergy_level_weights:
|
|||||||
'7': 2.6
|
'7': 2.6
|
||||||
'8': 3.0
|
'8': 3.0
|
||||||
'9': 3.3
|
'9': 3.3
|
||||||
'10': 3.5
|
synergy_weights:
|
||||||
|
人造人: 1
|
||||||
|
召唤物: 0
|
||||||
|
圣灵使者: 1
|
||||||
|
堡垒卫士: 1
|
||||||
|
幻灵战队: 1
|
||||||
|
弑魂者: 1
|
||||||
|
强袭射手: 1
|
||||||
|
战地机甲: 1
|
||||||
|
战略分析师: 1
|
||||||
|
执事: 1
|
||||||
|
斗士: 1
|
||||||
|
杀手: 1
|
||||||
|
源计划: 1
|
||||||
|
病毒魔人: 1
|
||||||
|
福牛守护者: 1
|
||||||
|
网络之神: 1
|
||||||
|
街头恶魔: 1
|
||||||
|
裁决使: 1
|
||||||
|
赛博老大: 1
|
||||||
|
超频战士: 1
|
||||||
|
辛迪加: 1
|
||||||
|
迅捷射手: 1
|
||||||
|
重装战士: 1
|
||||||
|
高级工程师: 1
|
||||||
|
魔装机神: 1
|
||||||
|
鳄霸: 1
|
||||||
|
86
main.py
86
main.py
@ -13,6 +13,7 @@ from src.interface.cli import main as cli_main
|
|||||||
from src.scoring.scoring_system import TeamScorer
|
from src.scoring.scoring_system import TeamScorer
|
||||||
from src.test_scoring import main as test_scoring
|
from src.test_scoring import main as test_scoring
|
||||||
from src.web import run_server # 导入Web模块
|
from src.web import run_server # 导入Web模块
|
||||||
|
from src.config import get_global_weights_config
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -33,6 +34,7 @@ def main():
|
|||||||
|
|
||||||
# 阵容推荐模块演示
|
# 阵容推荐模块演示
|
||||||
recommend_parser = subparsers.add_parser("recommend", help="阵容推荐模块演示")
|
recommend_parser = subparsers.add_parser("recommend", help="阵容推荐模块演示")
|
||||||
|
recommend_parser.add_argument("--config", type=str, help="指定配置文件路径")
|
||||||
|
|
||||||
# 命令行界面
|
# 命令行界面
|
||||||
cli_parser = subparsers.add_parser("cli", help="交互式命令行界面")
|
cli_parser = subparsers.add_parser("cli", help="交互式命令行界面")
|
||||||
@ -41,16 +43,26 @@ def main():
|
|||||||
cli_parser.add_argument("--level-weight", type=float, help="羁绊等级权重")
|
cli_parser.add_argument("--level-weight", type=float, help="羁绊等级权重")
|
||||||
cli_parser.add_argument("--count-weight", type=float, help="羁绊数量权重")
|
cli_parser.add_argument("--count-weight", type=float, help="羁绊数量权重")
|
||||||
cli_parser.add_argument("--cost-weight", type=float, help="棋子费用权重")
|
cli_parser.add_argument("--cost-weight", type=float, help="棋子费用权重")
|
||||||
|
cli_parser.add_argument("--config", type=str, help="指定配置文件路径")
|
||||||
|
|
||||||
# 评分模块测试 (新增)
|
# 评分模块测试
|
||||||
scoring_parser = subparsers.add_parser("scoring", help="评分模块测试")
|
scoring_parser = subparsers.add_parser("scoring", help="评分模块测试")
|
||||||
scoring_parser.add_argument("--config", type=str, help="指定配置文件路径")
|
scoring_parser.add_argument("--config", type=str, help="指定配置文件路径")
|
||||||
|
|
||||||
# Web界面 (新增)
|
# 权重配置管理(新增)
|
||||||
|
config_parser = subparsers.add_parser("config", help="权重配置管理")
|
||||||
|
config_parser.add_argument("--show", action="store_true", help="显示当前权重配置")
|
||||||
|
config_parser.add_argument("--set-synergy", nargs=2, metavar=("NAME", "WEIGHT"), help="设置羁绊权重")
|
||||||
|
config_parser.add_argument("--set-chess", nargs=2, metavar=("NAME", "WEIGHT"), help="设置棋子权重")
|
||||||
|
config_parser.add_argument("--set-base", nargs=2, metavar=("PARAM", "WEIGHT"), help="设置基础权重参数")
|
||||||
|
config_parser.add_argument("--config", type=str, help="指定配置文件路径")
|
||||||
|
|
||||||
|
# Web界面
|
||||||
web_parser = subparsers.add_parser("web", help="启动Web界面")
|
web_parser = subparsers.add_parser("web", help="启动Web界面")
|
||||||
web_parser.add_argument("--host", type=str, default="0.0.0.0", help="服务器主机地址")
|
web_parser.add_argument("--host", type=str, default="0.0.0.0", help="服务器主机地址")
|
||||||
web_parser.add_argument("--port", type=int, default=5000, help="服务器端口")
|
web_parser.add_argument("--port", type=int, default=5000, help="服务器端口")
|
||||||
web_parser.add_argument("--dev", action="store_true", help="开发模式")
|
web_parser.add_argument("--dev", action="store_true", help="开发模式")
|
||||||
|
web_parser.add_argument("--config", type=str, help="指定配置文件路径")
|
||||||
|
|
||||||
# 解析命令行参数
|
# 解析命令行参数
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@ -64,7 +76,7 @@ def main():
|
|||||||
elif args.command == "recommend":
|
elif args.command == "recommend":
|
||||||
logger.info("启动阵容推荐模块演示")
|
logger.info("启动阵容推荐模块演示")
|
||||||
# 调用阵容推荐模块演示函数
|
# 调用阵容推荐模块演示函数
|
||||||
recommendation_demo()
|
recommendation_demo(config_path=getattr(args, 'config', None))
|
||||||
|
|
||||||
elif args.command == "cli":
|
elif args.command == "cli":
|
||||||
logger.info("启动交互式命令行界面")
|
logger.info("启动交互式命令行界面")
|
||||||
@ -74,17 +86,70 @@ def main():
|
|||||||
results=args.results,
|
results=args.results,
|
||||||
level_weight=args.level_weight,
|
level_weight=args.level_weight,
|
||||||
count_weight=args.count_weight,
|
count_weight=args.count_weight,
|
||||||
cost_weight=args.cost_weight
|
cost_weight=args.cost_weight,
|
||||||
|
config_path=getattr(args, 'config', None)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif args.command == "scoring":
|
elif args.command == "scoring":
|
||||||
logger.info("启动评分模块测试")
|
logger.info("启动评分模块测试")
|
||||||
if args.config:
|
test_scoring(config_path=getattr(args, 'config', None))
|
||||||
# 如果指定了配置文件路径,使用该路径进行测试
|
|
||||||
test_scoring(config_path=args.config)
|
elif args.command == "config":
|
||||||
|
# 获取权重配置
|
||||||
|
config_path = getattr(args, 'config', None)
|
||||||
|
weights_config = get_global_weights_config(config_path)
|
||||||
|
|
||||||
|
if args.show:
|
||||||
|
# 显示当前配置
|
||||||
|
print("\n=== 当前权重配置 ===")
|
||||||
|
|
||||||
|
# 基础权重
|
||||||
|
print("\n基础权重:")
|
||||||
|
for key, value in weights_config.get_base_weights().items():
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
|
||||||
|
# 羁绊权重
|
||||||
|
print("\n羁绊权重:")
|
||||||
|
for key, value in sorted(weights_config.get_synergy_weights().items()):
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
|
||||||
|
# 棋子权重
|
||||||
|
print("\n棋子权重:")
|
||||||
|
for key, value in sorted(weights_config.get_chess_weights().items()):
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
|
||||||
|
# 羁绊等级权重
|
||||||
|
print("\n羁绊等级权重:")
|
||||||
|
for key, value in sorted(weights_config.get_synergy_level_weights().items(),
|
||||||
|
key=lambda x: int(x[0])):
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
|
||||||
|
# 棋子费用权重
|
||||||
|
print("\n棋子费用权重:")
|
||||||
|
for key, value in sorted(weights_config.get_cost_weights().items(),
|
||||||
|
key=lambda x: int(x[0])):
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
|
||||||
|
elif args.set_synergy:
|
||||||
|
# 设置羁绊权重
|
||||||
|
name, weight = args.set_synergy
|
||||||
|
weights_config.set_synergy_weight(name, float(weight))
|
||||||
|
print(f"已设置羁绊 [{name}] 的权重为 {weight}")
|
||||||
|
|
||||||
|
elif args.set_chess:
|
||||||
|
# 设置棋子权重
|
||||||
|
name, weight = args.set_chess
|
||||||
|
weights_config.set_chess_weight(name, float(weight))
|
||||||
|
print(f"已设置棋子 [{name}] 的权重为 {weight}")
|
||||||
|
|
||||||
|
elif args.set_base:
|
||||||
|
# 设置基础权重参数
|
||||||
|
param, weight = args.set_base
|
||||||
|
weights_config.set_base_weight(param, float(weight))
|
||||||
|
print(f"已设置基础权重参数 [{param}] 的值为 {weight}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# 否则使用默认配置文件
|
config_parser.print_help()
|
||||||
test_scoring()
|
|
||||||
|
|
||||||
elif args.command == "web":
|
elif args.command == "web":
|
||||||
logger.info("启动Web界面")
|
logger.info("启动Web界面")
|
||||||
@ -92,7 +157,8 @@ def main():
|
|||||||
run_server(
|
run_server(
|
||||||
host=args.host,
|
host=args.host,
|
||||||
port=args.port,
|
port=args.port,
|
||||||
dev_mode=args.dev
|
dev_mode=args.dev,
|
||||||
|
config_path=getattr(args, 'config', None)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
11
src/config/__init__.py
Normal file
11
src/config/__init__.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
全局配置模块 - 提供整个应用的配置管理
|
||||||
|
|
||||||
|
此模块包含应用程序的全局配置管理功能,包括:
|
||||||
|
1. 权重配置的加载和管理
|
||||||
|
2. 全局参数的设置和获取
|
||||||
|
"""
|
||||||
|
|
||||||
|
from src.config.weights_config import WeightsConfig, get_global_weights_config
|
||||||
|
|
||||||
|
__all__ = ["WeightsConfig", "get_global_weights_config"]
|
235
src/config/weights_config.py
Normal file
235
src/config/weights_config.py
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
"""
|
||||||
|
权重配置管理器 - 用于从外部配置文件加载并管理全局权重设置
|
||||||
|
|
||||||
|
此模块提供了从YAML或JSON配置文件中加载自定义权重设置的功能,
|
||||||
|
支持为特定羁绊和棋子设置自定义权重,并在整个应用程序中提供统一的权重配置。
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import yaml
|
||||||
|
import logging
|
||||||
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
|
# 配置日志
|
||||||
|
logger = logging.getLogger("TFT-Strategist-WeightsConfig")
|
||||||
|
|
||||||
|
# 全局权重配置实例
|
||||||
|
_global_weights_config = None
|
||||||
|
|
||||||
|
class WeightsConfig:
|
||||||
|
"""
|
||||||
|
权重配置管理器,用于从外部文件加载自定义权重设置
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, config_path: Optional[str] = None):
|
||||||
|
"""
|
||||||
|
初始化权重配置管理器
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path: 配置文件路径,如果为None则使用默认路径
|
||||||
|
"""
|
||||||
|
self.config_path = config_path or os.path.join("data", "weights_config.yaml")
|
||||||
|
self.config_data = {}
|
||||||
|
# 自动加载配置文件
|
||||||
|
self.load_config()
|
||||||
|
|
||||||
|
def load_config(self) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
加载配置文件
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: 包含配置数据的字典
|
||||||
|
"""
|
||||||
|
if not os.path.exists(self.config_path):
|
||||||
|
logger.warning(f"配置文件不存在: {self.config_path}")
|
||||||
|
# 如果文件不存在,创建默认配置
|
||||||
|
self._create_default_config()
|
||||||
|
return self.config_data
|
||||||
|
|
||||||
|
try:
|
||||||
|
file_ext = os.path.splitext(self.config_path)[1].lower()
|
||||||
|
|
||||||
|
if file_ext == '.json':
|
||||||
|
with open(self.config_path, 'r', encoding='utf-8') as f:
|
||||||
|
self.config_data = json.load(f)
|
||||||
|
elif file_ext in ['.yaml', '.yml']:
|
||||||
|
with open(self.config_path, 'r', encoding='utf-8') as f:
|
||||||
|
self.config_data = yaml.safe_load(f)
|
||||||
|
else:
|
||||||
|
logger.error(f"不支持的配置文件格式: {file_ext}")
|
||||||
|
self._create_default_config()
|
||||||
|
return self.config_data
|
||||||
|
|
||||||
|
logger.info(f"成功加载配置文件: {self.config_path}")
|
||||||
|
return self.config_data
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"加载配置文件失败: {str(e)}")
|
||||||
|
# 如果加载失败,创建默认配置
|
||||||
|
self._create_default_config()
|
||||||
|
return self.config_data
|
||||||
|
|
||||||
|
def get_synergy_weights(self) -> Dict[str, float]:
|
||||||
|
"""
|
||||||
|
获取羁绊权重配置
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, float]: 羁绊名称到权重的映射
|
||||||
|
"""
|
||||||
|
return self.config_data.get('synergy_weights', {})
|
||||||
|
|
||||||
|
def get_chess_weights(self) -> Dict[str, float]:
|
||||||
|
"""
|
||||||
|
获取棋子权重配置
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, float]: 棋子名称到权重的映射
|
||||||
|
"""
|
||||||
|
return self.config_data.get('chess_weights', {})
|
||||||
|
|
||||||
|
def get_base_weights(self) -> Dict[str, float]:
|
||||||
|
"""
|
||||||
|
获取基础权重配置
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
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 set_synergy_weight(self, synergy_name: str, weight: float) -> None:
|
||||||
|
"""
|
||||||
|
设置特定羁绊的权重
|
||||||
|
|
||||||
|
Args:
|
||||||
|
synergy_name: 羁绊名称
|
||||||
|
weight: 权重值
|
||||||
|
"""
|
||||||
|
if 'synergy_weights' not in self.config_data:
|
||||||
|
self.config_data['synergy_weights'] = {}
|
||||||
|
self.config_data['synergy_weights'][synergy_name] = weight
|
||||||
|
self._save_config()
|
||||||
|
|
||||||
|
def set_chess_weight(self, chess_name: str, weight: float) -> None:
|
||||||
|
"""
|
||||||
|
设置特定棋子的权重
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chess_name: 棋子名称
|
||||||
|
weight: 权重值
|
||||||
|
"""
|
||||||
|
if 'chess_weights' not in self.config_data:
|
||||||
|
self.config_data['chess_weights'] = {}
|
||||||
|
self.config_data['chess_weights'][chess_name] = weight
|
||||||
|
self._save_config()
|
||||||
|
|
||||||
|
def set_base_weight(self, param_name: str, weight: float) -> None:
|
||||||
|
"""
|
||||||
|
设置基础权重参数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
param_name: 参数名称
|
||||||
|
weight: 权重值
|
||||||
|
"""
|
||||||
|
if 'base_weights' not in self.config_data:
|
||||||
|
self.config_data['base_weights'] = {}
|
||||||
|
self.config_data['base_weights'][param_name] = weight
|
||||||
|
self._save_config()
|
||||||
|
|
||||||
|
def _save_config(self) -> None:
|
||||||
|
"""保存配置到文件"""
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
|
||||||
|
file_ext = os.path.splitext(self.config_path)[1].lower()
|
||||||
|
|
||||||
|
if file_ext == '.json':
|
||||||
|
with open(self.config_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(self.config_data, f, ensure_ascii=False, indent=2)
|
||||||
|
elif file_ext in ['.yaml', '.yml']:
|
||||||
|
with open(self.config_path, 'w', encoding='utf-8') as f:
|
||||||
|
yaml.dump(self.config_data, f, allow_unicode=True, default_flow_style=False)
|
||||||
|
else:
|
||||||
|
logger.error(f"不支持的配置文件格式: {file_ext}")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f"成功保存配置文件: {self.config_path}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"保存配置文件失败: {str(e)}")
|
||||||
|
|
||||||
|
def _create_default_config(self) -> None:
|
||||||
|
"""创建默认配置文件"""
|
||||||
|
default_config = {
|
||||||
|
'base_weights': {
|
||||||
|
'synergy_level_weight': 1.0,
|
||||||
|
'synergy_count_weight': 0.5,
|
||||||
|
'chess_cost_weight': 0.1
|
||||||
|
},
|
||||||
|
'synergy_weights': {
|
||||||
|
'重装战士': 1.5,
|
||||||
|
'魔法师': 1.2,
|
||||||
|
'神谕者': 1.3,
|
||||||
|
'斗士': 1.1
|
||||||
|
},
|
||||||
|
'chess_weights': {
|
||||||
|
'亚索': 1.5,
|
||||||
|
'艾希': 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config_data = default_config
|
||||||
|
self._save_config()
|
||||||
|
|
||||||
|
|
||||||
|
def get_global_weights_config(config_path: Optional[str] = None) -> WeightsConfig:
|
||||||
|
"""
|
||||||
|
获取全局权重配置实例
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path: 配置文件路径,如果为None则使用默认路径
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
WeightsConfig: 全局权重配置实例
|
||||||
|
"""
|
||||||
|
global _global_weights_config
|
||||||
|
if _global_weights_config is None:
|
||||||
|
_global_weights_config = WeightsConfig(config_path)
|
||||||
|
return _global_weights_config
|
@ -16,6 +16,7 @@ import logging
|
|||||||
from src.data_provider import DataQueryAPI
|
from src.data_provider import DataQueryAPI
|
||||||
from src.recommendation import RecommendationEngine
|
from src.recommendation import RecommendationEngine
|
||||||
from src.scoring import TeamScorer, ScoringConfig
|
from src.scoring import TeamScorer, ScoringConfig
|
||||||
|
from src.config import get_global_weights_config
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -28,11 +29,20 @@ logger = logging.getLogger("TFT-Strategist-CLI")
|
|||||||
class TFTCommandLine:
|
class TFTCommandLine:
|
||||||
"""云顶之弈阵容推荐命令行接口"""
|
"""云顶之弈阵容推荐命令行接口"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, config_path: Optional[str] = None):
|
||||||
"""初始化命令行接口"""
|
"""
|
||||||
|
初始化命令行接口
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path: 配置文件路径,用于全局权重配置
|
||||||
|
"""
|
||||||
|
# 加载全局权重配置
|
||||||
|
self.weights_config = get_global_weights_config(config_path)
|
||||||
|
|
||||||
|
# 初始化API和推荐引擎
|
||||||
self.api = DataQueryAPI()
|
self.api = DataQueryAPI()
|
||||||
self.recommendation_engine = RecommendationEngine(api=self.api)
|
self.scorer = TeamScorer(config_path=config_path)
|
||||||
self.scorer = TeamScorer()
|
self.recommendation_engine = RecommendationEngine(api=self.api, scorer=self.scorer, config_path=config_path)
|
||||||
|
|
||||||
def run(self, population=None, results=None, level_weight=None, count_weight=None, cost_weight=None):
|
def run(self, population=None, results=None, level_weight=None, count_weight=None, cost_weight=None):
|
||||||
"""
|
"""
|
||||||
@ -62,12 +72,16 @@ class TFTCommandLine:
|
|||||||
count_weight = count_weight or args.count_weight
|
count_weight = count_weight or args.count_weight
|
||||||
cost_weight = cost_weight or args.cost_weight
|
cost_weight = cost_weight or args.cost_weight
|
||||||
|
|
||||||
# 自定义评分权重
|
# 自定义评分权重,同时更新全局配置
|
||||||
self.scorer.customize_scoring(
|
if level_weight is not None:
|
||||||
synergy_level_weight=level_weight,
|
self.weights_config.set_base_weight('synergy_level_weight', level_weight)
|
||||||
synergy_count_weight=count_weight,
|
if count_weight is not None:
|
||||||
chess_cost_weight=cost_weight
|
self.weights_config.set_base_weight('synergy_count_weight', count_weight)
|
||||||
)
|
if cost_weight is not None:
|
||||||
|
self.weights_config.set_base_weight('chess_cost_weight', cost_weight)
|
||||||
|
|
||||||
|
# 更新评分系统的配置
|
||||||
|
self.scorer.reload_config()
|
||||||
|
|
||||||
# 初始化阵容参数
|
# 初始化阵容参数
|
||||||
required_synergies = []
|
required_synergies = []
|
||||||
@ -78,6 +92,13 @@ class TFTCommandLine:
|
|||||||
print(f"当前游戏版本: {version}")
|
print(f"当前游戏版本: {version}")
|
||||||
print(f"阵容人口: {population}")
|
print(f"阵容人口: {population}")
|
||||||
|
|
||||||
|
# 显示当前权重配置
|
||||||
|
base_weights = self.weights_config.get_base_weights()
|
||||||
|
print("\n当前评分权重配置:")
|
||||||
|
print(f" 羁绊等级权重: {base_weights.get('synergy_level_weight', 1.0)}")
|
||||||
|
print(f" 羁绊数量权重: {base_weights.get('synergy_count_weight', 0.5)}")
|
||||||
|
print(f" 棋子费用权重: {base_weights.get('chess_cost_weight', 0.1)}")
|
||||||
|
|
||||||
# 交互式添加必选羁绊
|
# 交互式添加必选羁绊
|
||||||
print("\n-- 添加必选羁绊 --")
|
print("\n-- 添加必选羁绊 --")
|
||||||
while True:
|
while True:
|
||||||
@ -113,6 +134,22 @@ class TFTCommandLine:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
print("请输入数字")
|
print("请输入数字")
|
||||||
|
|
||||||
|
# 显示羁绊在配置中的权重
|
||||||
|
synergy_weight = self.weights_config.get_synergy_weights().get(synergy_name, 1.0)
|
||||||
|
print(f"羁绊 {synergy_name} 的当前权重为: {synergy_weight}")
|
||||||
|
|
||||||
|
# 可选:调整羁绊权重
|
||||||
|
adjust = input(f"是否调整该羁绊的权重?(y/n,当前: {synergy_weight}): ").strip().lower()
|
||||||
|
if adjust == 'y':
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
new_weight = float(input(f"请输入新权重值 (当前: {synergy_weight}): ").strip())
|
||||||
|
self.weights_config.set_synergy_weight(synergy_name, new_weight)
|
||||||
|
print(f"已将羁绊 {synergy_name} 的权重设置为 {new_weight}")
|
||||||
|
break
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
required_synergies.append({
|
required_synergies.append({
|
||||||
'name': synergy_name,
|
'name': synergy_name,
|
||||||
'level': level
|
'level': level
|
||||||
@ -130,6 +167,22 @@ class TFTCommandLine:
|
|||||||
if not chess:
|
if not chess:
|
||||||
print(f"未找到棋子: {chess_name}")
|
print(f"未找到棋子: {chess_name}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 显示棋子在配置中的权重
|
||||||
|
chess_weight = self.weights_config.get_chess_weights().get(chess_name, 1.0)
|
||||||
|
print(f"棋子 {chess_name} 的当前权重为: {chess_weight}")
|
||||||
|
|
||||||
|
# 可选:调整棋子权重
|
||||||
|
adjust = input(f"是否调整该棋子的权重?(y/n,当前: {chess_weight}): ").strip().lower()
|
||||||
|
if adjust == 'y':
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
new_weight = float(input(f"请输入新权重值 (当前: {chess_weight}): ").strip())
|
||||||
|
self.weights_config.set_chess_weight(chess_name, new_weight)
|
||||||
|
print(f"已将棋子 {chess_name} 的权重设置为 {new_weight}")
|
||||||
|
break
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
required_chess.append({
|
required_chess.append({
|
||||||
'name': chess_name
|
'name': chess_name
|
||||||
@ -192,7 +245,7 @@ class TFTCommandLine:
|
|||||||
print("\n------------------------")
|
print("\n------------------------")
|
||||||
|
|
||||||
|
|
||||||
def main(population=None, results=None, level_weight=None, count_weight=None, cost_weight=None):
|
def main(population=None, results=None, level_weight=None, count_weight=None, cost_weight=None, config_path=None):
|
||||||
"""
|
"""
|
||||||
主函数
|
主函数
|
||||||
|
|
||||||
@ -202,26 +255,18 @@ def main(population=None, results=None, level_weight=None, count_weight=None, co
|
|||||||
level_weight (float, optional): 羁绊等级权重
|
level_weight (float, optional): 羁绊等级权重
|
||||||
count_weight (float, optional): 羁绊数量权重
|
count_weight (float, optional): 羁绊数量权重
|
||||||
cost_weight (float, optional): 棋子费用权重
|
cost_weight (float, optional): 棋子费用权重
|
||||||
|
config_path (str, optional): 配置文件路径
|
||||||
Returns:
|
|
||||||
int: 退出码
|
|
||||||
"""
|
"""
|
||||||
cli = TFTCommandLine()
|
|
||||||
try:
|
try:
|
||||||
cli.run(
|
cli = TFTCommandLine(config_path=config_path)
|
||||||
population=population,
|
cli.run(population, results, level_weight, count_weight, cost_weight)
|
||||||
results=results,
|
return 0
|
||||||
level_weight=level_weight,
|
|
||||||
count_weight=count_weight,
|
|
||||||
cost_weight=cost_weight
|
|
||||||
)
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\n程序已退出")
|
print("\n操作被用户中断")
|
||||||
except Exception as e:
|
return 1
|
||||||
logger.error(f"发生错误: {e}", exc_info=True)
|
except Exception as e:
|
||||||
print(f"\n程序发生错误: {e}")
|
logger.error(f"发生错误: {str(e)}")
|
||||||
return 1
|
return 1
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -12,6 +12,7 @@ import itertools
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from src.data_provider import DataQueryAPI
|
from src.data_provider import DataQueryAPI
|
||||||
from src.scoring.scoring_system import TeamScorer
|
from src.scoring.scoring_system import TeamScorer
|
||||||
|
from src.config import get_global_weights_config
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -109,16 +110,26 @@ class RecommendationEngine:
|
|||||||
阵容推荐引擎,负责根据用户需求生成最优阵容
|
阵容推荐引擎,负责根据用户需求生成最优阵容
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, api: Optional[DataQueryAPI] = None, scorer: Optional[TeamScorer] = None):
|
def __init__(self, api: DataQueryAPI, scorer: TeamScorer, config_path: Optional[str] = None, config_obj: Optional[Dict[str, Any]] = None):
|
||||||
"""
|
"""
|
||||||
初始化阵容推荐引擎
|
初始化阵容推荐引擎
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
api: 数据查询API实例,如果为None则创建一个新的实例
|
api: 数据查询API实例,如果为None则创建一个新的实例
|
||||||
scorer: 阵容评分系统实例,如果为None则创建一个新的实例
|
scorer: 阵容评分系统实例,如果为None则创建一个新的实例
|
||||||
|
config_path: 配置文件路径,用于全局权重配置
|
||||||
|
config_obj: 配置对象,优先于config_path使用
|
||||||
"""
|
"""
|
||||||
self.api = api if api else DataQueryAPI()
|
self.api = api if api else DataQueryAPI()
|
||||||
self.scorer = scorer if scorer else TeamScorer()
|
|
||||||
|
# 加载全局权重配置
|
||||||
|
if config_obj:
|
||||||
|
self.weights_config = config_obj
|
||||||
|
else:
|
||||||
|
self.weights_config = get_global_weights_config(config_path).config_data
|
||||||
|
|
||||||
|
# 创建阵容评分系统
|
||||||
|
self.scorer = scorer if scorer else TeamScorer(config_path=config_path)
|
||||||
|
|
||||||
def recommend_team(
|
def recommend_team(
|
||||||
self,
|
self,
|
||||||
@ -198,27 +209,37 @@ class RecommendationEngine:
|
|||||||
break
|
break
|
||||||
|
|
||||||
if target_level == 0:
|
if target_level == 0:
|
||||||
target_level = min_level
|
logger.warning(f"羁绊 {synergy.get('name', '')} 找不到满足最低等级 {min_level} 的激活条件")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 考虑羁绊在全局配置中的权重
|
||||||
|
weight = self.weights_config.get('synergy_weights', {}).get(synergy.get('name', ''), 1.0)
|
||||||
|
|
||||||
synergy_chess_sets.append({
|
synergy_chess_sets.append({
|
||||||
'synergy': synergy,
|
'synergy': synergy,
|
||||||
'chess_list': chess_list,
|
'chess_list': chess_list,
|
||||||
'target_level': target_level
|
'target_level': target_level,
|
||||||
|
'weight': weight # 添加权重信息
|
||||||
})
|
})
|
||||||
|
|
||||||
# 3. 生成候选阵容
|
# 3. 根据已选棋子和羁绊要求生成候选阵容
|
||||||
candidate_teams = self._generate_candidate_teams(
|
candidate_teams = self._generate_candidate_teams(base_team, synergy_chess_sets, population)
|
||||||
base_team, synergy_chess_sets, population
|
|
||||||
)
|
|
||||||
|
|
||||||
# 4. 评分排序并返回结果
|
# 如果没有生成候选阵容,则尝试填充最佳棋子
|
||||||
|
if not candidate_teams:
|
||||||
|
remaining_slots = population - base_team.size
|
||||||
|
logger.info(f"未找到满足所有羁绊要求的阵容,尝试填充最佳棋子 (剩余槽位: {remaining_slots})")
|
||||||
|
candidate_teams = self._fill_team_with_best_chess(base_team, remaining_slots)
|
||||||
|
|
||||||
|
# 4. 计算每个阵容的评分并排序
|
||||||
for team in candidate_teams:
|
for team in candidate_teams:
|
||||||
team.calculate_synergies(self.api)
|
team.calculate_synergies(self.api)
|
||||||
team.score = self.scorer.score_team(team)
|
team.score = self.scorer.score_team(team)
|
||||||
|
|
||||||
# 按评分从高到低排序
|
# 按分数从高到低排序
|
||||||
candidate_teams.sort(key=lambda x: x.score, reverse=True)
|
candidate_teams.sort(key=lambda t: t.score, reverse=True)
|
||||||
|
|
||||||
|
# 返回分数最高的几个阵容
|
||||||
return candidate_teams[:max_results]
|
return candidate_teams[:max_results]
|
||||||
|
|
||||||
def _generate_candidate_teams(
|
def _generate_candidate_teams(
|
||||||
@ -228,107 +249,105 @@ class RecommendationEngine:
|
|||||||
population: int
|
population: int
|
||||||
) -> List[TeamComposition]:
|
) -> List[TeamComposition]:
|
||||||
"""
|
"""
|
||||||
生成候选阵容
|
根据羁绊要求生成候选阵容
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
base_team: 基础阵容,包含必选棋子
|
base_team: 基础阵容,包含必选棋子
|
||||||
synergy_chess_sets: 各个必选羁绊的棋子集合
|
synergy_chess_sets: 羁绊需要的棋子集合列表
|
||||||
population: 人口限制
|
population: 人口限制
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[TeamComposition]: 候选阵容列表
|
List[TeamComposition]: 候选阵容列表
|
||||||
"""
|
"""
|
||||||
# 已经有基础棋子
|
# 如果没有羁绊要求,则直接填充最佳棋子
|
||||||
remaining_slots = population - base_team.size
|
|
||||||
|
|
||||||
# 如果没有必选羁绊,则直接从所有棋子中选择
|
|
||||||
if not synergy_chess_sets:
|
if not synergy_chess_sets:
|
||||||
|
remaining_slots = population - base_team.size
|
||||||
return self._fill_team_with_best_chess(base_team, remaining_slots)
|
return self._fill_team_with_best_chess(base_team, remaining_slots)
|
||||||
|
|
||||||
# 处理必选羁绊
|
# 排序羁绊集合,根据权重值从高到低排序
|
||||||
|
synergy_chess_sets.sort(key=lambda s: s.get('weight', 1.0), reverse=True)
|
||||||
|
|
||||||
|
# 开始构建组合
|
||||||
candidate_teams = []
|
candidate_teams = []
|
||||||
|
|
||||||
# 为每个羁绊选择合适的棋子
|
# 记录已经在基础阵容中的棋子
|
||||||
synergy_combinations = []
|
base_chess_names = {chess.get('displayName') for chess in base_team.chess_list}
|
||||||
|
|
||||||
|
# 1. 从每个羁绊中选择需要的棋子数量
|
||||||
|
# 创建每个羁绊至少需要的棋子数组合
|
||||||
|
min_chess_options = []
|
||||||
for synergy_set in synergy_chess_sets:
|
for synergy_set in synergy_chess_sets:
|
||||||
synergy = synergy_set['synergy']
|
|
||||||
chess_list = synergy_set['chess_list']
|
chess_list = synergy_set['chess_list']
|
||||||
target_level = synergy_set['target_level']
|
target_level = synergy_set['target_level']
|
||||||
|
|
||||||
# 过滤掉已经在基础阵容中的棋子
|
# 计算需要添加的棋子数量
|
||||||
available_chess = [
|
already_in_base = sum(1 for chess in base_team.chess_list
|
||||||
chess for chess in chess_list
|
if any(job_id in chess.get('jobIds', '').split(',')
|
||||||
if chess not in base_team.chess_list
|
for job_id in [synergy_set['synergy'].get('jobId')])
|
||||||
]
|
or any(race_id in chess.get('raceIds', '').split(',')
|
||||||
|
for race_id in [synergy_set['synergy'].get('raceId')]))
|
||||||
|
|
||||||
# 计算还需要多少个该羁绊的棋子
|
needed_count = max(0, target_level - already_in_base)
|
||||||
synergy_id = synergy.get('jobId') or synergy.get('raceId')
|
if needed_count <= 0:
|
||||||
|
# 如果基础阵容已经满足该羁绊要求,则跳过
|
||||||
# 统计基础阵容中有多少个该羁绊的棋子
|
|
||||||
base_count = 0
|
|
||||||
for chess in base_team.chess_list:
|
|
||||||
job_ids = chess.get('jobIds', '').split(',')
|
|
||||||
race_ids = chess.get('raceIds', '').split(',')
|
|
||||||
if synergy_id in job_ids or synergy_id in race_ids:
|
|
||||||
base_count += 1
|
|
||||||
|
|
||||||
# 还需要的棋子数量
|
|
||||||
needed_count = max(0, target_level - base_count)
|
|
||||||
|
|
||||||
# 如果该羁绊已经满足要求,跳过
|
|
||||||
if needed_count == 0:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 从该羁绊的棋子中选择还未在基础阵容中的
|
||||||
|
available_chess = [chess for chess in chess_list
|
||||||
|
if chess.get('displayName') not in base_chess_names]
|
||||||
|
|
||||||
# 如果可用棋子不足,则尽可能多选
|
# 如果可用棋子不足以满足羁绊要求,则返回空列表
|
||||||
if len(available_chess) < needed_count:
|
if len(available_chess) < needed_count:
|
||||||
needed_count = len(available_chess)
|
logger.warning(f"羁绊 {synergy_set['synergy'].get('name', '')} 可用棋子不足,无法满足等级 {target_level} 的要求")
|
||||||
|
return []
|
||||||
|
|
||||||
# 生成该羁绊的所有可能组合
|
# 所有可能的棋子组合
|
||||||
combinations = list(itertools.combinations(available_chess, needed_count))
|
chess_combinations = list(itertools.combinations(available_chess, needed_count))
|
||||||
synergy_combinations.append(combinations)
|
|
||||||
|
# 将权重因素纳入棋子选择决策
|
||||||
|
weighted_combinations = []
|
||||||
|
for combo in chess_combinations:
|
||||||
|
# 计算组合的权重分数
|
||||||
|
combo_weight = sum(self.weights_config.get('chess_weights', {}).get(chess.get('displayName', ''), 1.0) for chess in combo)
|
||||||
|
weighted_combinations.append((combo, combo_weight))
|
||||||
|
|
||||||
|
# 按权重从高到低排序
|
||||||
|
weighted_combinations.sort(key=lambda x: x[1], reverse=True)
|
||||||
|
|
||||||
|
# 选择权重较高的前几个组合
|
||||||
|
top_combinations = [combo for combo, _ in weighted_combinations[:min(5, len(weighted_combinations))]]
|
||||||
|
min_chess_options.append(top_combinations)
|
||||||
|
|
||||||
# 如果没有有效的羁绊组合,则直接填充最好的棋子
|
# 如果某个羁绊无法满足,则返回空列表
|
||||||
if not synergy_combinations:
|
if not all(min_chess_options):
|
||||||
return self._fill_team_with_best_chess(base_team, remaining_slots)
|
return []
|
||||||
|
|
||||||
# 生成所有羁绊组合的笛卡尔积
|
# 2. 尝试组合不同羁绊的棋子,生成可行阵容
|
||||||
for combo_product in itertools.product(*synergy_combinations):
|
# 为每个羁绊选择一种棋子组合方式
|
||||||
# 扁平化组合成一个列表
|
for chess_selection in itertools.product(*min_chess_options):
|
||||||
flat_combo = []
|
# 创建新的候选阵容
|
||||||
for combo in combo_product:
|
candidate = TeamComposition()
|
||||||
flat_combo.extend(combo)
|
# 添加基础阵容的棋子
|
||||||
|
|
||||||
# 去重
|
|
||||||
unique_combo = []
|
|
||||||
for chess in flat_combo:
|
|
||||||
if chess not in unique_combo:
|
|
||||||
unique_combo.append(chess)
|
|
||||||
|
|
||||||
# 如果组合后的棋子数量超过剩余槽位,跳过
|
|
||||||
if len(unique_combo) > remaining_slots:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 创建新的阵容
|
|
||||||
new_team = TeamComposition()
|
|
||||||
for chess in base_team.chess_list:
|
for chess in base_team.chess_list:
|
||||||
new_team.add_chess(chess)
|
candidate.add_chess(chess)
|
||||||
|
|
||||||
# 添加羁绊棋子
|
# 添加各羁绊选中的棋子
|
||||||
for chess in unique_combo:
|
all_selected_chess = set()
|
||||||
new_team.add_chess(chess)
|
for combo in chess_selection:
|
||||||
|
for chess in combo:
|
||||||
|
if chess.get('displayName') not in all_selected_chess:
|
||||||
|
candidate.add_chess(chess)
|
||||||
|
all_selected_chess.add(chess.get('displayName'))
|
||||||
|
|
||||||
# 如果还有剩余槽位,用最佳棋子填充
|
# 检查是否超出人口限制
|
||||||
if new_team.size < population:
|
if candidate.size <= population:
|
||||||
new_remaining_slots = population - new_team.size
|
# 如果有剩余人口,填充最佳棋子
|
||||||
filled_teams = self._fill_team_with_best_chess(new_team, new_remaining_slots)
|
if candidate.size < population:
|
||||||
if filled_teams:
|
remaining_slots = population - candidate.size
|
||||||
candidate_teams.extend(filled_teams)
|
filled_candidates = self._fill_team_with_best_chess(candidate, remaining_slots)
|
||||||
else:
|
candidate_teams.extend(filled_candidates)
|
||||||
candidate_teams.append(new_team)
|
else:
|
||||||
|
candidate_teams.append(candidate)
|
||||||
# 如果没有生成候选阵容,直接填充最好的棋子
|
|
||||||
if not candidate_teams:
|
|
||||||
return self._fill_team_with_best_chess(base_team, remaining_slots)
|
|
||||||
|
|
||||||
return candidate_teams
|
return candidate_teams
|
||||||
|
|
||||||
@ -338,7 +357,7 @@ class RecommendationEngine:
|
|||||||
remaining_slots: int
|
remaining_slots: int
|
||||||
) -> List[TeamComposition]:
|
) -> List[TeamComposition]:
|
||||||
"""
|
"""
|
||||||
用最佳棋子填充阵容剩余槽位
|
用最佳棋子填充剩余槽位
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
base_team: 基础阵容
|
base_team: 基础阵容
|
||||||
@ -349,51 +368,94 @@ class RecommendationEngine:
|
|||||||
"""
|
"""
|
||||||
if remaining_slots <= 0:
|
if remaining_slots <= 0:
|
||||||
return [base_team]
|
return [base_team]
|
||||||
|
|
||||||
# 获取所有可用棋子
|
|
||||||
all_chess = self.api.get_all_chess()
|
|
||||||
|
|
||||||
# 过滤掉已经在基础阵容中的棋子
|
|
||||||
available_chess = [
|
|
||||||
chess for chess in all_chess
|
|
||||||
if chess not in base_team.chess_list
|
|
||||||
]
|
|
||||||
|
|
||||||
# 按照一定策略选择最佳棋子
|
|
||||||
# 这里可以实现多种策略,如优先选择与现有阵容羁绊匹配的棋子、高费棋子等
|
|
||||||
# 简单起见,我们用一个贪心策略:计算每个棋子能为阵容增加的羁绊收益
|
|
||||||
|
|
||||||
# 先计算基础阵容的羁绊情况
|
|
||||||
base_team_copy = TeamComposition()
|
|
||||||
for chess in base_team.chess_list:
|
|
||||||
base_team_copy.add_chess(chess)
|
|
||||||
base_team_copy.calculate_synergies(self.api)
|
|
||||||
|
|
||||||
# 评估每个棋子的收益
|
|
||||||
chess_benefits = []
|
|
||||||
for chess in available_chess:
|
|
||||||
# 创建临时阵容
|
|
||||||
temp_team = TeamComposition()
|
|
||||||
for c in base_team.chess_list:
|
|
||||||
temp_team.add_chess(c)
|
|
||||||
temp_team.add_chess(chess)
|
|
||||||
temp_team.calculate_synergies(self.api)
|
|
||||||
|
|
||||||
# 计算羁绊增益
|
# 计算每个棋子的羁绊价值
|
||||||
benefit = self.scorer.score_team(temp_team) - self.scorer.score_team(base_team_copy)
|
all_chess = self.api.get_all_chess()
|
||||||
chess_benefits.append((chess, benefit))
|
base_chess_names = {chess.get('displayName') for chess in base_team.chess_list}
|
||||||
|
|
||||||
# 按收益排序
|
# 排除已在基础阵容中的棋子
|
||||||
chess_benefits.sort(key=lambda x: x[1], reverse=True)
|
available_chess = [chess for chess in all_chess
|
||||||
|
if chess.get('displayName') not in base_chess_names]
|
||||||
|
|
||||||
# 选择前N个最佳棋子
|
# 计算基础阵容中已有的羁绊
|
||||||
best_chess = [item[0] for item in chess_benefits[:remaining_slots]]
|
base_synergies = {}
|
||||||
|
|
||||||
# 创建填充后的阵容
|
|
||||||
filled_team = TeamComposition()
|
|
||||||
for chess in base_team.chess_list:
|
for chess in base_team.chess_list:
|
||||||
filled_team.add_chess(chess)
|
# 职业
|
||||||
for chess in best_chess:
|
for job_id in chess.get('jobIds', '').split(','):
|
||||||
filled_team.add_chess(chess)
|
if job_id:
|
||||||
|
base_synergies[job_id] = base_synergies.get(job_id, 0) + 1
|
||||||
|
|
||||||
|
# 特质
|
||||||
|
for race_id in chess.get('raceIds', '').split(','):
|
||||||
|
if race_id:
|
||||||
|
base_synergies[race_id] = base_synergies.get(race_id, 0) + 1
|
||||||
|
|
||||||
return [filled_team]
|
# 评估每个可用棋子的价值
|
||||||
|
chess_values = []
|
||||||
|
for chess in available_chess:
|
||||||
|
value = 0
|
||||||
|
|
||||||
|
# 根据费用评估基础价值
|
||||||
|
cost = int(chess.get('price', 1))
|
||||||
|
cost_multiplier = float(self.weights_config.get('cost_weights', {}).get(str(cost), 1.0))
|
||||||
|
value += cost * cost_multiplier
|
||||||
|
|
||||||
|
# 根据羁绊评估额外价值
|
||||||
|
for job_id in chess.get('jobIds', '').split(','):
|
||||||
|
if job_id and job_id in base_synergies:
|
||||||
|
# 如果能与基础阵容形成羁绊,增加价值
|
||||||
|
synergy = self.api.get_synergy_by_id(job_id)
|
||||||
|
if synergy:
|
||||||
|
# 检查是否能激活新的羁绊等级
|
||||||
|
levels = self.api.get_synergy_levels(job_id)
|
||||||
|
current_count = base_synergies[job_id]
|
||||||
|
for level_str in sorted(levels.keys()):
|
||||||
|
level = int(level_str)
|
||||||
|
if current_count + 1 >= level > current_count:
|
||||||
|
# 如果添加这个棋子后能激活新的等级
|
||||||
|
weight = self.weights_config.get('synergy_weights', {}).get(synergy.get('name', ''), 1.0)
|
||||||
|
level_multiplier = float(self.weights_config.get('synergy_level_weights', {}).get(level_str, 1.0))
|
||||||
|
value += weight * level_multiplier * 10
|
||||||
|
break
|
||||||
|
# 即使不能激活新等级,也增加一些价值
|
||||||
|
value += 1
|
||||||
|
|
||||||
|
for race_id in chess.get('raceIds', '').split(','):
|
||||||
|
if race_id and race_id in base_synergies:
|
||||||
|
# 如果能与基础阵容形成羁绊,增加价值
|
||||||
|
synergy = self.api.get_synergy_by_id(race_id)
|
||||||
|
if synergy:
|
||||||
|
# 检查是否能激活新的羁绊等级
|
||||||
|
levels = self.api.get_synergy_levels(race_id)
|
||||||
|
current_count = base_synergies[race_id]
|
||||||
|
for level_str in sorted(levels.keys()):
|
||||||
|
level = int(level_str)
|
||||||
|
if current_count + 1 >= level > current_count:
|
||||||
|
# 如果添加这个棋子后能激活新的等级
|
||||||
|
weight = self.weights_config.get('synergy_weights', {}).get(synergy.get('name', ''), 1.0)
|
||||||
|
level_multiplier = float(self.weights_config.get('synergy_level_weights', {}).get(level_str, 1.0))
|
||||||
|
value += weight * level_multiplier * 10
|
||||||
|
break
|
||||||
|
# 即使不能激活新等级,也增加一些价值
|
||||||
|
value += 1
|
||||||
|
|
||||||
|
# 添加棋子自定义权重
|
||||||
|
chess_weight = self.weights_config.get('chess_weights', {}).get(chess.get('displayName', ''), 1.0)
|
||||||
|
value *= chess_weight
|
||||||
|
|
||||||
|
chess_values.append((chess, value))
|
||||||
|
|
||||||
|
# 按价值从高到低排序
|
||||||
|
chess_values.sort(key=lambda x: x[1], reverse=True)
|
||||||
|
|
||||||
|
# 创建候选阵容,添加价值最高的棋子
|
||||||
|
candidate = TeamComposition()
|
||||||
|
# 添加基础阵容的棋子
|
||||||
|
for chess in base_team.chess_list:
|
||||||
|
candidate.add_chess(chess)
|
||||||
|
|
||||||
|
# 添加价值最高的剩余槽位数量的棋子
|
||||||
|
for i in range(min(remaining_slots, len(chess_values))):
|
||||||
|
candidate.add_chess(chess_values[i][0])
|
||||||
|
|
||||||
|
return [candidate]
|
@ -4,20 +4,29 @@
|
|||||||
from src.data_provider import DataQueryAPI
|
from src.data_provider import DataQueryAPI
|
||||||
from src.recommendation import RecommendationEngine
|
from src.recommendation import RecommendationEngine
|
||||||
from src.scoring import TeamScorer, ScoringConfig
|
from src.scoring import TeamScorer, ScoringConfig
|
||||||
|
from src.config import get_global_weights_config
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(config_path: Optional[str] = None):
|
||||||
"""主函数"""
|
"""
|
||||||
|
主函数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path: 配置文件路径,用于全局权重配置
|
||||||
|
"""
|
||||||
print("=== 云顶之弈阵容推荐模块演示 ===")
|
print("=== 云顶之弈阵容推荐模块演示 ===")
|
||||||
|
|
||||||
# 初始化数据查询API
|
# 初始化数据查询API
|
||||||
print("\n正在加载数据...")
|
print("\n正在加载数据...")
|
||||||
api = DataQueryAPI()
|
api = DataQueryAPI()
|
||||||
|
|
||||||
|
# 获取全局权重配置
|
||||||
|
weights_config = get_global_weights_config(config_path)
|
||||||
|
|
||||||
# 初始化评分系统和推荐引擎
|
# 初始化评分系统和推荐引擎
|
||||||
scoring_config = ScoringConfig()
|
scorer = TeamScorer(config_path=config_path)
|
||||||
scorer = TeamScorer(config=scoring_config)
|
engine = RecommendationEngine(api=api, scorer=scorer, config_path=config_path)
|
||||||
engine = RecommendationEngine(api=api, scorer=scorer)
|
|
||||||
|
|
||||||
# 显示数据基本信息
|
# 显示数据基本信息
|
||||||
print(f"\n当前游戏版本: {api.data_loader.get_latest_version()}")
|
print(f"\n当前游戏版本: {api.data_loader.get_latest_version()}")
|
||||||
@ -25,6 +34,13 @@ def main():
|
|||||||
print(f"特质数量: {len(api.get_all_races())}")
|
print(f"特质数量: {len(api.get_all_races())}")
|
||||||
print(f"棋子数量: {len(api.get_all_chess())}")
|
print(f"棋子数量: {len(api.get_all_chess())}")
|
||||||
|
|
||||||
|
# 显示当前权重配置
|
||||||
|
base_weights = weights_config.get_base_weights()
|
||||||
|
print("\n当前评分权重配置:")
|
||||||
|
print(f" 羁绊等级权重: {base_weights.get('synergy_level_weight', 1.0)}")
|
||||||
|
print(f" 羁绊数量权重: {base_weights.get('synergy_count_weight', 0.5)}")
|
||||||
|
print(f" 棋子费用权重: {base_weights.get('chess_cost_weight', 0.1)}")
|
||||||
|
|
||||||
# 互动推荐示例
|
# 互动推荐示例
|
||||||
while True:
|
while True:
|
||||||
print("\n请选择推荐方式:")
|
print("\n请选择推荐方式:")
|
||||||
@ -32,9 +48,10 @@ def main():
|
|||||||
print("2. 指定必选羁绊")
|
print("2. 指定必选羁绊")
|
||||||
print("3. 指定必选棋子")
|
print("3. 指定必选棋子")
|
||||||
print("4. 综合条件推荐")
|
print("4. 综合条件推荐")
|
||||||
print("5. 退出")
|
print("5. 调整权重配置")
|
||||||
|
print("6. 退出")
|
||||||
|
|
||||||
choice = input("请输入选项 (1-5): ")
|
choice = input("请输入选项 (1-6): ")
|
||||||
|
|
||||||
if choice == '1':
|
if choice == '1':
|
||||||
try:
|
try:
|
||||||
@ -56,6 +73,20 @@ def main():
|
|||||||
print(f"未找到名为 '{synergy_name}' 的羁绊")
|
print(f"未找到名为 '{synergy_name}' 的羁绊")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 显示羁绊在配置中的权重
|
||||||
|
synergy_weight = weights_config.get_synergy_weights().get(synergy_name, 1.0)
|
||||||
|
print(f"羁绊 {synergy_name} 的当前权重为: {synergy_weight}")
|
||||||
|
|
||||||
|
# 可选:调整羁绊权重
|
||||||
|
adjust = input(f"是否调整该羁绊的权重?(y/n,当前: {synergy_weight}): ").strip().lower()
|
||||||
|
if adjust == 'y':
|
||||||
|
try:
|
||||||
|
new_weight = float(input(f"请输入新权重值 (当前: {synergy_weight}): ").strip())
|
||||||
|
weights_config.set_synergy_weight(synergy_name, new_weight)
|
||||||
|
print(f"已将羁绊 {synergy_name} 的权重设置为 {new_weight}")
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
level = int(input(f"请输入 {synergy_name} 的目标等级 (默认为1): ") or "1")
|
level = int(input(f"请输入 {synergy_name} 的目标等级 (默认为1): ") or "1")
|
||||||
required_synergies = [{'name': synergy_name, 'level': level}]
|
required_synergies = [{'name': synergy_name, 'level': level}]
|
||||||
@ -81,6 +112,20 @@ def main():
|
|||||||
print(f"未找到名为 '{chess_name}' 的棋子")
|
print(f"未找到名为 '{chess_name}' 的棋子")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 显示棋子在配置中的权重
|
||||||
|
chess_weight = weights_config.get_chess_weights().get(chess_name, 1.0)
|
||||||
|
print(f"棋子 {chess_name} 的当前权重为: {chess_weight}")
|
||||||
|
|
||||||
|
# 可选:调整棋子权重
|
||||||
|
adjust = input(f"是否调整该棋子的权重?(y/n,当前: {chess_weight}): ").strip().lower()
|
||||||
|
if adjust == 'y':
|
||||||
|
try:
|
||||||
|
new_weight = float(input(f"请输入新权重值 (当前: {chess_weight}): ").strip())
|
||||||
|
weights_config.set_chess_weight(chess_name, new_weight)
|
||||||
|
print(f"已将棋子 {chess_name} 的权重设置为 {new_weight}")
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
population = int(input("请输入阵容人口数 (1-10,默认为9): ") or "9")
|
population = int(input("请输入阵容人口数 (1-10,默认为9): ") or "9")
|
||||||
if population < 1 or population > 10:
|
if population < 1 or population > 10:
|
||||||
@ -112,6 +157,20 @@ def main():
|
|||||||
print(f"未找到名为 '{synergy_name}' 的羁绊")
|
print(f"未找到名为 '{synergy_name}' 的羁绊")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 显示羁绊在配置中的权重
|
||||||
|
synergy_weight = weights_config.get_synergy_weights().get(synergy_name, 1.0)
|
||||||
|
print(f"羁绊 {synergy_name} 的当前权重为: {synergy_weight}")
|
||||||
|
|
||||||
|
# 可选:调整羁绊权重
|
||||||
|
adjust = input(f"是否调整该羁绊的权重?(y/n,当前: {synergy_weight}): ").strip().lower()
|
||||||
|
if adjust == 'y':
|
||||||
|
try:
|
||||||
|
new_weight = float(input(f"请输入新权重值 (当前: {synergy_weight}): ").strip())
|
||||||
|
weights_config.set_synergy_weight(synergy_name, new_weight)
|
||||||
|
print(f"已将羁绊 {synergy_name} 的权重设置为 {new_weight}")
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
level = int(input(f"请输入 {synergy_name} 的目标等级 (默认为1): ") or "1")
|
level = int(input(f"请输入 {synergy_name} 的目标等级 (默认为1): ") or "1")
|
||||||
required_synergies.append({'name': synergy_name, 'level': level})
|
required_synergies.append({'name': synergy_name, 'level': level})
|
||||||
|
|
||||||
@ -127,6 +186,20 @@ def main():
|
|||||||
print(f"未找到名为 '{chess_name}' 的棋子")
|
print(f"未找到名为 '{chess_name}' 的棋子")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 显示棋子在配置中的权重
|
||||||
|
chess_weight = weights_config.get_chess_weights().get(chess_name, 1.0)
|
||||||
|
print(f"棋子 {chess_name} 的当前权重为: {chess_weight}")
|
||||||
|
|
||||||
|
# 可选:调整棋子权重
|
||||||
|
adjust = input(f"是否调整该棋子的权重?(y/n,当前: {chess_weight}): ").strip().lower()
|
||||||
|
if adjust == 'y':
|
||||||
|
try:
|
||||||
|
new_weight = float(input(f"请输入新权重值 (当前: {chess_weight}): ").strip())
|
||||||
|
weights_config.set_chess_weight(chess_name, new_weight)
|
||||||
|
print(f"已将棋子 {chess_name} 的权重设置为 {new_weight}")
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
required_chess.append({'name': chess_name})
|
required_chess.append({'name': chess_name})
|
||||||
|
|
||||||
# 设置人口
|
# 设置人口
|
||||||
@ -147,6 +220,72 @@ def main():
|
|||||||
print("请输入有效的整数")
|
print("请输入有效的整数")
|
||||||
|
|
||||||
elif choice == '5':
|
elif choice == '5':
|
||||||
|
print("\n=== 调整权重配置 ===")
|
||||||
|
print("1. 调整基础权重")
|
||||||
|
print("2. 调整羁绊权重")
|
||||||
|
print("3. 调整棋子权重")
|
||||||
|
print("4. 返回主菜单")
|
||||||
|
|
||||||
|
sub_choice = input("请输入选项 (1-4): ")
|
||||||
|
|
||||||
|
if sub_choice == '1':
|
||||||
|
try:
|
||||||
|
print("\n当前基础权重:")
|
||||||
|
base_weights = weights_config.get_base_weights()
|
||||||
|
for key, value in base_weights.items():
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
|
||||||
|
param = input("\n请输入要调整的参数名称 (例如: synergy_level_weight): ")
|
||||||
|
if param not in base_weights:
|
||||||
|
print(f"参数 '{param}' 不存在")
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_value = float(input(f"请输入新的权重值 (当前: {base_weights.get(param, 1.0)}): "))
|
||||||
|
weights_config.set_base_weight(param, new_value)
|
||||||
|
print(f"已将 {param} 的权重设置为 {new_value}")
|
||||||
|
|
||||||
|
# 更新评分系统
|
||||||
|
scorer.reload_config()
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
|
elif sub_choice == '2':
|
||||||
|
synergy_name = input("请输入羁绊名称: ")
|
||||||
|
synergy = api.get_synergy_by_name(synergy_name)
|
||||||
|
if not synergy:
|
||||||
|
print(f"未找到名为 '{synergy_name}' 的羁绊")
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
current_weight = weights_config.get_synergy_weights().get(synergy_name, 1.0)
|
||||||
|
new_weight = float(input(f"请输入新的权重值 (当前: {current_weight}): "))
|
||||||
|
weights_config.set_synergy_weight(synergy_name, new_weight)
|
||||||
|
print(f"已将羁绊 {synergy_name} 的权重设置为 {new_weight}")
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
|
elif sub_choice == '3':
|
||||||
|
chess_name = input("请输入棋子名称: ")
|
||||||
|
chess = api.get_chess_by_name(chess_name)
|
||||||
|
if not chess:
|
||||||
|
print(f"未找到名为 '{chess_name}' 的棋子")
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
current_weight = weights_config.get_chess_weights().get(chess_name, 1.0)
|
||||||
|
new_weight = float(input(f"请输入新的权重值 (当前: {current_weight}): "))
|
||||||
|
weights_config.set_chess_weight(chess_name, new_weight)
|
||||||
|
print(f"已将棋子 {chess_name} 的权重设置为 {new_weight}")
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
|
elif sub_choice == '4':
|
||||||
|
continue
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("无效选项,请重新输入")
|
||||||
|
|
||||||
|
elif choice == '6':
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -200,11 +339,10 @@ def _display_recommended_teams(teams):
|
|||||||
if race:
|
if race:
|
||||||
race_names.append(race['name'])
|
race_names.append(race['name'])
|
||||||
|
|
||||||
synergy_text = ' / '.join(job_names + race_names)
|
job_str = ', '.join(job_names) if job_names else '无'
|
||||||
print(f" - {chess['displayName']} ({price}费): {synergy_text}")
|
race_str = ', '.join(race_names) if race_names else '无'
|
||||||
|
|
||||||
# 显示阵容总费用
|
print(f" - {chess['displayName']} ({price}金币) | 职业: {job_str} | 特质: {race_str}")
|
||||||
print(f"\n阵容总费用: {team.total_cost}")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
阵容评分模块
|
阵容评分模块 - 提供对云顶之弈阵容的评分功能
|
||||||
|
|
||||||
|
此模块包含评分系统相关的组件,用于对阵容进行综合评分。
|
||||||
"""
|
"""
|
||||||
from .scoring_system import TeamScorer, ScoringConfig
|
from .scoring_system import TeamScorer, ScoringConfig
|
||||||
from .config_loader import ConfigLoader, create_default_config
|
|
||||||
|
|
||||||
__all__ = ['TeamScorer', 'ScoringConfig', 'ConfigLoader', 'create_default_config']
|
__all__ = ['TeamScorer', 'ScoringConfig']
|
@ -1,170 +0,0 @@
|
|||||||
"""
|
|
||||||
配置加载器 - 用于从外部配置文件加载自定义权重设置
|
|
||||||
|
|
||||||
此模块提供了从YAML或JSON配置文件中加载自定义权重设置的功能,
|
|
||||||
支持为特定羁绊和棋子设置自定义权重。
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import yaml
|
|
||||||
import logging
|
|
||||||
from typing import Dict, Any, Optional
|
|
||||||
|
|
||||||
# 配置日志
|
|
||||||
logger = logging.getLogger("TFT-Strategist-ConfigLoader")
|
|
||||||
|
|
||||||
class ConfigLoader:
|
|
||||||
"""
|
|
||||||
配置加载器,用于从外部文件加载自定义权重设置
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config_path: Optional[str] = None):
|
|
||||||
"""
|
|
||||||
初始化配置加载器
|
|
||||||
|
|
||||||
Args:
|
|
||||||
config_path: 配置文件路径,如果为None则使用默认路径
|
|
||||||
"""
|
|
||||||
self.config_path = config_path or os.path.join("data", "weights_config.yaml")
|
|
||||||
self.config_data = {}
|
|
||||||
|
|
||||||
def load_config(self) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
加载配置文件
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, Any]: 包含配置数据的字典
|
|
||||||
"""
|
|
||||||
if not os.path.exists(self.config_path):
|
|
||||||
logger.warning(f"配置文件不存在: {self.config_path}")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
try:
|
|
||||||
file_ext = os.path.splitext(self.config_path)[1].lower()
|
|
||||||
|
|
||||||
if file_ext == '.json':
|
|
||||||
with open(self.config_path, 'r', encoding='utf-8') as f:
|
|
||||||
self.config_data = json.load(f)
|
|
||||||
elif file_ext in ['.yaml', '.yml']:
|
|
||||||
with open(self.config_path, 'r', encoding='utf-8') as f:
|
|
||||||
self.config_data = yaml.safe_load(f)
|
|
||||||
else:
|
|
||||||
logger.error(f"不支持的配置文件格式: {file_ext}")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
logger.info(f"成功加载配置文件: {self.config_path}")
|
|
||||||
return self.config_data
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"加载配置文件失败: {str(e)}")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def get_synergy_weights(self) -> Dict[str, float]:
|
|
||||||
"""
|
|
||||||
获取羁绊权重配置
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, float]: 羁绊名称到权重的映射
|
|
||||||
"""
|
|
||||||
return self.config_data.get('synergy_weights', {})
|
|
||||||
|
|
||||||
def get_chess_weights(self) -> Dict[str, float]:
|
|
||||||
"""
|
|
||||||
获取棋子权重配置
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, float]: 棋子名称到权重的映射
|
|
||||||
"""
|
|
||||||
return self.config_data.get('chess_weights', {})
|
|
||||||
|
|
||||||
def get_base_weights(self) -> Dict[str, float]:
|
|
||||||
"""
|
|
||||||
获取基础权重配置
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
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:
|
|
||||||
"""
|
|
||||||
创建默认配置文件
|
|
||||||
|
|
||||||
Args:
|
|
||||||
config_path: 配置文件路径
|
|
||||||
"""
|
|
||||||
default_config = {
|
|
||||||
'base_weights': {
|
|
||||||
'synergy_level_weight': 1.0,
|
|
||||||
'synergy_count_weight': 0.5,
|
|
||||||
'chess_cost_weight': 0.1
|
|
||||||
},
|
|
||||||
'synergy_weights': {
|
|
||||||
'重装战士': 1.5,
|
|
||||||
'魔法师': 1.2,
|
|
||||||
'神谕者': 1.3,
|
|
||||||
'斗士': 1.1
|
|
||||||
},
|
|
||||||
'chess_weights': {
|
|
||||||
'亚索': 1.5,
|
|
||||||
'艾希': 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
file_ext = os.path.splitext(config_path)[1].lower()
|
|
||||||
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
|
||||||
|
|
||||||
if file_ext == '.json':
|
|
||||||
with open(config_path, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(default_config, f, ensure_ascii=False, indent=2)
|
|
||||||
elif file_ext in ['.yaml', '.yml']:
|
|
||||||
with open(config_path, 'w', encoding='utf-8') as f:
|
|
||||||
yaml.dump(default_config, f, allow_unicode=True, default_flow_style=False)
|
|
||||||
else:
|
|
||||||
logger.error(f"不支持的配置文件格式: {file_ext}")
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info(f"成功创建默认配置文件: {config_path}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"创建默认配置文件失败: {str(e)}")
|
|
@ -12,7 +12,8 @@ from typing import Dict, List, Any, Optional
|
|||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import os
|
import os
|
||||||
from .config_loader import ConfigLoader, create_default_config
|
from src.config import get_global_weights_config
|
||||||
|
from src.data_provider import DataQueryAPI
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -35,10 +36,10 @@ class ScoringConfig:
|
|||||||
# 棋子费用权重倍数
|
# 棋子费用权重倍数
|
||||||
cost_multipliers: Dict[str, float] = None
|
cost_multipliers: Dict[str, float] = None
|
||||||
|
|
||||||
# 自定义羁绊权重 (新增)
|
# 自定义羁绊权重
|
||||||
synergy_weights: Dict[str, float] = None
|
synergy_weights: Dict[str, float] = None
|
||||||
|
|
||||||
# 自定义棋子权重 (新增)
|
# 自定义棋子权重
|
||||||
chess_weights: Dict[str, float] = None
|
chess_weights: Dict[str, float] = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
@ -80,70 +81,81 @@ class TeamScorer:
|
|||||||
阵容评分系统,负责对阵容进行综合评分
|
阵容评分系统,负责对阵容进行综合评分
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: Optional[ScoringConfig] = None, config_path: Optional[str] = None):
|
def __init__(self, api: Optional[DataQueryAPI] = None, config_path: Optional[str] = None, config_obj: Optional[Dict[str, Any]] = None):
|
||||||
"""
|
"""
|
||||||
初始化阵容评分系统
|
初始化阵容评分系统
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config: 评分配置,如果为None则使用默认配置
|
api: 数据查询API
|
||||||
config_path: 外部配置文件路径,如果提供则从中加载自定义权重
|
config_path: 外部配置文件路径,如果提供则从中加载自定义权重
|
||||||
|
config_obj: 配置对象,优先于config_path使用
|
||||||
"""
|
"""
|
||||||
self.config = config if config else ScoringConfig()
|
self.api = api if api else DataQueryAPI()
|
||||||
|
|
||||||
# 如果提供了配置文件路径,则加载自定义权重
|
# 加载权重配置
|
||||||
if config_path or os.path.exists(os.path.join("data", "weights_config.yaml")):
|
if config_obj:
|
||||||
self._load_custom_weights(config_path)
|
self.weights_config = config_obj
|
||||||
|
else:
|
||||||
|
self.weights_config = get_global_weights_config(config_path).config_data
|
||||||
|
|
||||||
|
# 加载基础权重参数
|
||||||
|
self.synergy_level_weight = self.weights_config.get('base_weights', {}).get('synergy_level_weight', 1.0)
|
||||||
|
self.synergy_count_weight = self.weights_config.get('base_weights', {}).get('synergy_count_weight', 0.5)
|
||||||
|
self.chess_cost_weight = self.weights_config.get('base_weights', {}).get('chess_cost_weight', 0.1)
|
||||||
|
|
||||||
|
# 加载自定义权重
|
||||||
|
self.synergy_weights = self.weights_config.get('synergy_weights', {})
|
||||||
|
self.chess_weights = self.weights_config.get('chess_weights', {})
|
||||||
|
|
||||||
|
# 加载羁绊等级权重和棋子费用权重倍数
|
||||||
|
synergy_level_weights = self.weights_config.get('synergy_level_weights', {})
|
||||||
|
cost_weights = self.weights_config.get('cost_weights', {})
|
||||||
|
|
||||||
|
# 将键转换为正确的类型
|
||||||
|
self.level_multipliers = {int(k) if isinstance(k, str) else k: float(v) for k, v in synergy_level_weights.items()}
|
||||||
|
self.cost_multipliers = {k: float(v) for k, v in cost_weights.items()}
|
||||||
|
|
||||||
def _load_custom_weights(self, config_path: Optional[str] = None) -> None:
|
def reload_config(self) -> None:
|
||||||
"""
|
"""重新加载配置"""
|
||||||
从配置文件加载自定义权重
|
if isinstance(self.weights_config, dict):
|
||||||
|
# 针对直接传入的配置对象,不需要重新加载
|
||||||
Args:
|
# 重新加载基础权重参数
|
||||||
config_path: 配置文件路径,如果为None则使用默认路径
|
self.synergy_level_weight = self.weights_config.get('base_weights', {}).get('synergy_level_weight', 1.0)
|
||||||
"""
|
self.synergy_count_weight = self.weights_config.get('base_weights', {}).get('synergy_count_weight', 0.5)
|
||||||
loader = ConfigLoader(config_path)
|
self.chess_cost_weight = self.weights_config.get('base_weights', {}).get('chess_cost_weight', 0.1)
|
||||||
config_data = loader.load_config()
|
|
||||||
|
|
||||||
if not config_data:
|
|
||||||
# 如果配置文件不存在或加载失败,创建默认配置文件
|
|
||||||
default_path = config_path or os.path.join("data", "weights_config.yaml")
|
|
||||||
create_default_config(default_path)
|
|
||||||
logger.info(f"已创建默认配置文件: {default_path}")
|
|
||||||
loader = ConfigLoader(default_path)
|
|
||||||
config_data = loader.load_config()
|
|
||||||
|
|
||||||
# 更新基础权重
|
|
||||||
base_weights = loader.get_base_weights()
|
|
||||||
for key, value in base_weights.items():
|
|
||||||
if hasattr(self.config, key):
|
|
||||||
setattr(self.config, key, value)
|
|
||||||
logger.info(f"从配置文件加载基础权重: {key} = {value}")
|
|
||||||
|
|
||||||
# 更新羁绊自定义权重
|
|
||||||
synergy_weights = loader.get_synergy_weights()
|
|
||||||
if synergy_weights:
|
|
||||||
self.config.synergy_weights.update(synergy_weights)
|
|
||||||
logger.info(f"从配置文件加载羁绊权重: {len(synergy_weights)}个")
|
|
||||||
|
|
||||||
# 更新棋子自定义权重
|
|
||||||
chess_weights = loader.get_chess_weights()
|
|
||||||
if chess_weights:
|
|
||||||
self.config.chess_weights.update(chess_weights)
|
|
||||||
logger.info(f"从配置文件加载棋子权重: {len(chess_weights)}个")
|
|
||||||
|
|
||||||
# 加载羁绊等级权重倍数 (使用新方法)
|
# 重新加载自定义权重
|
||||||
synergy_level_weights = loader.get_synergy_level_weights()
|
self.synergy_weights = self.weights_config.get('synergy_weights', {})
|
||||||
if synergy_level_weights:
|
self.chess_weights = self.weights_config.get('chess_weights', {})
|
||||||
# 将字符串键转换为整数键
|
|
||||||
level_multipliers = {int(k): float(v) for k, v in synergy_level_weights.items()}
|
# 重新加载羁绊等级权重和棋子费用权重倍数
|
||||||
self.config.level_multipliers = level_multipliers
|
synergy_level_weights = self.weights_config.get('synergy_level_weights', {})
|
||||||
logger.info(f"从配置文件加载羁绊等级权重倍数: {len(level_multipliers)}个")
|
cost_weights = self.weights_config.get('cost_weights', {})
|
||||||
|
|
||||||
# 加载棋子费用权重倍数 (使用新方法)
|
# 将键转换为正确的类型
|
||||||
cost_weights = loader.get_cost_weights()
|
self.level_multipliers = {int(k) if isinstance(k, str) else k: float(v) for k, v in synergy_level_weights.items()}
|
||||||
if cost_weights:
|
self.cost_multipliers = {k: float(v) for k, v in cost_weights.items()}
|
||||||
self.config.cost_multipliers = cost_weights
|
else:
|
||||||
logger.info(f"从配置文件加载棋子费用权重倍数: {len(cost_weights)}个")
|
# 获取全局配置的最新实例
|
||||||
|
config = get_global_weights_config()
|
||||||
|
self.weights_config = config.config_data
|
||||||
|
|
||||||
|
# 重新加载基础权重参数
|
||||||
|
self.synergy_level_weight = config.get_base_weights().get('synergy_level_weight', 1.0)
|
||||||
|
self.synergy_count_weight = config.get_base_weights().get('synergy_count_weight', 0.5)
|
||||||
|
self.chess_cost_weight = config.get_base_weights().get('chess_cost_weight', 0.1)
|
||||||
|
|
||||||
|
# 重新加载自定义权重
|
||||||
|
self.synergy_weights = config.get_synergy_weights()
|
||||||
|
self.chess_weights = config.get_chess_weights()
|
||||||
|
|
||||||
|
# 重新加载羁绊等级权重和棋子费用权重倍数
|
||||||
|
synergy_level_weights = config.get_synergy_level_weights()
|
||||||
|
cost_weights = config.get_cost_weights()
|
||||||
|
|
||||||
|
# 将键转换为正确的类型
|
||||||
|
self.level_multipliers = {int(k) if isinstance(k, str) else k: float(v) for k, v in synergy_level_weights.items()}
|
||||||
|
self.cost_multipliers = {k: float(v) for k, v in cost_weights.items()}
|
||||||
|
|
||||||
def score_team(self, team) -> float:
|
def score_team(self, team) -> float:
|
||||||
"""
|
"""
|
||||||
@ -164,15 +176,15 @@ class TeamScorer:
|
|||||||
# 3. 棋子费用评分
|
# 3. 棋子费用评分
|
||||||
chess_cost_score = self._score_chess_cost(team)
|
chess_cost_score = self._score_chess_cost(team)
|
||||||
|
|
||||||
# 4. 自定义羁绊和棋子权重评分 (新增)
|
# 4. 自定义羁绊和棋子权重评分
|
||||||
custom_weight_score = self._score_custom_weights(team)
|
custom_weight_score = self._score_custom_weights(team)
|
||||||
|
|
||||||
# 5. 阵容整体评分
|
# 5. 阵容整体评分
|
||||||
# 综合以上四项,加权求和
|
# 综合以上四项,加权求和
|
||||||
total_score = (
|
total_score = (
|
||||||
synergy_level_score * self.config.synergy_level_weight +
|
synergy_level_score * self.synergy_level_weight +
|
||||||
synergy_count_score * self.config.synergy_count_weight +
|
synergy_count_score * self.synergy_count_weight +
|
||||||
chess_cost_score * self.config.chess_cost_weight +
|
chess_cost_score * self.chess_cost_weight +
|
||||||
custom_weight_score # 自定义权重分数已经包含各自的权重
|
custom_weight_score # 自定义权重分数已经包含各自的权重
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -195,27 +207,26 @@ class TeamScorer:
|
|||||||
level = job_level['level']
|
level = job_level['level']
|
||||||
count = job_level['count']
|
count = job_level['count']
|
||||||
# 计算该羁绊的得分,根据等级和数量
|
# 计算该羁绊的得分,根据等级和数量
|
||||||
level_multiplier = self.config.level_multipliers.get(level, 1.0)
|
level_multiplier = self.level_multipliers.get(level, 1.0)
|
||||||
# 额外激活的羁绊单位也计入评分
|
# 额外激活的羁绊单位也计入评分
|
||||||
extra_units = max(0, count - level)
|
extra_units = max(0, count - level)
|
||||||
level_score = level * level_multiplier + extra_units * 0.5
|
score += level * level_multiplier + extra_units * 0.1
|
||||||
score += level_score
|
|
||||||
|
|
||||||
# 处理特质羁绊
|
# 处理特质羁绊
|
||||||
for race_level in team.synergy_levels.get('race', []):
|
for race_level in team.synergy_levels.get('race', []):
|
||||||
level = race_level['level']
|
level = race_level['level']
|
||||||
count = race_level['count']
|
count = race_level['count']
|
||||||
# 计算该羁绊的得分
|
# 计算该羁绊的得分,根据等级和数量
|
||||||
level_multiplier = self.config.level_multipliers.get(level, 1.0)
|
level_multiplier = self.level_multipliers.get(level, 1.0)
|
||||||
|
# 额外激活的羁绊单位也计入评分
|
||||||
extra_units = max(0, count - level)
|
extra_units = max(0, count - level)
|
||||||
level_score = level * level_multiplier + extra_units * 0.5
|
score += level * level_multiplier + extra_units * 0.1
|
||||||
score += level_score
|
|
||||||
|
|
||||||
return score
|
return score
|
||||||
|
|
||||||
def _score_synergy_count(self, team) -> float:
|
def _score_synergy_count(self, team) -> float:
|
||||||
"""
|
"""
|
||||||
评分羁绊种类数量
|
评分激活的羁绊种类数量
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
team: TeamComposition实例
|
team: TeamComposition实例
|
||||||
@ -223,17 +234,13 @@ class TeamScorer:
|
|||||||
Returns:
|
Returns:
|
||||||
float: 羁绊种类数量评分
|
float: 羁绊种类数量评分
|
||||||
"""
|
"""
|
||||||
# 计算激活的羁绊种类数量
|
# 获取激活的职业和特质数量
|
||||||
job_count = len(team.synergy_levels.get('job', []))
|
active_job_count = len(team.synergy_levels.get('job', []))
|
||||||
race_count = len(team.synergy_levels.get('race', []))
|
active_race_count = len(team.synergy_levels.get('race', []))
|
||||||
|
|
||||||
# 根据羁绊种类数量评分
|
# 根据激活的羁绊种类数量计算得分
|
||||||
# 羁绊越多,评分越高,但有一个合理的上限
|
# 激活的羁绊种类越多,得分越高,但增长是非线性的
|
||||||
total_count = job_count + race_count
|
return (active_job_count + active_race_count) ** 0.8
|
||||||
# 使用对数函数避免羁绊数量过多时分数过高
|
|
||||||
score = (1 + total_count) ** 0.8
|
|
||||||
|
|
||||||
return score
|
|
||||||
|
|
||||||
def _score_chess_cost(self, team) -> float:
|
def _score_chess_cost(self, team) -> float:
|
||||||
"""
|
"""
|
||||||
@ -247,21 +254,18 @@ class TeamScorer:
|
|||||||
"""
|
"""
|
||||||
score = 0.0
|
score = 0.0
|
||||||
|
|
||||||
# 按照棋子费用评分
|
# 根据棋子费用计算得分
|
||||||
for chess in team.chess_list:
|
for chess in team.chess_list:
|
||||||
cost = chess.get('price', '1')
|
cost = str(chess.get('price', '1'))
|
||||||
cost_multiplier = self.config.cost_multipliers.get(cost, 1.0)
|
# 费用越高的棋子,权重越大
|
||||||
|
cost_multiplier = self.cost_multipliers.get(cost, 1.0)
|
||||||
score += float(cost) * cost_multiplier
|
score += float(cost) * cost_multiplier
|
||||||
|
|
||||||
# 费用分布的平衡性评分
|
|
||||||
# 根据阵容人口,期望的费用分布可能不同
|
|
||||||
# 这里可以进一步完善
|
|
||||||
|
|
||||||
return score
|
return score
|
||||||
|
|
||||||
def _score_custom_weights(self, team) -> float:
|
def _score_custom_weights(self, team) -> float:
|
||||||
"""
|
"""
|
||||||
根据自定义权重评分
|
根据自定义权重对阵容进行评分
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
team: TeamComposition实例
|
team: TeamComposition实例
|
||||||
@ -271,79 +275,140 @@ class TeamScorer:
|
|||||||
"""
|
"""
|
||||||
score = 0.0
|
score = 0.0
|
||||||
|
|
||||||
# 应用自定义羁绊权重
|
# 1. 评分羁绊自定义权重
|
||||||
if self.config.synergy_weights:
|
for job_level in team.synergy_levels.get('job', []):
|
||||||
# 职业羁绊
|
synergy_name = job_level['name']
|
||||||
for job_level in team.synergy_levels.get('job', []):
|
if synergy_name in self.synergy_weights:
|
||||||
synergy_name = job_level.get('name', '')
|
weight = self.synergy_weights[synergy_name]
|
||||||
if synergy_name in self.config.synergy_weights:
|
score += weight * job_level['level']
|
||||||
weight = self.config.synergy_weights[synergy_name]
|
|
||||||
level = job_level['level']
|
|
||||||
score += level * weight
|
|
||||||
logger.debug(f"应用羁绊权重: {synergy_name}, 等级: {level}, 权重: {weight}")
|
|
||||||
|
|
||||||
# 特质羁绊
|
|
||||||
for race_level in team.synergy_levels.get('race', []):
|
|
||||||
synergy_name = race_level.get('name', '')
|
|
||||||
if synergy_name in self.config.synergy_weights:
|
|
||||||
weight = self.config.synergy_weights[synergy_name]
|
|
||||||
level = race_level['level']
|
|
||||||
score += level * weight
|
|
||||||
logger.debug(f"应用羁绊权重: {synergy_name}, 等级: {level}, 权重: {weight}")
|
|
||||||
|
|
||||||
# 应用自定义棋子权重
|
for race_level in team.synergy_levels.get('race', []):
|
||||||
if self.config.chess_weights:
|
synergy_name = race_level['name']
|
||||||
for chess in team.chess_list:
|
if synergy_name in self.synergy_weights:
|
||||||
chess_name = chess.get('displayName', '')
|
weight = self.synergy_weights[synergy_name]
|
||||||
if chess_name in self.config.chess_weights:
|
score += weight * race_level['level']
|
||||||
weight = self.config.chess_weights[chess_name]
|
|
||||||
score += weight
|
# 2. 评分棋子自定义权重
|
||||||
logger.debug(f"应用棋子权重: {chess_name}, 权重: {weight}")
|
for chess in team.chess_list:
|
||||||
|
chess_name = chess.get('displayName', '')
|
||||||
|
if chess_name in self.chess_weights:
|
||||||
|
weight = self.chess_weights[chess_name]
|
||||||
|
score += weight
|
||||||
|
|
||||||
return score
|
return score
|
||||||
|
|
||||||
def customize_scoring(self, **kwargs) -> None:
|
def customize_scoring(self, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
自定义评分参数
|
自定义评分参数
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
**kwargs: 评分参数,如synergy_level_weight, chess_cost_weight等
|
**kwargs: 参数名和值的映射
|
||||||
"""
|
"""
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
if hasattr(self.config, key):
|
if hasattr(self, key):
|
||||||
setattr(self.config, key, value)
|
setattr(self, key, value)
|
||||||
logger.info(f"设置评分参数 {key} = {value}")
|
logger.info(f"自定义评分参数: {key} = {value}")
|
||||||
else:
|
|
||||||
logger.warning(f"未知的评分参数: {key}")
|
|
||||||
|
|
||||||
def set_synergy_weight(self, synergy_name: str, weight: float) -> None:
|
def set_synergy_weight(self, synergy_name: str, weight: float) -> None:
|
||||||
"""
|
"""
|
||||||
设置特定羁绊的自定义权重
|
设置特定羁绊的权重
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
synergy_name: 羁绊名称
|
synergy_name: 羁绊名称
|
||||||
weight: 权重值
|
weight: 权重值
|
||||||
"""
|
"""
|
||||||
self.config.synergy_weights[synergy_name] = weight
|
self.synergy_weights[synergy_name] = weight
|
||||||
logger.info(f"设置羁绊权重: {synergy_name} = {weight}")
|
# 同时更新全局配置
|
||||||
|
weights_config = get_global_weights_config()
|
||||||
|
weights_config.set_synergy_weight(synergy_name, weight)
|
||||||
|
|
||||||
def set_chess_weight(self, chess_name: str, weight: float) -> None:
|
def set_chess_weight(self, chess_name: str, weight: float) -> None:
|
||||||
"""
|
"""
|
||||||
设置特定棋子的自定义权重
|
设置特定棋子的权重
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
chess_name: 棋子名称
|
chess_name: 棋子名称
|
||||||
weight: 权重值
|
weight: 权重值
|
||||||
"""
|
"""
|
||||||
self.config.chess_weights[chess_name] = weight
|
self.chess_weights[chess_name] = weight
|
||||||
logger.info(f"设置棋子权重: {chess_name} = {weight}")
|
# 同时更新全局配置
|
||||||
|
weights_config = get_global_weights_config()
|
||||||
|
weights_config.set_chess_weight(chess_name, weight)
|
||||||
|
|
||||||
def reload_config(self, config_path: Optional[str] = None) -> None:
|
def set_synergy_level_weight(self, level: int, weight: float) -> None:
|
||||||
"""
|
"""
|
||||||
重新加载配置文件
|
设置特定羁绊等级的权重
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config_path: 配置文件路径,如果为None则使用默认路径
|
level: 羁绊等级
|
||||||
|
weight: 权重值
|
||||||
"""
|
"""
|
||||||
self._load_custom_weights(config_path)
|
self.level_multipliers[level] = weight
|
||||||
logger.info("重新加载配置文件完成")
|
# 同时更新全局配置
|
||||||
|
weights_config = get_global_weights_config()
|
||||||
|
weights_config.set_synergy_level_weight(str(level), weight)
|
||||||
|
|
||||||
|
def set_cost_weight(self, cost: str, weight: float) -> None:
|
||||||
|
"""
|
||||||
|
设置特定棋子费用的权重
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cost: 棋子费用
|
||||||
|
weight: 权重值
|
||||||
|
"""
|
||||||
|
self.cost_multipliers[cost] = weight
|
||||||
|
# 同时更新全局配置
|
||||||
|
weights_config = get_global_weights_config()
|
||||||
|
weights_config.set_cost_weight(cost, weight)
|
||||||
|
|
||||||
|
def set_level_multiplier(self, level: int, multiplier: float) -> None:
|
||||||
|
"""
|
||||||
|
设置特定羁绊等级的权重倍数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level: 羁绊等级
|
||||||
|
multiplier: 权重倍数
|
||||||
|
"""
|
||||||
|
self.level_multipliers[level] = multiplier
|
||||||
|
# 同时更新全局配置
|
||||||
|
weights_config = get_global_weights_config()
|
||||||
|
weights_config.set_level_multiplier(level, multiplier)
|
||||||
|
|
||||||
|
def set_cost_multiplier(self, cost: str, multiplier: float) -> None:
|
||||||
|
"""
|
||||||
|
设置特定棋子费用的权重倍数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cost: 棋子费用
|
||||||
|
multiplier: 权重倍数
|
||||||
|
"""
|
||||||
|
self.cost_multipliers[cost] = multiplier
|
||||||
|
# 同时更新全局配置
|
||||||
|
weights_config = get_global_weights_config()
|
||||||
|
weights_config.set_cost_multiplier(cost, multiplier)
|
||||||
|
|
||||||
|
def set_synergy_level_multiplier(self, level: int, multiplier: float) -> None:
|
||||||
|
"""
|
||||||
|
设置特定羁绊等级权重倍数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level: 羁绊等级
|
||||||
|
multiplier: 权重倍数
|
||||||
|
"""
|
||||||
|
self.level_multipliers[level] = multiplier
|
||||||
|
# 同时更新全局配置
|
||||||
|
weights_config = get_global_weights_config()
|
||||||
|
weights_config.set_synergy_level_multiplier(level, multiplier)
|
||||||
|
|
||||||
|
def set_cost_level_multiplier(self, level: int, multiplier: float) -> None:
|
||||||
|
"""
|
||||||
|
设置特定羁绊等级费用权重倍数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level: 羁绊等级
|
||||||
|
multiplier: 权重倍数
|
||||||
|
"""
|
||||||
|
self.level_multipliers[level] = multiplier
|
||||||
|
# 同时更新全局配置
|
||||||
|
weights_config = get_global_weights_config()
|
||||||
|
weights_config.set_cost_level_multiplier(level, multiplier)
|
@ -5,12 +5,13 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
# 添加项目根目录到Python路径
|
# 添加项目根目录到Python路径
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
from src.scoring.scoring_system import TeamScorer, ScoringConfig
|
from src.scoring.scoring_system import TeamScorer
|
||||||
|
from src.config import get_global_weights_config
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
@ -22,35 +23,63 @@ class MockTeam:
|
|||||||
chess_list: List[Dict]
|
chess_list: List[Dict]
|
||||||
synergy_levels: Dict
|
synergy_levels: Dict
|
||||||
|
|
||||||
def main(config_path=None):
|
def main(config_path: Optional[str] = None):
|
||||||
"""主函数"""
|
"""
|
||||||
|
主函数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path: 配置文件路径,用于全局权重配置
|
||||||
|
"""
|
||||||
logger.info("开始测试评分系统...")
|
logger.info("开始测试评分系统...")
|
||||||
|
|
||||||
|
# 加载全局权重配置
|
||||||
|
weights_config = get_global_weights_config(config_path)
|
||||||
|
|
||||||
# 创建评分器实例
|
# 创建评分器实例
|
||||||
scorer = TeamScorer(config_path=config_path)
|
scorer = TeamScorer(config_path=config_path)
|
||||||
|
|
||||||
# 打印当前配置
|
# 打印全局配置
|
||||||
logger.info("基础权重:")
|
logger.info("基础权重:")
|
||||||
logger.info(f" 羁绊等级权重: {scorer.config.synergy_level_weight}")
|
base_weights = weights_config.get_base_weights()
|
||||||
logger.info(f" 羁绊数量权重: {scorer.config.synergy_count_weight}")
|
for param, value in base_weights.items():
|
||||||
logger.info(f" 棋子费用权重: {scorer.config.chess_cost_weight}")
|
logger.info(f" {param}: {value}")
|
||||||
|
|
||||||
logger.info("羁绊等级倍数:")
|
logger.info("羁绊等级倍数:")
|
||||||
for level, multiplier in sorted(scorer.config.level_multipliers.items()):
|
level_weights = weights_config.get_synergy_level_weights()
|
||||||
|
for level, multiplier in sorted(level_weights.items(), key=lambda x: int(x[0])):
|
||||||
logger.info(f" 等级 {level}: {multiplier}")
|
logger.info(f" 等级 {level}: {multiplier}")
|
||||||
|
|
||||||
logger.info("棋子费用倍数:")
|
logger.info("棋子费用倍数:")
|
||||||
for cost, multiplier in sorted(scorer.config.cost_multipliers.items()):
|
cost_weights = weights_config.get_cost_weights()
|
||||||
|
for cost, multiplier in sorted(cost_weights.items(), key=lambda x: int(x[0])):
|
||||||
logger.info(f" 费用 {cost}: {multiplier}")
|
logger.info(f" 费用 {cost}: {multiplier}")
|
||||||
|
|
||||||
logger.info("羁绊自定义权重 (前5个):")
|
logger.info("羁绊自定义权重 (前5个):")
|
||||||
for name, weight in list(scorer.config.synergy_weights.items())[:5]:
|
synergy_weights = weights_config.get_synergy_weights()
|
||||||
|
for name, weight in list(sorted(synergy_weights.items()))[:5]:
|
||||||
logger.info(f" {name}: {weight}")
|
logger.info(f" {name}: {weight}")
|
||||||
|
|
||||||
logger.info("棋子自定义权重 (前5个):")
|
logger.info("棋子自定义权重 (前5个):")
|
||||||
for name, weight in list(scorer.config.chess_weights.items())[:5]:
|
chess_weights = weights_config.get_chess_weights()
|
||||||
|
for name, weight in list(sorted(chess_weights.items()))[:5]:
|
||||||
logger.info(f" {name}: {weight}")
|
logger.info(f" {name}: {weight}")
|
||||||
|
|
||||||
|
# 测试修改权重
|
||||||
|
logger.info("\n测试修改权重:")
|
||||||
|
test_synergy = next(iter(synergy_weights.keys()))
|
||||||
|
original_weight = synergy_weights.get(test_synergy, 1.0)
|
||||||
|
new_weight = original_weight + 0.5
|
||||||
|
|
||||||
|
logger.info(f"将羁绊 {test_synergy} 的权重从 {original_weight} 修改为 {new_weight}")
|
||||||
|
weights_config.set_synergy_weight(test_synergy, new_weight)
|
||||||
|
|
||||||
|
# 确认修改生效
|
||||||
|
updated_weight = weights_config.get_synergy_weights().get(test_synergy)
|
||||||
|
logger.info(f"修改后的权重: {updated_weight}")
|
||||||
|
|
||||||
|
# 重新加载评分系统配置
|
||||||
|
scorer.reload_config()
|
||||||
|
|
||||||
# 测试评分函数
|
# 测试评分函数
|
||||||
mock_team = MockTeam(
|
mock_team = MockTeam(
|
||||||
chess_list=[
|
chess_list=[
|
||||||
@ -73,6 +102,15 @@ def main(config_path=None):
|
|||||||
score = scorer.score_team(mock_team)
|
score = scorer.score_team(mock_team)
|
||||||
logger.info(f"模拟阵容评分: {score}")
|
logger.info(f"模拟阵容评分: {score}")
|
||||||
|
|
||||||
|
# 恢复原来的权重
|
||||||
|
logger.info(f"将羁绊 {test_synergy} 的权重恢复为 {original_weight}")
|
||||||
|
weights_config.set_synergy_weight(test_synergy, original_weight)
|
||||||
|
|
||||||
|
# 重新评分
|
||||||
|
scorer.reload_config()
|
||||||
|
score = scorer.score_team(mock_team)
|
||||||
|
logger.info(f"恢复权重后的评分: {score}")
|
||||||
|
|
||||||
logger.info("测试完成!")
|
logger.info("测试完成!")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
150
src/web/app.py
150
src/web/app.py
@ -4,14 +4,16 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import copy
|
||||||
from flask import Flask, render_template, request, jsonify
|
from flask import Flask, render_template, request, jsonify
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
from waitress import serve
|
from waitress import serve
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
|
||||||
from src.data_provider import DataQueryAPI
|
from src.data_provider import DataQueryAPI
|
||||||
from src.recommendation import RecommendationEngine
|
from src.recommendation import RecommendationEngine
|
||||||
from src.scoring.scoring_system import TeamScorer, ScoringConfig
|
from src.scoring.scoring_system import TeamScorer
|
||||||
from src.scoring.config_loader import ConfigLoader
|
from src.config import get_global_weights_config, WeightsConfig
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -20,8 +22,13 @@ logging.basicConfig(
|
|||||||
)
|
)
|
||||||
logger = logging.getLogger("TFT-Strategist-Web")
|
logger = logging.getLogger("TFT-Strategist-Web")
|
||||||
|
|
||||||
def create_app():
|
def create_app(config_path: Optional[str] = None):
|
||||||
"""创建Flask应用实例"""
|
"""
|
||||||
|
创建Flask应用实例
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path: 配置文件路径,用于全局权重配置
|
||||||
|
"""
|
||||||
app = Flask(__name__,
|
app = Flask(__name__,
|
||||||
static_folder='static',
|
static_folder='static',
|
||||||
template_folder='templates')
|
template_folder='templates')
|
||||||
@ -29,10 +36,16 @@ def create_app():
|
|||||||
# 启用CORS,允许跨域请求
|
# 启用CORS,允许跨域请求
|
||||||
CORS(app)
|
CORS(app)
|
||||||
|
|
||||||
# 加载数据
|
# 加载全局权重配置
|
||||||
|
weights_config = get_global_weights_config(config_path)
|
||||||
|
|
||||||
|
# 加载数据和组件
|
||||||
data_api = DataQueryAPI()
|
data_api = DataQueryAPI()
|
||||||
engine = RecommendationEngine(api=data_api)
|
scorer = TeamScorer(config_path=config_path)
|
||||||
config_loader = ConfigLoader()
|
engine = RecommendationEngine(api=data_api, scorer=scorer, config_path=config_path)
|
||||||
|
|
||||||
|
# 原始默认配置,用于重置操作
|
||||||
|
default_config = copy.deepcopy(weights_config.config_data)
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
@ -43,15 +56,17 @@ def create_app():
|
|||||||
all_synergies = all_jobs + all_races
|
all_synergies = all_jobs + all_races
|
||||||
all_chess = data_api.get_all_chess()
|
all_chess = data_api.get_all_chess()
|
||||||
|
|
||||||
# 获取配置文件中的权重信息
|
# 获取全局配置中的权重信息
|
||||||
weights_config = config_loader.load_config()
|
base_weights = weights_config.get_base_weights()
|
||||||
|
synergy_weights = weights_config.get_synergy_weights()
|
||||||
|
chess_weights = weights_config.get_chess_weights()
|
||||||
|
|
||||||
return render_template('index.html',
|
return render_template('index.html',
|
||||||
synergies=all_synergies,
|
synergies=all_synergies,
|
||||||
chess=all_chess,
|
chess=all_chess,
|
||||||
base_weights=weights_config.get('base_weights', {}),
|
base_weights=base_weights,
|
||||||
synergy_weights=weights_config.get('synergy_weights', {}),
|
synergy_weights=synergy_weights,
|
||||||
chess_weights=weights_config.get('chess_weights', {}))
|
chess_weights=chess_weights)
|
||||||
|
|
||||||
@app.route('/api/recommend', methods=['POST'])
|
@app.route('/api/recommend', methods=['POST'])
|
||||||
def recommend():
|
def recommend():
|
||||||
@ -67,6 +82,20 @@ def create_app():
|
|||||||
synergy_weights = data.get('synergy_weights', {})
|
synergy_weights = data.get('synergy_weights', {})
|
||||||
chess_weights = data.get('chess_weights', {})
|
chess_weights = data.get('chess_weights', {})
|
||||||
|
|
||||||
|
# 创建临时配置对象,不修改全局配置
|
||||||
|
temp_config = create_temp_config(
|
||||||
|
weights_config.config_data,
|
||||||
|
base_weights,
|
||||||
|
synergy_weights,
|
||||||
|
chess_weights
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建临时评分器
|
||||||
|
temp_scorer = TeamScorer(config_obj=temp_config)
|
||||||
|
|
||||||
|
# 创建临时推荐引擎
|
||||||
|
temp_engine = RecommendationEngine(api=data_api, scorer=temp_scorer, config_obj=temp_config)
|
||||||
|
|
||||||
# 获取必选棋子和必选羁绊
|
# 获取必选棋子和必选羁绊
|
||||||
required_chess_ids = data.get('required_chess', [])
|
required_chess_ids = data.get('required_chess', [])
|
||||||
required_synergy_ids = data.get('required_synergies', [])
|
required_synergy_ids = data.get('required_synergies', [])
|
||||||
@ -75,20 +104,8 @@ def create_app():
|
|||||||
required_chess = [{'id': str(chess_id)} for chess_id in required_chess_ids]
|
required_chess = [{'id': str(chess_id)} for chess_id in required_chess_ids]
|
||||||
required_synergies = [{'id': str(synergy_id)} for synergy_id in required_synergy_ids]
|
required_synergies = [{'id': str(synergy_id)} for synergy_id in required_synergy_ids]
|
||||||
|
|
||||||
# 创建评分配置
|
|
||||||
scoring_config = ScoringConfig(
|
|
||||||
synergy_level_weight=base_weights.get('synergy_level_weight', 1.0),
|
|
||||||
synergy_count_weight=base_weights.get('synergy_count_weight', 0.5),
|
|
||||||
chess_cost_weight=base_weights.get('chess_cost_weight', 0.1),
|
|
||||||
synergy_weights=synergy_weights,
|
|
||||||
chess_weights=chess_weights
|
|
||||||
)
|
|
||||||
|
|
||||||
# 创建评分系统
|
|
||||||
scorer = TeamScorer(config=scoring_config)
|
|
||||||
|
|
||||||
# 调用推荐引擎生成阵容
|
# 调用推荐引擎生成阵容
|
||||||
teams = engine.recommend_team(
|
teams = temp_engine.recommend_team(
|
||||||
population=population,
|
population=population,
|
||||||
required_synergies=required_synergies,
|
required_synergies=required_synergies,
|
||||||
required_chess=required_chess,
|
required_chess=required_chess,
|
||||||
@ -153,11 +170,88 @@ def create_app():
|
|||||||
'message': str(e)
|
'message': str(e)
|
||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
|
@app.route('/api/weights', methods=['GET'])
|
||||||
|
def get_weights():
|
||||||
|
"""获取当前权重配置API"""
|
||||||
|
return jsonify({
|
||||||
|
'base_weights': weights_config.get_base_weights(),
|
||||||
|
'synergy_weights': weights_config.get_synergy_weights(),
|
||||||
|
'chess_weights': weights_config.get_chess_weights(),
|
||||||
|
'synergy_level_weights': weights_config.get_synergy_level_weights(),
|
||||||
|
'cost_weights': weights_config.get_cost_weights()
|
||||||
|
})
|
||||||
|
|
||||||
|
@app.route('/api/weights', methods=['POST'])
|
||||||
|
def update_weights():
|
||||||
|
"""更新权重配置API - 已弃用,仅保留API兼容性"""
|
||||||
|
try:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'success',
|
||||||
|
'message': '权重配置已更新(本地存储模式)'
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("更新权重配置时发生错误")
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'message': str(e)
|
||||||
|
}), 500
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
def run_server(host='0.0.0.0', port=5000, dev_mode=False):
|
def create_temp_config(base_config: Dict[str, Any],
|
||||||
"""运行Web服务器"""
|
base_weights: Dict[str, float],
|
||||||
app = create_app()
|
synergy_weights: Dict[str, float],
|
||||||
|
chess_weights: Dict[str, float]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建临时配置对象,不修改原始配置
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base_config: 基础配置
|
||||||
|
base_weights: 用户自定义的基础权重
|
||||||
|
synergy_weights: 用户自定义的羁绊权重
|
||||||
|
chess_weights: 用户自定义的棋子权重
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: 临时配置对象
|
||||||
|
"""
|
||||||
|
# 深拷贝基础配置,避免修改原始配置
|
||||||
|
temp_config = copy.deepcopy(base_config)
|
||||||
|
|
||||||
|
# 更新基础权重
|
||||||
|
if 'base_weights' not in temp_config:
|
||||||
|
temp_config['base_weights'] = {}
|
||||||
|
|
||||||
|
for param, value in base_weights.items():
|
||||||
|
temp_config['base_weights'][param] = value
|
||||||
|
|
||||||
|
# 更新羁绊权重
|
||||||
|
if 'synergy_weights' not in temp_config:
|
||||||
|
temp_config['synergy_weights'] = {}
|
||||||
|
|
||||||
|
for synergy_name, weight in synergy_weights.items():
|
||||||
|
temp_config['synergy_weights'][synergy_name] = weight
|
||||||
|
|
||||||
|
# 更新棋子权重
|
||||||
|
if 'chess_weights' not in temp_config:
|
||||||
|
temp_config['chess_weights'] = {}
|
||||||
|
|
||||||
|
for chess_name, weight in chess_weights.items():
|
||||||
|
temp_config['chess_weights'][chess_name] = weight
|
||||||
|
|
||||||
|
return temp_config
|
||||||
|
|
||||||
|
def run_server(host='0.0.0.0', port=5000, dev_mode=False, config_path=None):
|
||||||
|
"""
|
||||||
|
运行Web服务器
|
||||||
|
|
||||||
|
Args:
|
||||||
|
host: 服务器主机地址
|
||||||
|
port: 服务器端口
|
||||||
|
dev_mode: 是否启用开发模式
|
||||||
|
config_path: 配置文件路径
|
||||||
|
"""
|
||||||
|
app = create_app(config_path)
|
||||||
logger.info(f"启动Web服务器: {'开发模式' if dev_mode else '生产模式'}")
|
logger.info(f"启动Web服务器: {'开发模式' if dev_mode else '生产模式'}")
|
||||||
|
|
||||||
if dev_mode:
|
if dev_mode:
|
||||||
|
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
// 页面加载完成后执行
|
// 页面加载完成后执行
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
// 初始化抽屉
|
||||||
|
initDrawer();
|
||||||
|
|
||||||
|
// 加载本地存储的权重
|
||||||
|
loadWeightsFromLocalStorage();
|
||||||
|
|
||||||
// 初始化基础权重滑块
|
// 初始化基础权重滑块
|
||||||
initBasicWeightSliders();
|
initBasicWeightSliders();
|
||||||
|
|
||||||
@ -119,16 +125,182 @@ $(document).ready(function() {
|
|||||||
$('.chess-weight-item').show();
|
$('.chess-weight-item').show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听权重变化,保存到本地存储
|
||||||
|
$(document).on('change', '.noUi-target', function() {
|
||||||
|
saveWeightsToLocalStorage();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 恢复默认配置按钮
|
||||||
|
$('#reset-to-default').on('click', function() {
|
||||||
|
resetToDefaultWeights();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化抽屉控制
|
||||||
|
*/
|
||||||
|
function initDrawer() {
|
||||||
|
// 打开抽屉
|
||||||
|
$('#open-weights-drawer').on('click', function() {
|
||||||
|
$('#weights-drawer').addClass('open');
|
||||||
|
$('#drawer-backdrop').addClass('open');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 关闭抽屉
|
||||||
|
$('#close-drawer, #drawer-backdrop').on('click', function() {
|
||||||
|
$('#weights-drawer').removeClass('open');
|
||||||
|
$('#drawer-backdrop').removeClass('open');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从本地存储加载权重设置
|
||||||
|
*/
|
||||||
|
function loadWeightsFromLocalStorage() {
|
||||||
|
const storedWeights = localStorage.getItem('tft-weights');
|
||||||
|
if (!storedWeights) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const weights = JSON.parse(storedWeights);
|
||||||
|
|
||||||
|
// 设置基础权重
|
||||||
|
if (weights.base_weights) {
|
||||||
|
if (weights.base_weights.synergy_level_weight) {
|
||||||
|
$('#synergy-level-weight-value').text(weights.base_weights.synergy_level_weight.toFixed(1));
|
||||||
|
}
|
||||||
|
if (weights.base_weights.synergy_count_weight) {
|
||||||
|
$('#synergy-count-weight-value').text(weights.base_weights.synergy_count_weight.toFixed(1));
|
||||||
|
}
|
||||||
|
if (weights.base_weights.chess_cost_weight) {
|
||||||
|
$('#chess-cost-weight-value').text(weights.base_weights.chess_cost_weight.toFixed(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置羁绊权重
|
||||||
|
if (weights.synergy_weights) {
|
||||||
|
for (const [synergyId, weight] of Object.entries(weights.synergy_weights)) {
|
||||||
|
$(`.synergy-weight-item[data-synergy-id="${synergyId}"] .synergy-weight-value`).text(weight.toFixed(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置棋子权重
|
||||||
|
if (weights.chess_weights) {
|
||||||
|
for (const [chessId, weight] of Object.entries(weights.chess_weights)) {
|
||||||
|
$(`.chess-weight-item[data-chess-id="${chessId}"] .chess-weight-value`).text(weight.toFixed(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载权重设置失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存权重设置到本地存储
|
||||||
|
*/
|
||||||
|
function saveWeightsToLocalStorage() {
|
||||||
|
// 获取基础权重
|
||||||
|
const synergyLevelWeight = parseFloat($('#synergy-level-weight-value').text());
|
||||||
|
const synergyCountWeight = parseFloat($('#synergy-count-weight-value').text());
|
||||||
|
const chessCountWeight = parseFloat($('#chess-cost-weight-value').text());
|
||||||
|
|
||||||
|
// 获取羁绊权重
|
||||||
|
const synergyWeights = {};
|
||||||
|
$('.synergy-weight-item').each(function() {
|
||||||
|
const synergyId = $(this).data('synergy-id');
|
||||||
|
const weight = parseFloat($(this).find('.synergy-weight-value').text());
|
||||||
|
synergyWeights[synergyId] = weight;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取棋子权重
|
||||||
|
const chessWeights = {};
|
||||||
|
$('.chess-weight-item').each(function() {
|
||||||
|
const chessId = $(this).data('chess-id');
|
||||||
|
const weight = parseFloat($(this).find('.chess-weight-value').text());
|
||||||
|
chessWeights[chessId] = weight;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 构建权重对象
|
||||||
|
const weightsObj = {
|
||||||
|
base_weights: {
|
||||||
|
synergy_level_weight: synergyLevelWeight,
|
||||||
|
synergy_count_weight: synergyCountWeight,
|
||||||
|
chess_cost_weight: chessCountWeight
|
||||||
|
},
|
||||||
|
synergy_weights: synergyWeights,
|
||||||
|
chess_weights: chessWeights
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存到本地存储
|
||||||
|
localStorage.setItem('tft-weights', JSON.stringify(weightsObj));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置为默认权重
|
||||||
|
*/
|
||||||
|
function resetToDefaultWeights() {
|
||||||
|
// 发送请求获取默认权重
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/weights',
|
||||||
|
type: 'GET',
|
||||||
|
success: function(response) {
|
||||||
|
// 更新基础权重
|
||||||
|
if (response.base_weights) {
|
||||||
|
if (response.base_weights.synergy_level_weight) {
|
||||||
|
const slider = document.getElementById('synergy-level-weight-slider').noUiSlider;
|
||||||
|
slider.set(response.base_weights.synergy_level_weight);
|
||||||
|
}
|
||||||
|
if (response.base_weights.synergy_count_weight) {
|
||||||
|
const slider = document.getElementById('synergy-count-weight-slider').noUiSlider;
|
||||||
|
slider.set(response.base_weights.synergy_count_weight);
|
||||||
|
}
|
||||||
|
if (response.base_weights.chess_cost_weight) {
|
||||||
|
const slider = document.getElementById('chess-cost-weight-slider').noUiSlider;
|
||||||
|
slider.set(response.base_weights.chess_cost_weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新羁绊权重
|
||||||
|
if (response.synergy_weights) {
|
||||||
|
for (const [synergyId, weight] of Object.entries(response.synergy_weights)) {
|
||||||
|
const slider = $(`.synergy-weight-item[data-synergy-id="${synergyId}"] .synergy-weight-slider`)[0];
|
||||||
|
if (slider && slider.noUiSlider) {
|
||||||
|
slider.noUiSlider.set(weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新棋子权重
|
||||||
|
if (response.chess_weights) {
|
||||||
|
for (const [chessId, weight] of Object.entries(response.chess_weights)) {
|
||||||
|
const slider = $(`.chess-weight-item[data-chess-id="${chessId}"] .chess-weight-slider`)[0];
|
||||||
|
if (slider && slider.noUiSlider) {
|
||||||
|
slider.noUiSlider.set(weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除本地存储
|
||||||
|
localStorage.removeItem('tft-weights');
|
||||||
|
|
||||||
|
alert('已恢复默认权重设置');
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
alert(`恢复默认权重失败: ${error}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化基础权重滑块
|
* 初始化基础权重滑块
|
||||||
*/
|
*/
|
||||||
function initBasicWeightSliders() {
|
function initBasicWeightSliders() {
|
||||||
// 羁绊等级权重
|
// 羁绊等级权重
|
||||||
const synergyLevelSlider = document.getElementById('synergy-level-weight-slider');
|
const synergyLevelSlider = document.getElementById('synergy-level-weight-slider');
|
||||||
|
const synergyLevelValue = parseFloat($('#synergy-level-weight-value').text());
|
||||||
|
|
||||||
noUiSlider.create(synergyLevelSlider, {
|
noUiSlider.create(synergyLevelSlider, {
|
||||||
start: [1.0],
|
start: [synergyLevelValue],
|
||||||
connect: [true, false],
|
connect: [true, false],
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
range: {
|
range: {
|
||||||
@ -144,8 +316,10 @@ function initBasicWeightSliders() {
|
|||||||
|
|
||||||
// 羁绊数量权重
|
// 羁绊数量权重
|
||||||
const synergyCountSlider = document.getElementById('synergy-count-weight-slider');
|
const synergyCountSlider = document.getElementById('synergy-count-weight-slider');
|
||||||
|
const synergyCountValue = parseFloat($('#synergy-count-weight-value').text());
|
||||||
|
|
||||||
noUiSlider.create(synergyCountSlider, {
|
noUiSlider.create(synergyCountSlider, {
|
||||||
start: [0.5],
|
start: [synergyCountValue],
|
||||||
connect: [true, false],
|
connect: [true, false],
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
range: {
|
range: {
|
||||||
@ -161,8 +335,10 @@ function initBasicWeightSliders() {
|
|||||||
|
|
||||||
// 棋子费用权重
|
// 棋子费用权重
|
||||||
const chessCountSlider = document.getElementById('chess-cost-weight-slider');
|
const chessCountSlider = document.getElementById('chess-cost-weight-slider');
|
||||||
|
const chessCountValue = parseFloat($('#chess-cost-weight-value').text());
|
||||||
|
|
||||||
noUiSlider.create(chessCountSlider, {
|
noUiSlider.create(chessCountSlider, {
|
||||||
start: [0.1],
|
start: [chessCountValue],
|
||||||
connect: [true, false],
|
connect: [true, false],
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
range: {
|
range: {
|
||||||
@ -289,6 +465,9 @@ function generateTeam() {
|
|||||||
required_chess: requiredChess
|
required_chess: requiredChess
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 保存当前设置到本地存储
|
||||||
|
saveWeightsToLocalStorage();
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/api/recommend',
|
url: '/api/recommend',
|
||||||
|
@ -13,14 +13,139 @@
|
|||||||
<script src="https://unpkg.com/nouislider@15.5.1/dist/nouislider.min.js"></script>
|
<script src="https://unpkg.com/nouislider@15.5.1/dist/nouislider.min.js"></script>
|
||||||
<!-- 自定义样式 -->
|
<!-- 自定义样式 -->
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||||
|
<style>
|
||||||
|
/* 抽屉样式 */
|
||||||
|
.drawer {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: -100%;
|
||||||
|
height: 100vh;
|
||||||
|
width: 320px;
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: right 0.3s ease;
|
||||||
|
z-index: 1000;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.drawer.open {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.drawer-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 999;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.drawer-backdrop.open {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-100 min-h-screen">
|
<body class="bg-gray-100 min-h-screen">
|
||||||
|
<!-- 抽屉背景遮罩 -->
|
||||||
|
<div class="drawer-backdrop" id="drawer-backdrop"></div>
|
||||||
|
|
||||||
|
<!-- 权重设置抽屉 -->
|
||||||
|
<div class="drawer" id="weights-drawer">
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<h2 class="text-xl font-semibold text-indigo-700">权重设置</h2>
|
||||||
|
<button id="close-drawer" class="text-gray-500 hover:text-gray-700">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<button id="reset-to-default" class="w-full bg-gray-200 text-gray-800 px-4 py-2 rounded-lg hover:bg-gray-300 transition-colors mb-4">
|
||||||
|
恢复默认配置
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="text-lg font-semibold mb-4 text-indigo-700">基础权重</h3>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-700 mb-2">羁绊等级权重</label>
|
||||||
|
<div id="synergy-level-weight-slider" class="weight-slider"></div>
|
||||||
|
<div class="flex justify-between mt-1">
|
||||||
|
<span class="text-xs text-gray-500">0.0</span>
|
||||||
|
<span id="synergy-level-weight-value" class="text-xs text-gray-700">1.0</span>
|
||||||
|
<span class="text-xs text-gray-500">3.0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-700 mb-2">羁绊数量权重</label>
|
||||||
|
<div id="synergy-count-weight-slider" class="weight-slider"></div>
|
||||||
|
<div class="flex justify-between mt-1">
|
||||||
|
<span class="text-xs text-gray-500">0.0</span>
|
||||||
|
<span id="synergy-count-weight-value" class="text-xs text-gray-700">0.5</span>
|
||||||
|
<span class="text-xs text-gray-500">3.0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<label class="block text-gray-700 mb-2">棋子费用权重</label>
|
||||||
|
<div id="chess-cost-weight-slider" class="weight-slider"></div>
|
||||||
|
<div class="flex justify-between mt-1">
|
||||||
|
<span class="text-xs text-gray-500">0.0</span>
|
||||||
|
<span id="chess-cost-weight-value" class="text-xs text-gray-700">0.1</span>
|
||||||
|
<span class="text-xs text-gray-500">1.0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<h3 class="text-lg font-semibold text-indigo-700">羁绊权重设置</h3>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input type="checkbox" id="show-only-active" class="mr-2">
|
||||||
|
<label for="show-only-active" class="text-sm text-gray-700">仅显示已激活</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="synergy-weights-container" class="space-y-4 mb-6">
|
||||||
|
{% for synergy_id, weight in synergy_weights.items() %}
|
||||||
|
<div class="synergy-weight-item" data-synergy-id="{{ synergy_id }}">
|
||||||
|
<div class="flex items-center justify-between mb-1">
|
||||||
|
<label class="text-gray-700">{{ synergy_id }}</label>
|
||||||
|
<span class="synergy-weight-value text-xs text-gray-700">{{ weight }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="synergy-weight-slider"></div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<h3 class="text-lg font-semibold text-indigo-700">棋子权重设置</h3>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input type="checkbox" id="show-only-active-chess" class="mr-2">
|
||||||
|
<label for="show-only-active-chess" class="text-sm text-gray-700">仅显示已激活</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="chess-weights-container" class="space-y-4">
|
||||||
|
{% for chess_id, weight in chess_weights.items() %}
|
||||||
|
<div class="chess-weight-item" data-chess-id="{{ chess_id }}">
|
||||||
|
<div class="flex items-center justify-between mb-1">
|
||||||
|
<label class="text-gray-700">{{ chess_id }}</label>
|
||||||
|
<span class="chess-weight-value text-xs text-gray-700">{{ weight }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="chess-weight-slider"></div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container mx-auto px-4 py-8">
|
<div class="container mx-auto px-4 py-8">
|
||||||
<h1 class="text-3xl font-bold text-center text-indigo-600 mb-8">云顶之弈阵容推荐器</h1>
|
<h1 class="text-3xl font-bold text-center text-indigo-600 mb-8">云顶之弈阵容推荐器</h1>
|
||||||
|
|
||||||
<!-- 主要内容区 -->
|
<!-- 主要内容区 -->
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
<!-- 左侧权重设置区 -->
|
<!-- 左侧设置区 -->
|
||||||
<div class="lg:col-span-1 bg-white rounded-lg shadow-md p-6">
|
<div class="lg:col-span-1 bg-white rounded-lg shadow-md p-6">
|
||||||
<h2 class="text-xl font-semibold mb-4 text-indigo-700">基础设置</h2>
|
<h2 class="text-xl font-semibold mb-4 text-indigo-700">基础设置</h2>
|
||||||
|
|
||||||
@ -42,36 +167,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold mb-4 mt-6 text-indigo-700">基础权重</h2>
|
<div class="flex mb-4">
|
||||||
|
<button id="open-weights-drawer" class="bg-indigo-600 text-white px-4 py-2 rounded hover:bg-indigo-700 mr-2">
|
||||||
<div class="mb-4">
|
打开权重设置
|
||||||
<label class="block text-gray-700 mb-2">羁绊等级权重</label>
|
</button>
|
||||||
<div id="synergy-level-weight-slider" class="weight-slider"></div>
|
|
||||||
<div class="flex justify-between mt-1">
|
|
||||||
<span class="text-xs text-gray-500">0.0</span>
|
|
||||||
<span id="synergy-level-weight-value" class="text-xs text-gray-700">1.0</span>
|
|
||||||
<span class="text-xs text-gray-500">3.0</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-4">
|
|
||||||
<label class="block text-gray-700 mb-2">羁绊数量权重</label>
|
|
||||||
<div id="synergy-count-weight-slider" class="weight-slider"></div>
|
|
||||||
<div class="flex justify-between mt-1">
|
|
||||||
<span class="text-xs text-gray-500">0.0</span>
|
|
||||||
<span id="synergy-count-weight-value" class="text-xs text-gray-700">0.5</span>
|
|
||||||
<span class="text-xs text-gray-500">3.0</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-6">
|
|
||||||
<label class="block text-gray-700 mb-2">棋子费用权重</label>
|
|
||||||
<div id="chess-cost-weight-slider" class="weight-slider"></div>
|
|
||||||
<div class="flex justify-between mt-1">
|
|
||||||
<span class="text-xs text-gray-500">0.0</span>
|
|
||||||
<span id="chess-cost-weight-value" class="text-xs text-gray-700">0.1</span>
|
|
||||||
<span class="text-xs text-gray-500">1.0</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 必要羁绊设置 -->
|
<!-- 必要羁绊设置 -->
|
||||||
@ -110,49 +209,6 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 中间自定义权重设置区 -->
|
|
||||||
<div class="lg:col-span-1 bg-white rounded-lg shadow-md p-6 h-[800px] overflow-y-auto">
|
|
||||||
<div class="flex items-center justify-between mb-4">
|
|
||||||
<h2 class="text-xl font-semibold text-indigo-700">羁绊权重设置</h2>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<input type="checkbox" id="show-only-active" class="mr-2">
|
|
||||||
<label for="show-only-active" class="text-sm text-gray-700">仅显示已激活</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="synergy-weights-container" class="space-y-4">
|
|
||||||
{% for synergy_id, weight in synergy_weights.items() %}
|
|
||||||
<div class="synergy-weight-item" data-synergy-id="{{ synergy_id }}">
|
|
||||||
<div class="flex items-center justify-between mb-1">
|
|
||||||
<label class="text-gray-700">{{ synergy_id }}</label>
|
|
||||||
<span class="synergy-weight-value text-xs text-gray-700">{{ weight }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="synergy-weight-slider"></div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center justify-between mt-8 mb-4">
|
|
||||||
<h2 class="text-xl font-semibold text-indigo-700">棋子权重设置</h2>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<input type="checkbox" id="show-only-active-chess" class="mr-2">
|
|
||||||
<label for="show-only-active-chess" class="text-sm text-gray-700">仅显示已激活</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="chess-weights-container" class="space-y-4">
|
|
||||||
{% for chess_id, weight in chess_weights.items() %}
|
|
||||||
<div class="chess-weight-item" data-chess-id="{{ chess_id }}">
|
|
||||||
<div class="flex items-center justify-between mb-1">
|
|
||||||
<label class="text-gray-700">{{ chess_id }}</label>
|
|
||||||
<span class="chess-weight-value text-xs text-gray-700">{{ weight }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="chess-weight-slider"></div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 右侧结果展示区 -->
|
<!-- 右侧结果展示区 -->
|
||||||
<div class="lg:col-span-1 bg-white rounded-lg shadow-md p-6 h-[800px] overflow-y-auto">
|
<div class="lg:col-span-1 bg-white rounded-lg shadow-md p-6 h-[800px] overflow-y-auto">
|
||||||
<h2 class="text-xl font-semibold mb-4 text-indigo-700">阵容推荐结果</h2>
|
<h2 class="text-xl font-semibold mb-4 text-indigo-700">阵容推荐结果</h2>
|
||||||
|
88
test_direct.py
Normal file
88
test_direct.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
"""
|
||||||
|
直接测试阵容推荐功能,不通过HTTP请求
|
||||||
|
"""
|
||||||
|
from src.data_provider import DataQueryAPI
|
||||||
|
from src.scoring.scoring_system import TeamScorer
|
||||||
|
from src.recommendation import RecommendationEngine
|
||||||
|
import copy
|
||||||
|
|
||||||
|
def test_direct_recommendation():
|
||||||
|
"""直接测试阵容推荐功能"""
|
||||||
|
# 创建自定义配置
|
||||||
|
config = {
|
||||||
|
"base_weights": {
|
||||||
|
"synergy_level_weight": 1.5,
|
||||||
|
"synergy_count_weight": 0.8,
|
||||||
|
"chess_cost_weight": 0.3
|
||||||
|
},
|
||||||
|
"synergy_weights": {
|
||||||
|
"重装战士": 2.0,
|
||||||
|
"斗士": 1.8
|
||||||
|
},
|
||||||
|
"chess_weights": {
|
||||||
|
"盖伦": 2.0,
|
||||||
|
"赛娜": 2.5
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 初始化组件
|
||||||
|
data_api = DataQueryAPI()
|
||||||
|
scorer = TeamScorer(api=data_api, config_obj=copy.deepcopy(config))
|
||||||
|
engine = RecommendationEngine(api=data_api, scorer=scorer, config_obj=copy.deepcopy(config))
|
||||||
|
|
||||||
|
# 定义参数
|
||||||
|
population = 9
|
||||||
|
required_synergies = [] # 这里可以添加必选羁绊
|
||||||
|
required_chess = [] # 这里可以添加必选棋子
|
||||||
|
max_results = 3
|
||||||
|
|
||||||
|
# 生成阵容推荐
|
||||||
|
print("正在生成阵容推荐...")
|
||||||
|
teams = engine.recommend_team(
|
||||||
|
population=population,
|
||||||
|
required_synergies=required_synergies,
|
||||||
|
required_chess=required_chess,
|
||||||
|
max_results=max_results
|
||||||
|
)
|
||||||
|
|
||||||
|
# 打印结果
|
||||||
|
print(f"生成了 {len(teams)} 个推荐阵容")
|
||||||
|
|
||||||
|
for i, team in enumerate(teams):
|
||||||
|
print(f"\n阵容 #{i+1} (评分: {team.score:.2f})")
|
||||||
|
|
||||||
|
print("\n棋子列表:")
|
||||||
|
for chess in team.chess_list:
|
||||||
|
print(f" {chess.get('displayName')} ({chess.get('price')}费)")
|
||||||
|
|
||||||
|
print("\n激活的职业羁绊:")
|
||||||
|
for job in team.synergy_levels['job']:
|
||||||
|
print(f" {job['name']} (等级 {job['level']})")
|
||||||
|
|
||||||
|
print("\n激活的特质羁绊:")
|
||||||
|
for race in team.synergy_levels['race']:
|
||||||
|
print(f" {race['name']} (等级 {race['level']})")
|
||||||
|
|
||||||
|
print("\n" + "-"*50)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_direct_recommendation()
|
66
test_recommendation.py
Normal file
66
test_recommendation.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
"""
|
||||||
|
测试阵容推荐功能
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
def test_recommendation():
|
||||||
|
"""
|
||||||
|
测试阵容推荐功能
|
||||||
|
"""
|
||||||
|
# 构建请求数据
|
||||||
|
data = {
|
||||||
|
"population": 9,
|
||||||
|
"num_results": 3,
|
||||||
|
"base_weights": {
|
||||||
|
"synergy_level_weight": 1.5,
|
||||||
|
"synergy_count_weight": 0.8,
|
||||||
|
"chess_cost_weight": 0.3
|
||||||
|
},
|
||||||
|
"synergy_weights": {
|
||||||
|
"重装战士": 2.0,
|
||||||
|
"斗士": 1.8
|
||||||
|
},
|
||||||
|
"chess_weights": {
|
||||||
|
"盖伦": 2.0,
|
||||||
|
"赛娜": 2.5
|
||||||
|
},
|
||||||
|
"required_synergies": [],
|
||||||
|
"required_chess": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# 发送POST请求
|
||||||
|
url = "http://localhost:5000/api/recommend"
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
|
||||||
|
# 检查响应状态码
|
||||||
|
if response.status_code == 200:
|
||||||
|
result = response.json()
|
||||||
|
print("请求成功!")
|
||||||
|
print(f"状态: {result['status']}")
|
||||||
|
print(f"推荐阵容数量: {len(result['results'])}")
|
||||||
|
|
||||||
|
# 打印第一个阵容的详细信息
|
||||||
|
if result['results']:
|
||||||
|
first_team = result['results'][0]
|
||||||
|
print(f"\n第一个推荐阵容评分: {first_team['score']}")
|
||||||
|
|
||||||
|
print("\n棋子列表:")
|
||||||
|
for chess in first_team['chess_list']:
|
||||||
|
print(f" {chess['name']} ({chess['cost']}费)")
|
||||||
|
|
||||||
|
print("\n激活羁绊:")
|
||||||
|
for synergy in first_team['active_synergies']:
|
||||||
|
print(f" {synergy['name']} (等级 {synergy['level']})")
|
||||||
|
else:
|
||||||
|
print(f"请求失败: {response.status_code}")
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"发生错误: {str(e)}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_recommendation()
|
Loading…
x
Reference in New Issue
Block a user