Files
gplan-2/qa-data/growth_model.py
T
DKChan 8acd107533 feat(balance): Package B 七轮数值优化+关卡成长×永久成长对齿批——worktree adventure-mc-playtest 落籍
- Package B 迭代七轮:enemy HP 阶梯重锚+阶段拔高(L2/L3 14 行+伤害轴 atk 3)、AP 6→8、
  clear_blast 26/18/35→32/24/40、heal_minor 加强、hero max_hp 20、d3 缩板 10×10(板随战斗时长配)
- 数值设计批(game-balancer 工作流):关卡怪物成长×永久锻造对齿,forge base_price 10→20
- 三阶段快照验证(report_M0/M1/M2):墙真实存在、锻造单调开门,拍板 B 接受现曲线
- 详见 qa-data/iteration_log.md(九段台账)与 数值设计_关卡成长×永久成长_v1.md
- smoke 工具入库:adventure_monte_carlo.gd / calibrate_boards.gd;qa-data 原始 fulllog 不入库
2026-09-23 02:32:25 +08:00

95 lines
4.3 KiB
Python
Raw 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 毕业 ")