"""事件显著度评分与小人物过滤。 真实数据给的教训:40 万条事件里绝大多数是流水账 (change hf state 7.3 万、change hf job 5.8 万、written content composed 3.9 万), 直接按类型切章会得到 5910 章,毫无叙事价值。 因此策略是: 1. 只保留天然有叙事张力的事件类型(攻城、灭国、神器、死亡、结盟……) 2. 再按"参与者有多重要"做二次排序,让编年史跟着大人物走 """ from __future__ import annotations from collections import Counter from dfannals.legends import Event, World # 事件类型 → 基础分。0 分表示不单独叙事(流水账) NARRATIVE: dict[str, int] = { # ---- 天崩地裂级 "destroyed site": 100, "razed structure": 100, "entity overthrown": 100, "hf destroyed site": 95, "plundered site": 95, "field battle": 95, "entity dissolved": 90, "holy city declaration": 90, "site taken over": 90, "entity breach feature layer": 90, "entity alliance formed": 85, "peace accepted": 85, "hf enslaved": 85, "hf revived": 80, "entity relocate": 80, "hf ransomed": 80, "peace rejected": 78, # ---- 重大 "created site": 75, "reclaim site": 72, "attacked site": 70, "hf attacked site": 70, "entity created": 70, "artifact destroyed": 70, "new site leader": 65, "created world construction": 60, "entity persecuted": 60, "hf convicted": 60, "site dispute": 55, "failed intrigue corruption": 55, "artifact recovered": 60, "artifact possessed": 60, # ---- 值得一记 "artifact created": 45, "artifact lost": 45, "creature devoured": 42, "hf abducted": 40, "assume identity": 40, "knowledge discovered": 38, "artifact given": 36, "agreement formed": 36, "hf gains secret goal": 36, "item stolen": 34, "hf performed horrible experiments": 45, "failed frame attempt": 40, "hf profaned structure": 38, "changed creature type": 36, "hf interrogated": 34, "created structure": 30, "hf wounded": 30, "hf learns secret": 34, "changed hf body state": 30, } # 死亡单独处理:分数取决于死者本身有多重要 DEATH_TYPES = ("hf died",) CATEGORIES: list[tuple[tuple[str, ...], str]] = [ (("war", "battle", "siege", "raid", "attack", "plunder", "destroy", "sack", "taken over"), "战事"), (("died", "death", "slain", "murder", "assassin", "enslaved", "ransomed", "revived"), "生死"), (("created site", "reclaim", "relocate", "dissolved", "overthrown", "alliance", "entity created"), "兴亡"), (("artifact", "masterpiece", "construction", "structure", "razed"), "器物"), (("peace", "agreement", "tribute", "persecuted", "convicted", "dispute", "intrigue"), "政争"), (("knowledge", "secret", "abducted", "identity", "experiment", "profaned", "devoured"), "异闻"), ] def category(event: Event) -> str: t = event.type.lower() for keys, label in CATEGORIES: if any(k in t for k in keys): return label return "杂记" def prominence(world: World) -> Counter[int]: """每个人物被卷入的事件次数——用来衡量他在史料里有多重要。""" counts: Counter[int] = Counter() for e in world.events: seen: set[int] = set() for tag in ("hfid", "slayer_hfid", "target_hfid", "source_hfid", "deity", "worshipper_hfid"): for fid in e.ids(tag): seen.add(fid) counts.update(seen) return counts def _participants(event: Event) -> list[int]: out: list[int] = [] for tag in ("hfid", "slayer_hfid", "target_hfid", "source_hfid", "deity"): out.extend(event.ids(tag)) return out # 人物重要度阈值:参与事件数达到这个量级才算"要角" PROMINENT = 60 def significance(event: Event, prom: Counter[int] | None = None, prominent: int = PROMINENT) -> int: """事件的故事价值。0 表示不值得单独写。""" t = event.type.lower() base = NARRATIVE.get(t, 0) people = _participants(event) top = max((prom.get(p, 0) for p in people), default=0) if prom else 0 if t in DEATH_TYPES: # 无名死者不写;要角之死按重要度加分 if top < prominent: return 0 return 55 + min(45, top // 10) if base == 0: return 0 # 参与者越重要,越值得写;但不要盖过类型本身的分量 bonus = min(25, top // 25) if prom else 0 return base + bonus def is_narrative(event: Event, prom: Counter[int] | None = None) -> bool: return significance(event, prom) > 0