前言

上一篇我们拿到了云顶之弈的完整数据词典,但这只是第一步。真正有用的工具,是能把你手上任何形式的阵容一键转换成清晰的分析报告。

一、从“有了数据”到“能用起来”

上一篇文章里,我写了一个 tft_data_manager.py,可以从官方接口拉取最新赛季的英雄、羁绊、装备数据,生成本地 JSON 词典。有了这些原材料之后,下一个问题就是:怎么用这些数据解决实际问题?

目前云顶之弈玩家需求背后其实是一个共同的模式:

输入 = 英雄列表,输出 = 羁绊计算 + 阵容摘要 + 装备问题提示

于是,我动手写了 tft_converter.py,一个能自动识别输入格式、输出标准化分析结果的转换器。这篇博客就来拆解它的设计思路和实现细节,并补充完整的命令行入口,让你可以像使用普通工具一样直接运行它。

二、转换器能做什么?—— 输入输出一览

tft_converter.py 支持四种输入格式:

输入类型 举例 处理方式
Riot 官方对局 JSON 从 Riot API 拿到的 participant.units 直接解析,提取英雄、星级、装备、站位
截图识别结果 tft_screen_capture 模块的输出 委托给专门的识别模块,补全 cost
自由文本 "Aatrox, Riven, Garen" 正则匹配单词,在本地数据库里查找匹配英雄
手动 JSON 字符串 '[{"name":"Aatrox"}]'

按 Riot JSON 逻辑解析

无论输入是什么,最终都会输出一个统一结构的 JSON 对象:

{
  "team_size": 8,
  "champions": [
    {
      "id": "TFT16_Aatrox",
      "short_id": "Aatrox",
      "name_en": "Aatrox",
      "star": 2,
      "cost": 4,
      "items": ["TFT_Item_Bloodthirster"],
      "position": {"row": 3, "col": 2}
    }
  ],
  "traits": [
    {
      "id": "TFT16_Bruiser",
      "short_id": "Bruiser",
      "name_en": "Bruiser",
      "count": 4,
      "level": 4,
      "level_name": "Gold",
      "thresholds": [2, 4, 6]
    }
  ],
  "summary": {
    "front_row_ratio": "3/8",
    "main_carry": "Aatrox",
    "equipment_ok": false,
    "total_items": 5,
    "champion_count": 8
  },
  "equipment_issues": [
    "Aatrox(费用≥4) 无装备",
    "TFT_Item_GuinsoosRageblade 重复装备 x2"
  ],
  "_source": "riot_json"
}

这个结构非常清晰:英雄详情、激活的羁绊、阵容摘要、潜在问题列表,以及数据来源标记。

三、核心组件逐一拆解

1. ID 规范化 —— 让不同来源的命名都能对上号

来自 Riot API 的英雄 ID 可能是 TFT16_Aatrox,而玩家手打的文本可能是 Aatrox,截图识别出来的也可能是 aatrox。要让它们能在同一个数据库里查询,必须先统一格式。

def normalize_champ_id(raw: str, set_num: int = 16) -> str:
    if re.match(rf"^TFT{set_num}_", raw):
        return raw
    clean = strip_prefix(raw)
    return f"TFT{set_num}_{clean}"

strip_prefix函数的作用是移除各类前缀(如TFT16_TFTSet16_TFT_Item_等),然后统一添加标准前缀TFT{set_num}_。装备的处理方式类似,只是其标准前缀固定为TFT_Item_

这种规范化处理的必要性在于:只有统一格式的ID才能准确匹配本地数据库(由tft_data_manager.py生成)中的条目键值。

2. 数据库懒加载 —— 只读一次,全局复用

为了提高性能,转换器在首次加载时将英雄费用、羁绊列表和装备信息缓存为模块级全局变量,采用懒加载模式避免重复读取JSON文件。

_champion_db: Optional[Dict] = None
_trait_db:    Optional[Dict] = None
_item_db:     Optional[Dict] = None
_trait_dict:  Optional[Dict] = None

def _load_db():
    global _champion_db, _trait_db, _item_db, _trait_dict
    if _champion_db is not None:
        return
    _champion_db = _read("tft_champion_db.json")
    # ...

第一次调用 _load_db() 时,才会去读取四个 JSON 文件并缓存到全局变量中。后续所有查询都直接从内存拿,效率很高。

学生笔记:这种“懒加载”模式在实际项目中非常常见,特别是当数据文件比较大、或者不一定每次都会用到的时候。它兼顾了启动速度和资源占用。

3. 羁绊计算 —— 从英雄列表到激活的羁绊

讲解核心函数函数 calc_traits(champions, set_num)

步骤 1:统计每个羁绊有多少个英雄

trait_counts: Dict[str, int] = {}
for champ in champions:
    db_entry = _champion_db.get(champ_id, {})
    for t in db_entry.get("traits", []):
        trait_counts[t] = trait_counts.get(t, 0) + 1

这里直接用了上一篇文章生成的 tft_champion_db.json 里的 traits 字段(我们已经预先把它处理成了 short_id 列表)。

步骤 2:为每个羁绊查找激活等级

然后给每个已激活的羁绊打上一个等级标签(Bronze / Silver / Gold / Prismatic),方便阅读。

for short_id, count in trait_counts.items():
    # 在 _trait_db 或 _trait_dict 中找到对应的羁绊数据
    # 获取 levels 数组,例如 [2, 4, 6]
    for lvl in sorted(levels):
        if count >= lvl:
            active_level = lvl

最终按照激活等级降序、人数降序排列,这样最重要的羁绊会排在前面。

4. 摘要与装备问题检测 —— 给阵容一个“体检报告”

build_summary 函数负责生成人类友好的摘要和装备警告。我设计了几个简单但实用的检查项:

前后排比例

根据站位 position.row 来判断(行号 ≥ 3 算前排),输出如 "3/8" 的字符串。这个小细节对于评估阵容坦度很有帮助。

主C识别

遍历所有英雄,找出有装备且费用最高的那一个作为“主C”。虽然逻辑简单,但在大多数情况下是准确的。

装备问题检查

这里有两个规则:

  1. 高费英雄无装备警告:费用 ≥ 4 却没有装备 → 可能浪费了输出位。

  2. 重复装备警告:除了少数可叠加装备(如泰坦的坚决),其他装备重复出现就提示,因为它们的唯一被动会浪费。

STACKABLE = {"TFT_Item_TitansResolve", "TFT_Item_BlueBuff", "TFT_Item_Morellonomicon"}

小结

到这一步,我们已经完成了转换器的“智慧内核”:只要喂给它一个格式标准的英雄列表,它就能:

  • 统计羁绊数量并判断激活等级;

  • 生成阵容摘要(前后排比例、主 C 是谁);

  • 列出潜在的装备搭配问题。

就功能而言,它已经是一个完整的阵容分析引擎了。无论输入数据来自哪里,最终都会落到这个引擎里进行计算。

那么问题来了——谁会愿意每次手动把英雄整理成干净的标准化列表再输入呢?这很难长官

在下一篇文章中,我们将给这个引擎装上“眼睛和耳朵”:让它能自动识别来自 Riot API 的对局 JSON、截图识别模块的结果,甚至是你随手在命令行里瞎打的几个英雄英文名。我们还会把它包装成一个像模像样的命令行工具,让你在终端里敲一行命令就能立刻得到完整的阵容分析报告。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