「神怒」vs「秘技」

虽说已经脱坑毒奶粉好多年了,但和在坑里比起来,脱坑的时间并不怎么长,所以日常胡思乱想有时候又奶粉这样,奶粉那样的。不怕你笑话,我高中时代的梦想之一就是能做个毒奶粉的伤害计算器,好让我用来做装备搭配。

好吧,说是个伤害计算器,其实局限也挺大的。毒奶粉里有些技能有好几次伤害判定的:先是几次伤害不高的攻击判定,辅以华(lǎo)丽(tǔ)的技能特效,最后是一个伤害极(gǎn)高(rén)的收招。同时,毒奶粉里也有些特殊的装备,称作「远古装备」,他们有各种千奇百怪的属性。如

  • 增加移动距离的「疾风之影」系列
  • 增加技能伤害的「神之怒击」系列
  • 增加持续时间的「时光之源」系列
  • 减少MP消耗和冷却时间的「残忍之弑」系列
  • 增加技能范围的「禁锢之限」系列

其中「多段伤害」的技能有其独有的系列:「秘技之境」,它增加多段攻击次数,但减少伤害。

DNF中多段技能的次数在释放的瞬间确定,伤害倍率则随装备实时变化,于是就衍生出了「换装流」:在技能释放前带上「秘技之境」的装备,释放技能后马上换成「神之怒击」的装备。这样不但保留了「秘技之境」增加多段攻击次数的属性,也把「秘技之境」减少技能伤害的减益效果用「神之怒击」增加技能伤害的效果所替代。这样常常能打出5~10倍的伤害,在彼时版本称神。当然,也有「神之怒击」换「秘技之境」的计生流视频。

你看那个柔道,抱着怪摔了半天不掉血,柔道真垃圾,不要玩柔道,快去玩狂霸拽酷屌的红神吧,现在创建角色还赠送罐子头,铁脚链,红翅膀的红神套装,你还在等什么🙂

当然,对我这种懒人来说,换装流要按的键实在是太多了。我想要的是,在我已有的「神之怒击」和「秘技之境」搭配里,怎么选能让技能伤害最大化。这就构成了这个伤害计算器的目标。

彼时的我只接触过VB,是学校对付会考让我们学的。老师照本宣科,讲完要求的语法后就让我们回去看文化课。那时的我连「数组」都不知道,更不用说什么「函数」、「递归」了。那是晚自习的一大乐事就是琢磨这个程序该怎么写。还记得当时想的是,可以弄两排checkbox,上面代表「神怒」,下面代表「秘技」,有那些装备就勾上Checkbox,读一遍每个checkbox的状态就可以得到目前拥有的装备。接下来要穷举目前装备所能得到的所有组合方式,依次计算伤害,找出最大值对应的组合即可。计算伤害一个表达式就搞定了,找最大值也仅仅需要一个「循环」。就是「穷举所有组合方式」这个拼图的最后一片搞得我头大。要根据该部位装备的数量决定是否生成一个循环?这样9件装备就有9重循环了。对于单重循环都弄不太清的我来说这是条死路。去问老师吧,微机老师很凶的样子,再说万一她到班主任打报告说我不好好学习文化课怎么办?况且我这辈子问老师的次数屈指可数,还是算了吧。于是,这个计算器就这么一直搁置了下来。

后来的日子里,计算器慢慢的被遗忘,但「穷举组合」这个问题至今还是会冷不丁冒出来,困扰我一下。

近日和老友聊到高中,感叹时光飞逝的同时,又想起此事。「有兴趣没能力」时因为能力不足而夜不能寐。而今「有能力没兴趣」又笑自己看不开,把精力浪费到那些无所谓的事情上。「有能力有兴趣」当然是最好的,但又有多少人是这样的呢?「没兴趣没能力」也不赖,不用杞人忧天。能力的增长和兴趣的丧失都是不可避免的。既有兴趣又有能力,那就一定对这件事是真爱了。

好在现在的我不必再责备自己的无能,这样的东西不敢说信手拈来但也不怎么费劲。它还有改进的空间,比如做个GUI啊,技能数据弄成配置文件啊,甚至说做成考虑所有情况的伤害计算器啊。但我已经失去兴趣了。换句话说,我老了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
using System;
using System.Collections.Generic;
using System.Linq;

