chart_achievement: 正向支持按音符类别给判定数(绝赞单列),算出确切达成率
This commit is contained in:
@@ -427,6 +427,109 @@ def solve_forward(th: Theory, judges: Dict[str, int]) -> ForwardResult:
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ 正向:按音符类别精确计算
|
||||
# 达成率是按「音符权重」加权的,只给汇总判定数时只能得到区间;
|
||||
# 把判定数按权重类别分开给(绝赞单独一列),就能算出确切的数值。
|
||||
CLASS_ORDER = ("tap", "hold", "slide", "break")
|
||||
CLASS_LABEL = {
|
||||
"tap": "Tap/Star/Touch/EX",
|
||||
"hold": "Hold 系",
|
||||
"slide": "Slide 系",
|
||||
"break": "绝赞(Break 系)",
|
||||
}
|
||||
CLASS_WEIGHT = {"tap": 500, "hold": 1000, "slide": 1500, "break": 2500}
|
||||
CLASS_GROUPS = {
|
||||
"tap": (GROUP_TAP, GROUP_EX),
|
||||
"hold": (GROUP_HOLD,),
|
||||
"slide": (GROUP_SLIDE,),
|
||||
"break": (GROUP_BREAK, GROUP_BREAK_FLAT, GROUP_BREAK_SLIDE, GROUP_EX_BREAK),
|
||||
}
|
||||
# 每类里每个判定键的 (失分, 奖励分);绝赞的 Great / Perfect 有多个档位
|
||||
CLASS_OPTIONS = {
|
||||
"tap": {"cp": (0, 0), "perfect": (0, 0), "great": (100, 0),
|
||||
"good": (250, 0), "miss": (500, 0)},
|
||||
"hold": {"cp": (0, 0), "perfect": (0, 0), "great": (200, 0),
|
||||
"good": (500, 0), "miss": (1000, 0)},
|
||||
"slide": {"cp": (0, 0), "perfect": (0, 0), "great": (300, 0),
|
||||
"good": (750, 0), "miss": (1500, 0)},
|
||||
"break": {"cp": (0, 100), "perfect": (0, 75), "perfect50": (0, 50),
|
||||
"great": (500, 40), "great40": (1000, 40), "great50": (1250, 40),
|
||||
"good": (1500, 30), "miss": (2500, 0)},
|
||||
}
|
||||
_JUDGE_OF_KEY = {
|
||||
"cp": "cp", "perfect": "perfect", "perfect50": "perfect",
|
||||
"great": "great", "great40": "great", "great50": "great",
|
||||
"good": "good", "miss": "miss",
|
||||
}
|
||||
|
||||
|
||||
def class_capacity(th: Theory, cls: str) -> int:
|
||||
"""这一类音符在谱面里有多少个。"""
|
||||
return sum(th.counts.get(g, 0) for g in CLASS_GROUPS[cls])
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExactResult:
|
||||
theory: Theory
|
||||
spec: Dict[str, Dict[str, int]] # 实际参与计算的判定数(已补足 CP)
|
||||
achievement: Fraction
|
||||
dx: int
|
||||
loss_total: int
|
||||
bonus_total: int
|
||||
breakdown: Dict[str, Tuple[int, int, int]] # 类别 -> (音符数, 失分, 奖励分)
|
||||
|
||||
@property
|
||||
def score(self) -> int:
|
||||
return self.theory.all_perfect_score - self.loss_total
|
||||
|
||||
|
||||
def solve_exact(th: Theory, spec: Dict[str, Dict[str, int]]) -> ExactResult:
|
||||
"""按音符类别给判定数,算出**确切**的达成率与 DX 分。
|
||||
|
||||
spec: {"tap": {"cp": 250, "great": 2}, "break": {"cp": 4, "great": 1}, ...}
|
||||
每类没给满的部分按 CP 补足(非绝赞 CP 与 Perfect 同分;绝赞按 CP 计奖励 100)。
|
||||
"""
|
||||
filled: Dict[str, Dict[str, int]] = {}
|
||||
loss_total = 0
|
||||
bonus_total = 0
|
||||
dx = 0
|
||||
breakdown: Dict[str, Tuple[int, int, int]] = {}
|
||||
for cls in CLASS_ORDER:
|
||||
cap = class_capacity(th, cls)
|
||||
given = {k: int(v) for k, v in (spec.get(cls) or {}).items() if int(v)}
|
||||
for key in given:
|
||||
if key not in CLASS_OPTIONS[cls]:
|
||||
raise ValueError("「%s」没有「%s」这个判定" % (CLASS_LABEL[cls], key))
|
||||
used = sum(given.values())
|
||||
if used > cap:
|
||||
raise ValueError("%s 只有 %d 个音符,但给了 %d 个判定"
|
||||
% (CLASS_LABEL[cls], cap, used))
|
||||
if cap > used:
|
||||
given["cp"] = given.get("cp", 0) + (cap - used)
|
||||
filled[cls] = given
|
||||
cls_loss = 0
|
||||
cls_bonus = 0
|
||||
for key, n in given.items():
|
||||
dl, db = CLASS_OPTIONS[cls][key]
|
||||
cls_loss += dl * n
|
||||
cls_bonus += db * n
|
||||
dx += DX_PER[_JUDGE_OF_KEY[key]] * n
|
||||
loss_total += cls_loss
|
||||
bonus_total += cls_bonus
|
||||
breakdown[cls] = (cap, cls_loss, cls_bonus)
|
||||
|
||||
w = th.all_perfect_score
|
||||
if w <= 0:
|
||||
raise ValueError("谱面里没有计分音符")
|
||||
achievement = Fraction(100) - Fraction(100 * loss_total, w)
|
||||
if th.break_bonus_score:
|
||||
achievement += Fraction(bonus_total, th.break_bonus_score)
|
||||
return ExactResult(
|
||||
theory=th, spec=filled, achievement=achievement, dx=dx,
|
||||
loss_total=loss_total, bonus_total=bonus_total, breakdown=breakdown,
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ 反向:工具函数
|
||||
def _judges_for(th: Theory, miss: int, good: int, great: int,
|
||||
cp: int, perfect: int) -> Dict[str, int]:
|
||||
|
||||
Reference in New Issue
Block a user