Files
gplan-2/sim-data/growth_model.py

95 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
关卡怪物成长 × 永久成长 对齿模型(game-balancer 工作流 step 2-3)
链条模型:逐节点 击杀轮=t_kill=敌总血/我方DPS;承伤/轮=实测口径;池血衰减+战斗治疗;
裕度 m=进场池血/承伤每轮 − t_kill → 经验分段映射胜率(用第 6 轮 MC 实测校准,±10~15pp)。
阶段 S0-S3 = 永久锻造累计(atk+ / hp+ / ac+),ac 每点 ≈ 承伤 -7%(近似,标 provisional)。
输出:阶段×关卡 到达boss率/通关率 矩阵(v1=第6轮现役怪=校准组;v2=阶段拔高提案=设计组)。
"""
DPS_DIG = {1: 5.9, 2: 7.1, 3: 6.3} # 实测输出/轮 − 主角免费攻2(第6轮 normal)
HEAL_K = 0.85 # 治疗每战 ≈ 0.85×击杀轮(第6轮实测拟合)
# (关, 节点, 数量期望, 单位血v1, 单位血v2, atk v1, atk v2, 承伤/轮v1实测, boss)
NODES = [
(1, 0, 1.5, 8, 8, 2, 2, 0.79, False),
(1, 1, 2.5, 4, 4, 2, 2, 0.65, False),
(1, 2, 1.0, 50, 50, 2, 2, 1.09, False),
(1, 3, 2.5, 14, 14, 2, 2, 1.94, False),
(1, 4, 1.0, 72, 72, 2, 2, 1.12, True),
(2, 0, 1.0, 45, 50, 2, 2, 1.08, False),
(2, 1, 2.5, 18, 20, 2, 2, 1.99, False),
(2, 4, 2.0, 20, 22, 2, 2, 1.80, False),
(2, 5, 1.0, 68, 85, 2, 3, 1.32, False),
(2, 6, 2.5, 18, 20, 1, 1, 1.62, False),
(2, 7, 2.5, 18, 20, 2, 2, 1.80, False),
(2, 9, 1.0, 80,105, 2, 3, 1.32, True),
(3, 0, 1.0, 50, 60, 2, 3, 1.03, False),
(3, 1, 2.0, 25, 28, 2, 3, 1.53, False),
(3, 4, 2.5, 20, 22, 2, 3, 1.90, False),
(3, 5, 1.0, 76, 95, 2, 3, 1.40, False),
(3, 6, 2.5, 20, 22, 2, 3, 2.15, False),
(3, 7, 2.5, 20, 22, 2, 3, 2.15, False),
(3, 9, 1.0, 92,125, 2, 3, 1.06, True),
]
# 缺实测的行用同原型插值填的(2,4)/(2,7)/(3,4)/(3,5) 按双单位/单体原型估。
def win(m):
if m <= -5: return 0.35
if m <= -3: return 0.48
if m <= -1.5: return 0.60
if m <= 0: return 0.68
if m <= 2: return 0.80
if m <= 5: return 0.90
if m <= 9: return 0.95
return 0.97
def run_level(L, atk_up, hp_up, ac_up, ver, pool0, bonus, verbose=False):
dps = DPS_DIG[L] + 2 + atk_up
fac = 1.0 - 0.07 * ac_up
pool = pool0
reach = 1.0
for (l, d, u, h1, h2, a1, a2, intake1, boss) in NODES:
if l != L: continue
hp = u * (h2 if ver == 2 else h1)
atk = (a2 if ver == 2 else a1)
intake = intake1 * (atk / a1) * fac
if ver == 2: intake *= 1.0 # v2 承伤已按 atk 比例走上面一行
t_kill = hp / dps
t_live = pool / intake
m = t_live - t_kill
p = win(m)
if verbose:
print(f" L{L} d{d}{' BOSS' if boss else '':>5} hp={hp:5.0f} dps={dps:4.1f} tkill={t_kill:4.1f} intake={intake:4.2f} tlive={t_live:4.1f} m={m:+5.1f} p={p:.2f}")
yield_p = reach * p
# boss 到达率在进入该点时即定格
if boss and verbose is not None:
boss_reach = reach
pool -= t_kill * intake * (0.5 + 0.5 * p) # 败北也吃部分伤(平均 ~50%*tkill 承伤)
pool += HEAL_K * t_kill * p # 治疗只在胜局兑现大头
reach = yield_p
return boss_reach, reach
def stage_curve(atk_up, hp_up, ac_up, ver, bonus=10, tag=""):
out = {}
pool = 20 + hp_up
for L in (1, 2, 3):
poolL = pool + bonus * (L - 1)
r, c = run_level(L, atk_up, hp_up, ac_up, ver, poolL, bonus)
out[L] = (r, c, poolL)
pool = pool + bonus if c > 0.001 else pool # 通关才拿关间奖励
print(f"{tag} atk+{atk_up} hp+{hp_up} ac+{ac_up}: "
f"L1 达boss -/通关 {out[1][1]*100:4.1f}% | "
f"L2 达boss {out[2][0]*100:4.1f}% 通关 {out[2][1]*100:4.1f}% | "
f"L3 达boss {out[3][0]*100:4.1f}% 通关 {out[3][1]*100:4.1f}%")
return out
print("=== 校准组 v1(第6轮现役怪,模型 vs 实测)===")
print("实测: L1通关 normal49.7/perfect85 | L2条件通过53/77.6 | L2 boss胜率65/97 | L3 boss胜率47/67 | 全通关 10/39.7")
stage_curve(0, 0, 0, 1, tag="S0 fresh")
print()
print("=== 设计组 v2(阶段拔高提案)===")
stage_curve(0, 0, 0, 2, tag="S0 fresh ")
stage_curve(2, 6, 1, 2, tag="S1 ≈5次L1刷 ")
stage_curve(4, 12, 2, 2, tag="S2 ≈再刷L2 ")
stage_curve(6, 18, 3, 2, tag="S3 毕业 ")