namespace MaximumDamage
{
internal class Program
{
private static void Main(string[] args)
{
var skill = new Skill()
{
Name = "地狱摇篮",
DamagePerStrike = 7783,
LastStrike = 28536,
NumOfStrikes = 5
};
var gears = new List<List<Gear>>()
{
new List<Gear>()
{
new Gear(){ Location = "胸甲", Mode = 0, DamageIncrement = 0.09},
new Gear(){ Location = "胸甲",Mode = 1,DamageDecrement = 0.03, StrikeIncrement = 1}
},
new List<Gear>()
{
new Gear(){ Location = "护腿", Mode = 0, DamageIncrement = 0.09},
new Gear(){ Location = "护腿",Mode = 1,DamageDecrement = 0.03, StrikeIncrement = 1}
},
new List<Gear>()
{
new Gear(){ Location = "护肩", Mode = 0, DamageIncrement = 0.08},
new Gear(){ Location = "护肩",Mode = 1,DamageDecrement = 0.04, StrikeIncrement = 1}
},
new List<Gear>()
{
new Gear(){ Location = "腰带", Mode = 0, DamageIncrement = 0.08},
new Gear() { Location = "腰带",Mode = 1,DamageDecrement = 0.04, StrikeIncrement = 1}
},
new List<Gear>()
{
new Gear(){ Location = "护腿", Mode = 0, DamageIncrement = 0.09},
new Gear(){ Location = "护腿",Mode = 1,DamageDecrement = 0.05, StrikeIncrement = 2 }
},
new List<Gear>()
{
new Gear(){ Location = "鞋", Mode = 0, DamageIncrement = 0.08},
new Gear(){ Location = "鞋",Mode = 1,DamageDecrement = 0.04, StrikeIncrement = 1}
},
new List<Gear>()
{
new Gear(){ Location = "项链", Mode = 0, DamageIncrement = 0.06},
new Gear(){ Location = "项链",Mode = 1,DamageDecrement = 0.05, StrikeIncrement = 1}
},
new List<Gear>()
{
new Gear(){ Location = "手镯", Mode = 0, DamageIncrement = 0.05},
new Gear(){ Location = "手镯",Mode = 1,DamageDecrement = 0.06, StrikeIncrement = 1}
},
new List<Gear>()
{
new Gear(){ Location = "戒指", Mode = 0, DamageIncrement = 0.05},
new Gear(){ Location = "戒指",Mode = 1,DamageDecrement = 0.06, StrikeIncrement = 1}
},
new List<Gear>()
{
new Gear(){ Location = "武器", Mode = 0, DamageIncrement = 0.18},
new Gear(){ Location = "武器",Mode = 1,DamageDecrement = 0.03, StrikeIncrement = 2}
},
};
var calc = new Calculator(skill, gears);
var g = calc.Maximum();
int strikes = 0;
double modifier = 1d;
Console.WriteLine(skill);
foreach (var item in g)
{
if (item.Mode == 0)
{
modifier *= (1 + item.DamageIncrement);
}
else
{
strikes += item.StrikeIncrement;
modifier *= (1 - item.DamageIncrement);
}
}
foreach (var item in g)
{
Console.WriteLine(item);
}
Console.WriteLine($"最终伤害:({strikes}*{skill.NumOfStrikes + strikes}+{skill.LastStrike})*{modifier}={calc.maxDmg}");
Console.ReadLine();
}
}

public class Calculator
{
public Skill Skill { get; private set; }
public List<List<Gear>> Gears { get; private set; }

private List<int> Indexes;
private bool flag = false;

private List<Gear> NextCombination()
{
if (flag)
{
flag = false;
}
else
{
int carry = 0;
for (int i = Indexes.Count - 1; i >= 0; i--)
{
int bound = Gears[i].Count;
Indexes[i] = (carry + 1) + Indexes[i];
carry = Indexes[i] / bound;
Indexes[i] %= bound;
}
}

return Gears.Select((s, i) => s[Indexes[i]]).ToList();
}
public double maxDmg { get; private set; } = Double.MinValue;

public List<Gear> Maximum()
{
List<Gear> gears = new List<Gear>();
int size = Gears.Aggregate(1, (i, j) => i * j.Count);
for (int i = 0; i < size; i++)
{
var combination = NextCombination();
int strikes = 0;
double modifier = 1;
foreach (var g in combination)
{
if (g.Mode == 0)
{
modifier *= (1 + g.DamageIncrement);
}
else
{
strikes += g.StrikeIncrement;
modifier *= (1 - g.DamageIncrement);
}
}
var dmg = (Skill.LastStrike + (Skill.NumOfStrikes + strikes) * Skill.DamagePerStrike) * modifier;
if (dmg > maxDmg)
{
maxDmg = dmg;
gears = combination;
}
}
return gears;
}


public Calculator(Skill skill, List<List<Gear>> gears)
{
Skill = skill;
Gears = gears;
Indexes = gears.Select(i => 0).ToList();
}
}

public class Gear
{
public string Location { get; set; }
public int Mode { get; set; }// 0=神怒,1=秘技
public double DamageIncrement { get; set; }
public int StrikeIncrement { get; set; }
public double DamageDecrement { get; set; }

public override string ToString()
{
var prefix = $"{(Mode == 0 ? "神怒" : "秘技")}{Location}";
string prop = string.Empty;
if (Mode == 0)
{
prop = $"伤害增加:{DamageIncrement.ToString("P")}";
}
else
{
prop = $"增加攻击次数:{StrikeIncrement},伤害减少:{DamageDecrement.ToString("P")}";
}
return prefix + prop;
}
}

public class Skill
{
public override string ToString()
{
var str = $"{DamagePerStrike}*{NumOfStrikes }+{LastStrike}={DamagePerStrike * NumOfStrikes + LastStrike}";
return $"技能名:{Name},多段攻击次数:{NumOfStrikes},多段攻击伤害:{DamagePerStrike},最终攻击伤害:{LastStrike},总伤害{str}";
}
public string Name { get; set; }
public int LastStrike { get; set; }
public int NumOfStrikes { get; set; }
public int DamagePerStrike { get; set; }
}
}
1
2
3
4
5
6
7
8
9
10
11
12
技能名:地狱摇篮,多段攻击次数:5,多段攻击伤害:7783,最终攻击伤害:28536,总伤害7783*5+28536=67451
神怒胸甲伤害增加:9.00%
神怒护腿伤害增加:9.00%
神怒护肩伤害增加:8.00%
神怒腰带伤害增加:8.00%
秘技护腿增加攻击次数:2,伤害减少:5.00%
神怒鞋伤害增加:8.00%
秘技项链增加攻击次数:1,伤害减少:5.00%
秘技手镯增加攻击次数:1,伤害减少:6.00%
秘技戒指增加攻击次数:1,伤害减少:6.00%
神怒武器伤害增加:18.00%
最终伤害:(5*10+28536)*1.7660633160960004=187849.0906798672