WIKI冒险团明确声明不支持涉及代练和账号买卖的现金交易活动。
参与者应自行承担对应后果。
战力测试终极算法(代码向)
阅读
2023-07-27更新
最新编辑:欣酱是萌欣吖
阅读:
更新日期:2023-07-27
最新编辑:欣酱是萌欣吖
前言
本攻略更新于2023.7.27,以后即使夜莺装备变化,方法殊途同归,C++代码的参数不会更新,请自行理解修改。
在状态自相似中,我们通过求解多元一次方程组,找到夜莺状态迁移的平衡态,进而近似计算得概率。
它只计算了期望,没有能够计算伤害的分布,并且没有考虑战测的开始和结束的时候。
现在我弄出来了一个能够精确计算夜莺伤害分布的算法,无论是手操还是自动。这个算法执行的时间大约是5秒钟(手操),我用C++实现出来之后把结果导入到wiki上的js代码里,所以wiki的计算器始终保持两个优雅的特性:立马出结果,和绝对的精准。
战力测试终极算法(代码向)
这是一个纯计算机算法,纸笔玩家,手机计算器玩家可以骂骂咧咧离场了。
可直接移步计算器使用现成的结果。
把夜莺的战测视为一个马尔可夫过程,用状态记录夜莺的所有信息和历史信息。
struct state { float p; int focus; int cd; int amounta; int timea; int amountb; int timeb; float dot; float h1; float h2; float h3; };
它们分别表示这个状态出现的概率,专注剩余持续时间,技能冷却,流血伤害倍率,流血剩余时间,瘟疫伤害倍率,瘟疫剩余时间,总dot伤害倍率,和123技能释放次数
在战测的任意时刻,只要这些确定,那么以后的情况也就确定,而不用去关注历史。这就是一个马尔可夫过程。
如果使用矩阵推衍这个过程,看上去很帅,其实有很多状态不会出现,会耗费大量时间(几乎无穷)。
我做的最主要的优化,是把能合并的状态合并
bool Equal(state& a, state& b) {
if (a.focus == b.focus && a.cd == b.cd && a.amounta == b.amounta && a.timea == b.timea && a.amountb == b.amountb && a.timeb == b.timeb && a.dot == b.dot)return 1;
if (a.focus == b.focus && a.cd == b.cd && a.amounta == b.amountb && a.timea == b.timeb && a.amountb == b.amounta && a.timeb == b.timea && a.dot == b.dot)return 1;
return 0;
}
这个函数判断两个状态能否合并。第二行是一个对称的情况,也可以合并。
然后就是用if语句推进马尔可夫过程的演化。
注释掉的部分可以计算手动。事实上任意的手操策略只要稍微修改代码就能计算。最后得到的vector里面就是所有状态的分布。完全精确。
最后我有做一些怪异的处理,因为要导入到wiki上面的战测计算器。
重点关注合并时dot和h的更新,是以概率为权重的加权平均。
最终的dot那个数值,除以4,乘开局技巧爆发2.2*1.3,乘技能系数1.1,乘环,乘技巧,就是最终dot伤害。
vector<state> states, nexts; int main() { states.push_back({ 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0 }); for (int t = 0; t < 2000; t++) { for (int i = 0; i < states.size(); i++) { if (states[i].timea > 0) states[i].timea--; if (states[i].timea % 10 == 0) states[i].dot += states[i].amounta; if (states[i].timea == 0) states[i].amounta = 0; if (states[i].timeb > 0) states[i].timeb--; if (states[i].timeb % 10 == 0) states[i].dot += states[i].amountb; if (states[i].timeb == 0) states[i].amountb = 0; if (states[i].focus > 0) states[i].focus--; if (states[i].cd > 0) { states[i].cd--; nexts.push_back(states[i]); } else { /* if (states[i].timea == 0 || states[i].timea < states[i].timeb && states[i].focus > 0) { states[i].h1++; states[i].cd = 36; states[i].timea = 80; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.95; nexts[nexts.size() - 1].amounta = (states[i].focus > 0) ? 9 : 6; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.05 * 0.75; nexts[nexts.size() - 1].amounta = (states[i].focus > 0) ? 6 : 4; nexts[nexts.size() - 1].focus = 81; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.05 * 0.25; nexts[nexts.size() - 1].amounta = (states[i].focus > 0) ? 6 : 4; } else if (min(states[i].timea, states[i].timeb) > 0 && states[i].focus == 0) { states[i].h2++; states[i].cd = 5; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.05 * 0.75; nexts[nexts.size() - 1].focus = 81; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= (0.95 + 0.05 * 0.25); } else if (states[i].timeb == 0 || states[i].timeb < states[i].timea && states[i].focus > 0) { states[i].h3++; states[i].cd = 36; states[i].timeb = 80; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.95; nexts[nexts.size() - 1].amountb = (states[i].focus > 0) ? 9 : 6; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.05 * 0.75; nexts[nexts.size() - 1].amountb = (states[i].focus > 0) ? 6 : 4; nexts[nexts.size() - 1].focus = 81; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.05 * 0.25; nexts[nexts.size() - 1].amountb = (states[i].focus > 0) ? 6 : 4; } */ if (states[i].timea == 0 || states[i].timea < states[i].timeb) { states[i].h1++; states[i].cd = 36; states[i].timea = 80; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.95; nexts[nexts.size() - 1].amounta = (states[i].focus > 0) ? 9 : 6; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.05 * 0.75; nexts[nexts.size() - 1].amounta = (states[i].focus > 0) ? 6 : 4; nexts[nexts.size() - 1].focus = 81; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.05 * 0.25; nexts[nexts.size() - 1].amounta = (states[i].focus > 0) ? 6 : 4; } else { states[i].h3++; states[i].cd = 36; states[i].timeb = 80; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.95; nexts[nexts.size() - 1].amountb = (states[i].focus > 0) ? 9 : 6; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.05 * 0.75; nexts[nexts.size() - 1].amountb = (states[i].focus > 0) ? 6 : 4; nexts[nexts.size() - 1].focus = 81; nexts.push_back(states[i]); nexts[nexts.size() - 1].p *= 0.05 * 0.25; nexts[nexts.size() - 1].amountb = (states[i].focus > 0) ? 6 : 4; } } } states.clear(); for (int i = 0; i < nexts.size(); i++) { bool found = 0; for (int j = 0; j < states.size() && found == 0; j++) { if (Equal(nexts[i], states[j])) { states[j].h1 = (states[j].h1 * states[j].p + nexts[i].h1 * nexts[i].p) / (states[j].p + nexts[i].p); states[j].h2 = (states[j].h2 * states[j].p + nexts[i].h2 * nexts[i].p) / (states[j].p + nexts[i].p); states[j].h3 = (states[j].h3 * states[j].p + nexts[i].h3 * nexts[i].p) / (states[j].p + nexts[i].p); states[j].dot = (states[j].dot * states[j].p + nexts[i].dot * nexts[i].p) / (states[j].p + nexts[i].p); states[j].p += nexts[i].p; found = 1; } } if (found == 0) { states.push_back(nexts[i]); } } nexts.clear(); } float skill = 10000; float force = 2000; float th1 = 0, th2 = 0, th3 = 0, tdot = 0; float night[4000] = {0}; for (int i = 0; i < states.size(); i++) { night[int(states[i].dot + 0.5)] += states[i].p;
if (states[i].p * states[i].h1 > 0)th1 += states[i].p * states[i].h1; if (states[i].p * states[i].h2 > 0)th2 += states[i].p * states[i].h2; if (states[i].p * states[i].h3 > 0)th3 += states[i].p * states[i].h3; tdot += states[i].p * states[i].dot;
} for (int i = 0; i < 4000; i++) { if (night[i] != 0) { cout << " night[" << i << "] = " << night[i] << ";\n"; } } cout << th1 << " " << th2 << " " << th3 << " " << tdot <<endl; cout << states.size(); }