Radio Silence and SotR Musings

It’s been a quiet two weeks here on Sacred Duty, and I apologize for that.  All three of us have been very busy in real life, and haven’t had much time to post.  And of course, Diablo III has eaten up a lot of time as well.

The Mists of Pandaria beta hasn’t given us much to talk about anyway.  We have a new version of SotR, but it’s obviously unfinished.  As it stands, it gives us 3 seconds of 30% damage reduction per use, along with the new Bastion of Glory mechanic that buffs WoG.  It’s an interesting design, but in and of itself that doesn’t solve the mastery problems that plagued the old design.  What should fix them, presumably, is the new addition to our mastery.  Divine Bulwark now reads:

Increases the effectiveness of your Shield of the Righteous and Bastion of Glory by 10% and increases your chance to block melee attacks by 10%.

The values should scale with mastery, though the initial mmo-champion build diff showed it scaling with 1.2% spellpower.  I assume this is a mix-up in their build analysis code, and that the true scaling should be 1.2% per point of mastery, which is consistent with the tooltips and character sheet on beta.  However, that’s a pretty significant nerf to block (from 2.25% to 1.2%), which has to be made up for by the bonus to Shield of the Righteous (if we ignore the Bastion of Glory effect as an emergency button rather than part of a steady-state mitigation sequence).

Which brings up another quandary: the Divine Bulwark tooltip doesn’t specify how it buffs Shield of the Righteous.  The logical conclusion is that it increases the damage reduction (DR), and that’s the interpretation that I’ve adopted.  But other players have noted that the wording is ambiguous enough that it could instead increase the duration, or even simply improve the damage of SotR.

I think that neither of those are very likely, however.  A damage increase is not only completely unnecessary, but fails to address the problem that prompted the re-design: the poor performance of mastery as a survivability stat.  Duration would at least be a survivability benefit, but there’s two reasons I find that unlikely.  First, I think they’ll be wary of any effects that increase the duration for fear of unintentionally enabling block cap.  Though in this case, that fear is unfounded, because it would take over 80 mastery to reach cap, which seems like a lot even with proc trinkets.

More importantly, duration increases end up introducing a tiered benefit system.  It takes about 28 mastery to add a second to the duration.  If a careful player is hitting SotR shortly before a hit, then he’s already going to get two attacks out of it.  With a little bit of mastery, he’ll cover a third attack, given a 1.5-second swing timer.  but then it takes another 42 mastery to cover the fourth attack.  That means mastery is essentially useless until you can reach that new threshold, which is likely to be unattainable, turning mastery back into our dump stat.  I’m certain that Blizzard won’t like having multiple mastery caps or the near-uselessness of mastery between those caps, so a duration increase is probably out.

Which just leaves our initial impression – that mastery will increase the damage reduction of SotR.  But even that isn’t enough, because we have no indication whether the 1.2% is additive or multiplicative.  In other words, we don’t know if we get (30%+1.2%*mastery) or 30%*(1+0.12*mastery) reduction.  And we can’t even test this on beta yet, both because the tooltip doesn’t reflect changes in mastery and because initial testing suggests that the mastery scaling on the buff is even implemented yet.  So at this point, all we have is more guesses and questions.

However, the Monte-Carlo simulation was easily updated to handle the new mechanics, so I did so assuming that the mastery bonus to SotR will be additive.  That seems reasonable given what we expect mastery values to look like (at 20-30 mastery, we’re gaining 24%-36% mitigation for a total of 54%-66%, roughly in the ballpark of the old 75% block version of SotR).  I’ve been sitting on these simulation results for a few weeks now, waiting for Blizzard to implement the mastery scaling on beta just to ensure that my assumption was correct.  But since we haven’t seen any indication that it will be implemented any time soon, I’ve decided it would be better to post the results to facilitate discussion, even if we’re limited to speculation.

So, without further adieu, here are the stat weights I’ve calculated from four different runs of the simulation.  See the code (or the post linked above) for more details about how the simulation operates.  We’re comparing the effect of adding 600 of each rating, and the calculation is done for 10,000 minutes of combat with 10-millisecond time steps.  The values are related to how much damage reduction the stat gives, so higher is better.

Stat #1 #2 #3 #4 mean
Dodge/Parry 0.8059 0.8681 0.7638 0.7378 0.7939
Hit 0.4120 0.4220 0.4206 0.4747 0.4323
Expertise 0.3971 0.4732 0.3662 0.4187 0.4138
Haste 0.2617 0.3684 0.2541 0.3312 0.3039
Mastery 0.6940 0.8153 0.6701 0.8166 0.7490

Even with such a long integration time, there’s still quite a bit of variance between runs. If I find some time, I may do some statistical analysis with the simulation at 10 ms to get a better quantitative sense of how much these results vary, especially for shorter time scales (for example, calculating the mean and variance for a large number of 5-minute fights rather than one long 10,000-miute fight).

But for now, we can be content to note that mastery is competitive with dodge/parry and significantly ahead of hit/exp/haste with the additive implementation.  That’s a good sign, because it means this implementation fixes the “mastery as a dump stat” problem.  However, we should note that the additive DR assumption is a best-case scenario.  If the DR bonus is multiplicative with SotR, the effect of mastery is weaker than the additive model.  There are actually several ways you could calculate it multiplicatively, including the one I proposed earlier in the post, but none of them keep up with the additive model.  So it will be important to test and confirm the behavior once the mastery scaling is implemented on live, because it will make a big difference in how we value mastery.

In a later blog post, once we have some confirmation of the mechanics, we’ll talk about exactly how relevant these total-damage-reduction-based stat weights are for our gearing.  Keep in mind that TDR is only one facet of tanking, and it’s rarely the most important, even if it is the metric that’s easiest to quantify.

Posted in Tanking, Theorycrafting, Theck's Pounding Headaches | Tagged , , , , , , , , | Leave a comment

Warrior Block Calculations – Part 4

In the last three posts, we’ve built up the equations we need to calculate the survivability stat weights for warriors.  In this post, we’re going to summarize the equations we need, and then plug in numbers to see how it all works out.

Note that I’m not going to go through the derivation step by step here.  I’m just going to list the equations we need, and then analyze them.  If you want to know where a particular expression comes from, you’ll have to slog through reading parts 1-4.

Required Equations from Part 1:

In part 1, we built up the normalized scale factors \Delta_i:

\Delta_{\rm ar} = \frac{1}{f_{ar}}F_{\rm ar}F_{\rm av}F_b

\Delta_h = \Delta_e = \frac{1}{f_h}F_{\rm ar}F_{\rm av}\Phi_{\rm bh}

\Delta_s = \frac{1}{f_s}F_{\rm ar}F_{\rm av}\Phi_{\rm bs}

\Delta_x = \frac{1}{f_x}F_{\rm ar}F_{\rm av}\Phi_{\rm bx}

\Delta_m = \frac{1}{f_m}F_{\rm ar}F_{\rm av}\Phi_{\rm bm}

\Delta_p = \frac{1}{f_p}\left [ F_{\rm ar}\Phi_{\rm av} F_b + F_{\rm ar}F_{\rm av}\Phi_{\rm bp} \right ]

\Delta_d = \frac{1}{f_d}\left [ F_{\rm ar}\Phi_{\rm av} F_b + F_{\rm ar}F_{\rm av}\Phi_{\rm bd} \right ]

where f_{ar}, f_h, f_s, f_x, f_m, f_p, and f_d are the factors that convert rating to percentage for armor (f_{ar} = (Ar+K)), hit, haste, crit, mastery (in this case, rating->mastery), parry, and dodge, respectively.  F_{\rm ar} is the armor mitigation factor,

F_{\rm ar} = K/(Ar+K),

where K is a constant that depends on player and target level, and Ar is the player’s armor.  F_{\rm av} is the avoidance mitigation factor,

F_{\rm av} = 1-A,

where A is the player’s total (post-DR) avoidance.  F_b is the block mitigation factor:

F_b = 1-G(1+C)B_v - (1-G)B_c(1+C)B_v,

where G is the probability that an unavoided attack becomes a guaranteed block, C is the critical block chance as determined from mastery,  B_c is the player’s total block chance, and B_v is the player’s block value (30%).  G has the form,

G = R_{\rm SB} T_{\rm buff},

where we’ve implicitly defined the Shield Block buff duration T_{\rm buff} and the Shield Block cast rate R_{\rm SB}, which will be defined later on in the rage generation section.

We also have the factor \Phi_{\rm av}, which represents the avoidance mitigation factor’s scaling with dodge and parry:

\Phi_{\rm av} = \frac{1}{k_d}\left ( 1-\frac{A_d}{C_d} \right )^2

with A_d being the player’s post-DR dodge from sources subject to DR, and k_d and C_d being the avoidance diminishing returns coefficients.  We’ve assumed here that dodge and parry are properly balanced.  And we have the factors \Phi_{\rm bi}, which represent the scaling of the block factor with respect to different stats:

\Phi_{\rm bh} = \Phi_{\rm be} = \gamma_h\phi_{\rm G}
\Phi_{\rm bs} = \gamma_s\phi_{\rm G}
\Phi_{\rm bx} = \gamma_x\phi_{\rm G}
\Phi_{\rm bp} = \gamma_p\phi_{\rm G}
\Phi_{\rm bd} = \gamma_d\phi_{\rm G}
\Phi_{\rm bm} = \gamma_m\phi_{\rm G} + \beta_{vm}\phi_{\rm BV} + \beta_{cm}\phi_{\rm BC}

where we’ve incorporated definitions from the differential block factor equation:

\phi_{\rm G} = (1-B_c)(1+C)B_v
\phi_{\rm BV} = [G+(1-G)B_c]B_v
\phi_{\rm BC} = (1-G)(1+C)B_v

as well as factors from the equation for dG:

\gamma_h = \rho_h T_{\rm buff}
\gamma_s = \rho_s T_{\rm buff}
\gamma_m = \rho_m T_{\rm buff}
\gamma_x = \rho_x T_{\rm buff}
\gamma_p = \rho_p T_{\rm buff}
\gamma_d = \rho_d T_{\rm buff}

and the factor representing diminishing returns on block:

\beta_{cm} = \frac{f_b}{k_B}\left ( 1-\frac{B_{cm}}{C_B} \right )^2

where f_b is the mastery-to-block-chance conversion factor (1.5%) B_{cm} is the player’s post-DR block chance due to mastery (i.e. the part that’s subject to DR), and C_B and k_B are the diminishing returns coefficients for blocking.

We’ve also used \beta_{vm} = f_{cb}, which is the mastery-to-crit-block conversion factor (1.5%).

In the expressions for \gamma_i we’ve defined some factors \rho_i which represent the scaling of the SB cast rate with different stats.  These will be defined in the “Part 3″ section.

Required Equations from Part 2

In part 2, we modeled the rotation.  We defined \Theta and \Theta_s, the melee and spell hit factors:

\Theta = 1-(\mu-h)-(d+p-e)
\Theta_s = 1 -(2\mu-h-e)

And used a stochastic matrix method to determine the steady-state probability factors:

\Pi_2 = (0.6 \Theta)(1- 0.6 \Theta)
\Pi_3 = (1- 0.6 \Theta)^2
\Pi_4 = 0.6 \Theta

We then determined the cast rates for different abilities:

R_{\rm SS} = R_{\rm rSS} + R_{\rm SnB}
R_{\rm rSS} = \Pi_2/4.5+\Pi_3/6
R_{\rm SnB}=\Pi_4/4.5
R_{\rm R}=(\Pi_2+\Pi_4)/4.5+\Pi_3/6 = R_{\rm SS}
R_{\rm shout} = 1/60
R_{\rm TC} = 1/6 - (2/15)\Theta_s
R_{\rm D} = R_{\rm D0} - R_{\rm TC} - R_{\rm shout}
R_{\rm D0}=(\Pi_2+\Pi_4)/4.5+2\Pi_3/6

And we defined some differential cast rate factors \chi_i:

\chi_{\rm rSS} = 0.6(1-1.2\Theta)/4.5 -1.2(1-0.6\Theta)/6
\chi_{\rm SnB} = 0.6/4.5
\chi_{\rm SS} = \chi_{\rm rSS}+\chi_{\rm SnB}
\chi_{\rm R} = \chi_{\rm SS}
\chi_{\rm TC}=\frac{2}{15}
\chi_{\rm D0} = 0.6(2-1.2\Theta)/4.5 - 1.2(1-0.6\Theta)/3.

We then went on to calculate the effects of parry-haste, giving us definitions for the auto-attack rate:

R_{\rm AA} = R_{\rm AA}^{(0)}(1+s)+cR_{\rm att}

c = 0.24 P \left ( \frac{R_{\rm att}}{R_{\rm AA}^{(0)}(1+s)}-\frac{2R_{\rm att}P}{R_{\rm AA}^{(0)}(1+s)}+2P\right )
P = A_{p0}+A_p

in terms of the incoming attack rate R_{\rm att}, and two more definitions that represent haste and parry-haste scaling of auto-attacks:

\pi_s = R_{\rm AA}^{(0)} - \frac{0.24 P (1-P) R_{\rm att}}{R_{\rm AA}^{(0)}(1+s)^2}
\pi_p = 0.24 R_{\rm att} \Phi_{\rm av}\left ( \frac{R_{\rm att}(1-4P)}{R_{\rm AA}^{(0)}(1+s)}+4P\right )

Required Equations from Part 3

The shield block cast rate is found from the rage generation equation (link):

R_{\rm SB} = RPS/60
RPS = \Theta[(1+0.5E)(5R_{\rm AA}/R_{\rm AA}^{(0)})+10R_{\rm rSS}+15R_{\rm SnB}] +(8/3)\Theta_s-3,

where E is the enrage buff uptime, R_{\rm AA}^{(0)} is the player’s base auto-attack rate, R_{\rm AA} is the player’s auto-attack rate after haste and parry-haste effects, R_{\rm rSS} the rate of regular shield slams, R_{\rm SnB} the rate of Sword-and-Board-affected Shield Slams

where m, d, and p are the hit, dodge, and parry caps (7.5% each), and h and e are the player’s hit and expertise percentages.

The \rho_i found in the \gamma_i expressions come from the derivative of the Shield Block cast rate equation, and are:

\rho_h = (\sigma_h+\sigma_E\epsilon_h)/60
\rho_s = (\sigma_E\epsilon_s + \sigma_{AA}\pi_s)/60
\rho_m = (\sigma_E\epsilon_m)/60
\rho_x = \sigma_E\epsilon_x/60
\rho_d = \sigma_E\epsilon_d/60
\rho_p = (\sigma_E\epsilon_p+\sigma_{AA}\pi_p)/60 = \rho_d + (\sigma_E\eta_{1p} \ln x + \sigma_{AA}\pi_p)/60

where we’ve made use of the following definitions from the Enrage derivation:

E =1 -0.8 q_1^{N_1}q_2^{N_2}q_3^{N_3}

q_1 = 1-x
q_2 = 1-C
q_3 = 1-u

N_1=6 [ (R_{\rm SS}+R_{\rm D}+R_{\rm R})\Theta+R_{\rm TC}\Theta_s+R_{\rm AA}(\Theta-g) ]
N_2 = 6 R_{\rm att}(1-A)\left [ G+ (1-G)B_c\right ]
N_3 = 6 R_{\rm D}\Theta

\sigma_h = [5(1+0.5 E)R_{\rm AA}/R_{\rm AA}^{(0)} +10 R_{\rm rSS} + 15 R_{\rm SnB} + \Theta (10 \chi_{\rm rSS} + 15\chi_{\rm SnB}) + 8/3]
\sigma_E = 2.5 \Theta(1-E)R_{\rm AA}/R_{\rm AA}^{(0)}
\sigma_{AA} = 5\Theta(1+0.5 E)/R_{\rm AA}^{(0)}

\epsilon_x = N_1/(1-x)
\epsilon_h = -\eta_{1h}\ln (1-x) - \eta_{2h}\ln (1-C) - \eta_{3h} \ln (1-u)
\epsilon_m = N_2\beta_{vm}/(1-C) - \eta_{2m}\ln (1-C)
\epsilon_s = -\eta_{1s}\ln (1-x) - \eta_{2s}\ln (1-C)
\epsilon_d = -\eta_{2d}\ln (1-C)
\epsilon_p = -\eta_{1p}\ln (1-x) + \epsilon_d

\eta_{1h} = 6\left [ R_{\rm GCD}-R_{\rm shout}+R_{\rm AA}+\Theta(\chi_{\rm SS}+\chi_{\rm D0}+\chi_{\rm R})+\chi_{\rm TC}(\mu-d-p) \right ]
\eta_{1s} = 6\pi_s (\Theta-g)
\eta_{1p}= 6\pi_p(\Theta-g)

\eta_{2d} = 6R_{\rm att}\left [\gamma_d(1-A)(1-B_c) - \Phi_{\rm av}(G+(1-G)B_c)\right ]
\eta_{2m} = 6R_{\rm att}(1-A)\left [\beta_{cm}(1-G)+\gamma_m(1-B_c)\right ]
\eta_{2h}=6R_{\rm att}(1-A)(1-B_c)\gamma_h
\eta_{2s}=6R_{\rm att}(1-A)(1-B_c)\gamma_s

\eta_{3h} = 6[R_{\rm D}+\chi_{\rm D0}-\chi_{\rm TC}]

Tying it all together

At this point, you might expect that I would start back-substituting expressions.  There are two reasons I’m not going to do that.  The first is that these expressions are complicated enough that doing so is futile.  It’s hard to get any useful information out of these expressions once they become too complicated to read. It’s a little easier to glean intuition by simply evaluating all of the different factors we have and understanding why they’re positive, negative, small, large, etc.

The other problem is recursion.  If you were paying close attention, you might have noticed the following quandary:

G = R_{\rm SB} T_{\rm buff}
R_{\rm SB} = RPS/60
RPS = \Theta[(1+0.5E)(5R_{\rm AA}/R_{\rm AA}^{(0)})+10R_{\rm rSS}+15R_{\rm SnB}] +(8/3)\Theta_s-3
E =1 -0.8 q_1^{N_1}q_2^{N_2}q_3^{N_3}
N_2 = 6 R_{\rm att}(1-A)\left [ G+ (1-G)B_c\right ]

So G depends R_{\rm SB}, which depends on E, which depends on N_2, which depends on…. G.  This isn’t a simple thing to solve analytically, and the expressions will end up getting even worse in the process. A similar situation happens for haste, mastery, hit, and dodge when we try to evaluate \gamma_i and \eta_2i.

It’s much faster to use recursion to do this numerically.  Basically, we make a guess at G, and then evaluate the whole chain, recalculating G at the end.  We then take the new value of G and repeat the process.  We do this until a certain tolerance threshold is reached – say, the maximum change in value of any of these variables changes by less than 10^-13%.  It turns out that only takes around 12 iterations, so it’s not particularly time consuming.

Numerical Calculation

The code I’m using to calculate the results can be found here:

derivations_warr.m

You can see that I’ve more-or-less reproduced all of the equations from this post, and written small loops to handle the recursive definitions.  The input values I’m using are:

Ar = 40000
A = 0.35 (5% base miss/dodge/parry plus 10% dodge and parry from DR)
A_d = 0.1
A_p = 0.1
A_{p0}=0.5
B_c = 0.55
B_{cm}=0.4
B_v=0.3
C=0.37
h=0.02
e=0.02
s=0.1
x=0.05
\mu = 0.075
d=0.075
p=0.075
g = 0.24
u=0.3
R_{\rm AA}^{(0)} = 1/2.6

k=0.9560
C_d = 0.65631440
C_b = 1.351
K=32573

f_{ar} = Ar+K
f_m = 179.28004
f_b = 0.015
f_cb = 0.015
f_d = 176.71890258/0.01
f_p = 176.71890258/0.01
fs=128.05715942/0.01
fe=120.10880279/0.01
fh=120.10880279/0.01
f_x = 179.28004

T_{\rm buff}=6
R_{\rm att} = 1/2

Numerical Results

Plugging those in and multiplying by 10^5 for ease of analysis, we get the following stat weights:

\Gamma_p=1.2220
\Gamma_d=1.1779
\Gamma_m=0.8315
\Gamma_h=0.6048
\Gamma_{ar}=0.2510
\Gamma_s=0.2254
\Gamma_x =0.0700

Parry and dodge rank at the top, as expected.  Parry pulls ahead of dodge by virtue of increased rage generation through parry-haste, but it’s a pretty small effect.  Mastery doesn’t fare too badly for warriors, but it’s definitely not as attractive as dodge and parry.  I was actually a little surprised at how well mastery did, figuring that the Shield Block mechanic would suppress it some.  In fact, it does – about 80% of mastery’s value comes from the raw mitigation of more critical blocks.  Only about 12% comes from increased block chance, and the other 8% comes from increased Shield Block uptime (via more critical blocks increasing Enrage uptime).

Hit and expertise help increase rage generation and significantly impact Shield Slam casts (by reducing Revenge misses/dodges/parries), so it has a strong effect on resource generation.  Note that armor is actually ahead of mastery in terms of itemization, because you get 4 armor per itemization point.  Haste and crit both provide some increased resource generation (from auto-attacks and Enrage uptime, respectively), but they’re both weak effects.

While we’re at it, let’s see what Shield Block and Enrage uptimes look like:

G: 80.88\%
E: 80.10\%

That’s an incredibly large uptime on Shield Block.  Most of this is due to the changes in this last beta push – doubling the Enrage bonus and reducing Revenge’s cooldown both had a significant impact on rage generation.  Let’s quickly look at the breakdown of rage sources:

AA: 6.58
SS: 2.14
Sh: 0.33
TC: -0.96

So our net rage generation rate is 8.09 rage per second, and the majority of it is coming from Enrage-buffed auto-attacks.  Shield Slam is only accounting for a little over 2 RPS because we’re at low hit/exp.  At 7.5% hit/exp, we jump up to about 9.7 RPS, a little over 7.5 of it from auto-attacks and 2.5 from Shield Slams.  At that level of hit/exp, Shield Block uptime reaches 97% – nearly block-capped.

This is a bit of a problem, in my mind.  Shield Block is rather powerful because it forces block-cap for a fixed time period, and we’ve seen how unbalancing that was in Cataclysm.  If warriors can successfully reach 90%+ uptimes on Shield Block, it will become a balance problem.  I’m not sure what the solution is – nerfing the duration on Shield Block would do it, but that would make Shield Block a bit more timing-intensive too.  Lowering rage generation rates seems like the simplest way to do it.  They could keep the Revenge change, since that makes the rotation more interesting, but drop the auto-attack accumulation rate so that it’s on par with Shield Slams.  That could be achieved by dropping the Enrage bonus, or by keeping Enrage at 50% but dropping the normalized rage generation from auto-attacks from 5/second to 3/second.

Conclusions

In this series, we’ve seen that the warrior equations are much more onerous than the paladin ones due to the complications of parry-haste, Enrage, and auto-attack-based resource generation.  Nonetheless, we slogged through it to get stat weights, and those stat weights seem reasonable given what we know about warrior mechanics.  The dodge/parry values aren’t highly inflated because Shield Block’s guaranteed block effect doesn’t get consumed, unlike the Paladin version.  And since warrior mastery isn’t simply block chance, it interacts with their active mitigation and keeps mastery attractive.  They still see a mitigation benefit from hit/exp, haste, and even crit due to faster rage generation, but those effects are all weaker than the increase from “true” tanking stats.  All in all, the system doesn’t seem too bad.

The real eye-openers are the rage generation levels and subsequent Shield Block uptimes we’re seeing in the model.  If accurate, those suggest a potential balance problem as warriors gear up and can afford to reach hit and expertise caps.  Of course, there could still be changes in the pipeline for warriors, so it’s too early to tell.

Posted in Tanking, Theck's Pounding Headaches, Theorycrafting | Tagged , , , , , , , , , | 7 Comments

Warrior Block Calculations – Part 3

So far, we’ve developed the general expressions for warrior block mechanics, and we’ve worked out the warrior rotation and the effects of parry haste.  In this installment, we’ll work out the last two pieces to the warrior block mechanics puzzle: the Enrage mechanic, and the rage generation equation.

1. Enrage Mechanics

The Enrage buff increases rage generation from auto-attacks by 25%, so we need an accurate model of it if we intend to get accurate estimates of rage generation.  Normally, buff uptimes are fairly simple – a buff with a single potential trigger has an uptime of

{\rm uptime} = 1 - q^N

where q = 1 - p is the probability that the effect doesn’t proc (p is the probability that a proc does occur), and N is the number of proc chances that occur during the buff’s duration.  The logic behind this is pretty straightforward – the probability of having N failed proc chances during the buff’s duration is q^N, and if that happens, the buff will fall off.  You can rigorously show that this is the correct formula using a Markov chain, if you’re so inclined.  Since this is a well-established result, we’re not going to repeat that here.

However, Enrage is more complicated than this, because Enrage has three proc mechanics.  It can proc from critical strikes (so auto-attacks, Shield Slam, Devastate, Revenge, and Thunder Clap in our model), critical blocks, and from automatic crits through the interaction of Ultimatum and Glyph of Incite.  And on top of all of that, Berserker Rage grants the enrage effect on-demand.

1.1. Multi-Sourced Uptime Equation

Let’s ignore Berserker Rage for a moment, and handle the three proc mechanics.  Using the same logic as the simple example, the buff will drop off if we get no successful procs from any of the three mechanics during the buff duration, so our uptime should look like:

{\rm uptime} = 1-q_1^{N_1} q_2^{N_2} q_3^{N_3},

where q_1, q_2, and q_3 are the “failure” chances for critical strikes, critical blocks, and Ultimatum procs, respectively, and N_1, N_2, and N_3 are the number of times each of those events can happen in the 6-second buff duration.  We can write explicit expressions for q_i:

q_1 = 1-x
q_2 = 1-C
q_3 = 1-u

Where x is our crit chance, C is our crit block chance, and u=0.3 is the Ultimatum proc chance. The expressions for N_i are:

N_1=6 [ (R_{\rm SS}+R_{\rm D}+R_{\rm R})\Theta+R_{\rm TC}\Theta_s+R_{\rm AA}(\Theta-g) ]
N_2 = 6 R_{\rm att}(1-A)\left [ G+ (1-G)B_c\right ]
N_3 = 6 R_{\rm D}\Theta

Where we’ve used our definitions of \Theta and \Theta_s from last time, and included the glancing blows chance g.  Here, we’re assuming that the warrior will only ever use Heroic Strike if it’s free (i.e. 0 rage coast), and Glyph of Incite guarantees each of those will be a crit.  If the glyph isn’t used, q_3 is no longer necessary, and regular Ultimatum-induced crits can be folded into N_1 by adding a 1.3 to the coefficient of R_{\rm D}.

Note also that N_1 is complicated by the fact that we have different miss rates for each attack source.  Shield Slam, Devastate, and Revenge are all melee attacks, while Thunder Clap is a spell, and auto-attacks have an extra chance for glancing blows (which cannot be crits).

Berserker rage can be trivially included in this, as it gives 20% uptime automatically.  If we treat this as a stochastic event (i.e. random), then our expression for enrage uptime is

E =1 -0.8 q_1^{N_1}q_2^{N_2}q_3^{N_3}

In theory, you might be able to do a little better than this by saving Berserker Rage and using it only when your stochastic (i.e. random) Enrage effects aren’t active, but it’s not likely to be a big improvement.

1.2. Derivatives of q and N

We’re going to need to differentiate E, so it will be to our benefit to calculate the contributing differentials dq_i and dN_i.  The dq_i are very simple:

dq_1 = -dx
dq_2 = -dC = -dm \beta_{vm}
dq_3 = 0

where \beta_{vm} is as defined in part 1.

The derivatives of N_i are much more annoying, so we’ll do them one at a time:

dN_1 = 6d\Theta(R_{\rm SS}+R_{\rm D}+R_{\rm R}+R_{\rm TC}+R_{\rm AA}) \\+ 6\Theta(dR_{\rm SS}+dR_{\rm D}+dR_{\rm R}) \\+6\Theta_s dR_{\rm TC}+6 dR_{\rm AA}(\Theta-g)

There are a few simplifications we can make to this expression.  We know that dR_{\rm D} = dR_{\rm D0} - dR_{\rm TC} from part 2.  We also know that R_{\rm SS}+R_{\rm D}+R_{\rm R}+R_{\rm TC} is all of our GCD-based casts except for Battle Shout, thus

R_{\rm SS}+R_{\rm D}+R_{\rm R}+R_{\rm TC} = R_{\rm GCD} - R_{\rm shout}

Using all of these, along with our definitions of dR_{\rm SS}, dR_{\rm R}, dR_{\rm D0}, dR_{\rm TC}, and dR_{\rm AA} from part 2, we have:

dN_1 = 6 d\Theta [(R_{\rm GCD}-R_{\rm shout}+R_{\rm AA}) +\Theta(\chi_{\rm SS} + \chi_{\rm D0} + \chi_{\rm R}) + (\Theta-\Theta_s)\chi_{\rm TC} ]\\+ ds 6\pi_s(\Theta-g) \\+ da_p 6\pi_p(\Theta-g)

We can define a few abstraction factors to help clean this up:

dN_1 = \eta_{1h}(dh+de)+\eta_{1s}ds+\eta_{1p}da_p
\eta_{1h} = 6\left [ R_{\rm GCD}-R_{\rm shout}+R_{\rm AA}+\Theta(\chi_{\rm SS}+\chi_{\rm D0}+\chi_{\rm R})+\chi_{\rm TC}(\mu-d-p) \right ]
\eta_{1s} = 6\pi_s (\Theta-g)
\eta_{1p}= 6\pi_p(\Theta-g)

N_2 is even worse, because it’s got G in it.  Let’s crank through that:

dN_2 = -6R_{\rm att}(dA_d + dA_p)[G + (1-G)B_c] \\+ 6R_{\rm att}(1-A)[dG(1-B_c)+(1-G)dB_c]

And, again, saving you some algebra:

dN_2 = (da_d+da_p)\eta_{2d}+dm\eta_{2m}+(dh+de)\eta_{2h}+ds\eta_{2s},

where

\eta_{2d} = 6R_{\rm att}\left [\gamma_d(1-A)(1-B_c) - \Phi_{\rm av}(G+(1-G)B_c)\right ]
\eta_{2m} = 6R_{\rm att}(1-A)\left [\beta_{cm}(1-G)+\gamma_m(1-B_c)\right ]
\eta_{2h}=6R_{\rm att}(1-A)(1-B_c)\gamma_h
\eta_{2s}=6R_{\rm att}(1-A)(1-B_c)\gamma_s,

and I’ve used the \gamma_i definitions from part 1.

dN_3 is quite simple:

dN_3 = 6dR_{\rm D}\Theta + 6R_{\rm D}d\Theta

which simplifies to

dN_3 = (dh+de)eta_{3h}
\eta_{3h} = 6[R_{\rm D}+\chi_{\rm D0}-\chi_{\rm TC}].

Now, we have all the derivatives of q_i and N_i that we need to complete our Enrage model.  All that remains is to differentiate our expression for E and plug in the results from this section.

1.3. Differentiating E

Unfortunately, differentiating E is a bit of a pain.  The problem is that both q_i and N_i differentials come into play, which makes the results a bit cumbersome.  To differentiate, we’ll make use of the identity:

d(x^y) = yx^{y-1}dx + ln(x)x^y dy

Doing so gives us:

dE = -0.8 [ (N_1 q_1^{N_1-1} dq_1 + \ln(q_1) q_1^{N_1} dN_1) q_2^{N_2} q_3^{N_3} \\+ q_1^{N_1} (N_2 q_2^{N_2-1} dq_2 + \ln(q_2) q_2^{N_2} dN_2) q_3^{N_3} \\+ q_1^{N_1} q_2^{N_2} (N_3 q_3^{N_3-1} dq_3 + \ln(q_3)q_3^{N_3} dN_3)]

We can simplify this some by pulling out a factor of q_1^{N_1}q_2^{N_2}q_3^{N_3}:

dE = -0.8q_1^{N_1}q_2^{N_2}q_3^{N_3}[(N_1 dq_1/q_1 + \ln(q_1) dN_1) \\+ (N_2 dq_2/q_2 + \ln(q_2) dN_2) \\+ (N_3 dq_3/q_3 + \ln(q_3) dN_3)]

We can simplify this further by noting that (1-E) = 0.8 q_1^{N_1} q_2^{N_2} q_3^{N_3} and plugging in our results for dq_i:

dE = (1-E)\left [ N_1 dx/(1-x) - \ln(1-x)dN_1 + N_2 dm\beta_{vm}/(1-C) -\ln(1-C) dN_2 - \ln (1-u) dN_3\right ]

Unfortunately, the expression balloons a bit when we plug in the dN_i:

dE = (1-E) [ dx \frac{N_1}{1-x} - (dh+de)\eta_{1h}\ln (1-x) - ds\eta_{1s}\ln (1-x) - da_p \eta_{1p}\ln (1-x) \\+ dm\frac{N_2\beta_{vm}}{1-C} - (da_d+da_p)\eta_{2d}\ln (1-C) - dm \eta_{2m}\ln (1-C) - (dh+de) \eta_{2h}\ln (1-C) \\- ds \eta_{2s}\ln (1-C) - (dh+de)\eta_{3h}\ln (1-u)]

But by combining terms and abstracting, we can get a reasonably manageable form:

dE = (1-E)[dx\epsilon_x + (dh+de)\epsilon_h+dm\epsilon_m+ds\epsilon_s + da_d\epsilon_d+da_p\epsilon_p]
\epsilon_x = N_1/(1-x)
\epsilon_h = -\eta_{1h}\ln (1-x) - \eta_{2h}\ln (1-C) - \eta_{3h} \ln (1-u)
\epsilon_m = N_2\beta_{vm}/(1-C) - \eta_{2m}\ln (1-C)
\epsilon_s = -\eta_{1s}\ln (1-x) - \eta_{2s}\ln (1-C)
\epsilon_d = -\eta_{2d}\ln (1-C)
\epsilon_p = -\eta_{1p}\ln (1-x) - \eta_{2d}\ln (1-C) = -\eta_{1p}\ln (1-x) + \epsilon_d

Which is our final form for dE, and completes our model of Enrage effects.  Note that while there seem to be a lot of negative signs in these expressions, it’s because all of the natural log factors are negative.

2. Rage Per Second Calculation

At long last, we’re ready to construct our rage generation equation.  The basic form will be:

RPS = RPS_{\rm AA} + RPS_{\rm SS} + RPS_{\rm shout} - RPS_{\rm TC}

The easiest is RPS_{\rm shout}, which is just

RPS_{\rm shout} = 20 R_{\rm shout} = 20/60 = 1/3,

and since it’s a constant, its derivative is zero.

2.1. Shield Slam Rage Generation

Shield Slam is also fairly straightforward,

RPS_{\rm SS} =\Theta[10 R_{\rm rSS} + 15 R_{\rm SnB}]

The term in brackets is the base SS rage generation rate (maximum 2.8 rage per second at hit- and exp-caps). The derivative is then,

dRPS_{\rm SS} = d\Theta[10 R_{\rm rSS} + 15 R_{\rm SnB}] + \Theta[10 dR_{\rm rSS} + 15 dR_{\rm SnB}]

Using our expressions for dR_{\rm rSS} and dR_{\rm SnB} from part 2, we have:

dRPS_{\rm SS} = (dh+de)[10R_{\rm rSS}+15R_{\rm SnB} + \Theta(10\chi_{\rm rSS}+15\chi_{\rm SnB})]

2.2. Thunder Clap Rage Cost

Thunder Clap is also not particularly difficult, since we’ve already developed an expression for R_{\rm TC}:

RPS_{\rm TC} = 20 R_{\rm TC} = 10/3 - (8/3)\Theta_s

Differentiating gives us

dRPS_{\rm TC} = -(8/3)(dh+de).

2.3. Auto-Attack Rage Generation

Finally, the expression for auto-attacks is:

RPS_{\rm AA} = \Theta(1+0.5 E)(5/R_{\rm AA}^{(0)}) R_{\rm AA}

Note that each melee attack generates a normalized amount of rage 5/R_{\rm AA}^{(0)}.

Differentiating gives us,

dRPS_{\rm AA} = d\Theta(1+0.5E)(5/R_{\rm AA}^{(0)})R_{\rm AA} \\+dE 2.5\Theta R_{\rm AA}/R_{\rm AA}^{(0)} \\+dR_{\rm AA}\Theta(1+0.5E)(5/R_{\rm AA}^{(0)})

2.4. Combined Rage Generation Equations

Combining all four of these expressions, we have:

RPS =\Theta(1+0.5E)(5R_{\rm AA}/R_{\rm AA}^{(0)}) + \Theta[10R_{\rm rSS}+15R_{\rm SnB}] - (10/3-(8/3)\Theta_s)+1/3

which simplifies to

RPS = \Theta[(1+0.5E)(5R_{\rm AA}/R_{\rm AA}^{(0)})+10R_{\rm rSS}+15R_{\rm SnB}] +(8/3)\Theta_s-3.

The Shield Block cast rate R_{\rm SB} is simply RPS/60.  We could differentiate this directly, but it’s a little easier to just combine the individual differentials worked out earlier:

dRPS = (dh+de)(1+0.5E)(5R_{\rm AA}/R_{\rm AA}^{(0)}) + dE 2.5\Theta R_{\rm AA}/R_{\rm AA}^{(0)} \\+dR_{\rm AA}\Theta (1+0.5E)(5/R_{\rm AA}^{(0)}) +(dh+de)[10 R_{\rm rSS} + 15 R_{\rm SnB} + \Theta (10 \chi_{\rm rSS} + 15\chi_{\rm SnB})] \\+(8/3)(dh+de)

Which is, of course, annoying to deal with, so we abstract some:

dRPS = (dh+de)\sigma_h + dE\sigma_E/(1-E) + dR_{\rm AA}\sigma_{AA}

\sigma_h = [5(1+0.5 E)R_{\rm AA}/R_{\rm AA}^{(0)} +10 R_{\rm rSS} + 15 R_{\rm SnB} + \Theta (10 \chi_{\rm rSS} + 15\chi_{\rm SnB}) + 8/3]
\sigma_E = 2.5 \Theta(1-E)R_{\rm AA}/R_{\rm AA}^{(0)}
\sigma_{AA} = 5\Theta(1+0.5 E)/R_{\rm AA}^{(0)}

We then plug in our expressions for dE and dR_{\rm AA}:

dRPS = (dh+de)\sigma_h + \sigma_E (dx\epsilon_x + (dh+de)\epsilon_h + dm\epsilon_m + ds\epsilon_s + da_d\epsilon_d + da_p\epsilon_p) + \sigma_{AA}(ds\pi_s + da_p\pi_p)

Combining terms, we have

dRPS = (dh+de)[\sigma_h + \sigma_E\epsilon_h] + ds[\sigma_E\epsilon_s + \sigma_{AA}\pi_s] + dm\sigma_E\epsilon_m \\+ dx\sigma_E\epsilon_x + da_d\sigma_E\epsilon_d + da_p[\sigma_E\epsilon_p + \sigma_{AA}\pi_p]

The change in the rate of Shield Block casts dR_{\rm SB} due to a change in rage generation is simply dRPS/60, and we’ve already defined the expression for dR_{\rm SB} in part 1:

dR_{\rm SB} = \frac{dRPS}{60} = (dh+de)\rho_h + ds\rho_s + dm\rho_m + da_d\rho_d + da_p\rho_p + dx\rho_x

By inspection, we can now define the \rho_i as follows:

\rho_h = (\sigma_h+\sigma_E\epsilon_h)/60
\rho_s = (\sigma_E\epsilon_s + \sigma_{AA}\pi_s)/60
\rho_m = (\sigma_E\epsilon_m)/60
\rho_x = \sigma_E\epsilon_x/60
\rho_d = \sigma_E\epsilon_d/60
\rho_p = (\sigma_E\epsilon_p+\sigma_{AA}\pi_p)/60 = \rho_d + (\sigma_E\eta_{1p} \ln x + \sigma_{AA}\pi_p)/60

And finally, we have a complete model of warrior block mechanics.  In the next post, I’ll collect all of the expressions we need in one place and plug in numbers to see how the different stats compare.

Posted in Tanking, Theck's Pounding Headaches, Theorycrafting | Tagged , , , , , , , , , | 1 Comment

Warrior Block Calculations – Part 2

In the last installment, we calculated expressions for the normalized scale factors \Delta_i that represent the effectiveness of a given stat at reducing damage taken.  To finish the job, we need to calculate explicit expressions for the variables \rho_h, \rho_e, \rho_s, \rho_m, \rho_x, \rho_d, and \rho_p, which represent how a warrior’s Shield Block cast rate varies with hit, expertise, haste, mastery, crit, dodge, and parry, respectively.

1. Rotation Modeling

The first step in this process is to determine exactly what the warrior rotation looks like.  We can make a few assumptions here:

  • The warrior dumps all of their rage into Shield Block
  • We don’t care about DPS, so we’ll use the highest-rage-per-second rotation

The primary source of rage generation is Shield Slam (SS), which generates 10 rage.  However, it generates 15 rage if cast during a Sword and Board (SnB) proc.  So the rotation will revolve around maximizing SS casts.  The most efficient way to do that is with a simple Priority: SS>Revenge>Devastate

In this model, we cast the abilities as soon as they’re available, following the priority order given.  If you do that, you can break the rotation down into four possible sub-sequences:

  • 1: SS-Revenge
  • 2: Devastate-SS-Revenge
  • 3: Devastate-Devastate-SS-Revenge
  • 4: SS(SnB)-Devastate-Revenge

This may look funny, but the idea is reminiscent of a Markov chain.  Every time we cast Revenge, we get a branch, and #1-4 are the only four possible branches we can enter.  For example, we always start with #1.  If that Revenge triggers SnB, we always jump to #4 (SS-Rev-SS(SnB)-Dev-Rev), and if it doesn’t we’ll always jump to #3 (SS-Rev-Dev-Dev-SS-Rev).  Thus, we can find the steady-state probability of each sequence occurring using the concept of Stochastic (or Markov) matrices.  We construct our transition probability matrix P:

In this matrix, each element P_{ij} represents the probability of transitioning from state i to state j.  To find the steady-state probabilities, we just calculate P^k in the limit as k \rightarrow \infty.  In fact, we don’t even need to go very high in k – the matrix P^k is identical for k>2:

Thus, we have a 24% chance of being in state #2, a 16% chance of being in state #3, and a 60% chance of being in state #4.  We can use this information to calculate the generated rage per second.  State 2 generates 10 rage in 4.5 seconds, state 3 generates 10 rage in 6 seconds, and state 3 generates 15 rage in 4.5 seconds, giving a net Rage Per Second (RPS) of (10/4.5*0.24 + 10/6*0.16+15/4.5*0.60) = 2.8 rage per second.  This is the maximum rage generation rotation for warriors now that Revenge has a 4.5-second cooldown.

There’s an additional complication though – we haven’t accounted for Revenge misses.  That reduces the transition probability to state #4 from 0.6 to 0.6(1-(\mu-h)-(d+p-e)), and increases the other transition probabilities from 0.4 to 1-0.6(1-(\mu-h)-(d+p-e)).  Here, \mu is our base chance to miss (7.5% against a boss), d and p are our base chance to be dodged/parried, and h and e are our hit and expertise.  Since this term in parentheses (which is our melee success probability) will show up fairly often, let’s define a placeholder for it.  While we’re at it, we’ll define one for our spell success probability (1-(2\mu-h-e)) as well:

\Theta = (1-(\mu-h)-(d+p-e))
d\Theta = (dh+de)
\Theta_s = (1-(2\mu-h-e))
d\Theta_s = (dh+de) = d\Theta

Once we include this, the steady-state probabilities are analytically:

\Pi_2 = P^k_{i2} = (0.6 \Theta)(1- 0.6 \Theta)
\Pi_3 = P^k_{i3} = (1- 0.6 \Theta)^2
\Pi_4 = P^k_{i4} = 0.6 \Theta

and their derivatives are

d\Pi_2 = 0.6(1-1.2\Theta)d\Theta
d\Pi_3 = -1.2(1-0.6\Theta)d\Theta
d\Pi_4 = 0.6d\Theta

We can then represent this rotation by writing down the cast rates for certain abilities.

1.1 Shield Slam cast rates

Starting with the highest priority, Shield Slam:

R_{\rm SS} = R_{\rm rSS} + R_{\rm SnB}

We’ve divided this up into rates for “regular” Shield Slams R_{\rm rSS} and Sword and Board Shield Slams R_{\rm SnB}.  By deconstructing the weighted average we used above, you can show that

R_{\rm rSS} = \Pi_2/4.5+\Pi_3/6
R_{\rm SnB}=\Pi_4/4.5.

We’ll also want the variation of each of these terms, so let’s calculate them right away:

dR_{\rm rSS} = d\Pi_2/4.5+d\Pi_3/6 = d\Theta \chi_{\rm rSS}
dR_{\rm SnB} = d\Pi_4/4.5 = d\Theta 0.6/4.5 = d\Theta \chi_{\rm SnB}
dR_{\rm SS} = dR_{\rm rSS} + dR_{\rm SnB} = d\Theta \chi_{\rm SS}

with

\chi_{\rm rSS} = 0.6(1-1.2\Theta)/4.5 -1.2(1-0.6\Theta)/6
\chi_{\rm SnB} = 0.6/4.5
\chi_{\rm SS} = \chi_{\rm rSS}+\chi_{\rm SnB}

1.2 Revenge cast rates

We can also quickly figure out Revenge’s cast rate,

R_{\rm R}=(\Pi_2+\Pi_4)/4.5+\Pi_3/6 = R_{\rm SS}

using the same technique.  Note that we’re assuming here that Revenge is always available, which is a pretty safe assumption if you’re tanking.  It turns out we won’t need either of these values, but they’re good to have lying around.  The derivatives of R_{\rm R} are identical to that of R_{\rm SS}, so

dR_{\rm R} = d\Theta \chi_{\rm R} = d\Theta \chi_{\rm SS}.

1.3 Thunder Clap and Battle Shout cast rates

Devastate is a little trickier, because we’ll be replacing some Devastate casts with Battle Shouts and Thunder Claps.  So let’s work out those first.  Shout is easy – it’s just

R_{\rm shout} = 1/60.

Thunder Clap is slightly trickier.  Ideally, we would cast it every 30 seconds to keep up the Weakened Blows debuff.  However, it’s not automatic.  If it misses, we’ll cast it again as soon as it’s available (6 seconds).  So the proper way to write it would be:

R_{\rm TC} = \frac{\Theta_s}{30}+\frac{1-\Theta_s}{6}.

In other words, it’s the weighted average based on hit/miss chances.  Note that I’ve assumed it misses as a spell for this rather than as a melee strike.  I’m not sure if this is correct, but it’s easy to fix if we find out that assumption is wrong.  This simplifies to

R_{\rm TC} = 1/6 - (2/15)\Theta_s,

and the derivative  is

dR_{\rm TC} = -\chi_{\rm TC}d\Theta,

with \chi_{\rm TC}=\frac{2}{15}.

1.4 Devastate cast rates

Now for Devastate.  In the scenarios above, we cast Devastate at a rate of

R_{\rm D0}=(\Pi_2+\Pi_4)/4.5+2\Pi_3/6,

with a derivative scaling of

dR_{\rm D0} = \chi_{\rm D0}d\Theta,

with

\chi_{\rm D0} = 0.6(2-1.2\Theta)/4.5 - 1.2(1-0.6\Theta)/3.

However, we need to subtract off to account for Thunder Clap and Battle Shout:

R_{\rm D} = R_{\rm D0} - R_{\rm TC} - R_{\rm shout}

From this we can immediately see that

dR_{\rm D} = dR_{\rm D0}-dR_{\rm TC} = d\Theta(\chi_{\rm D0}-\chi_{\rm TC}),

We could substitute in our expressions for R_{\rm TC} and R_{\rm shout}, but that doesn’t make the expression too much easier to work with, so we won’t bother.  At this point, we have a complete model of the warrior rotation.

If Shield Slam and Battle Shout were a warrior’s only sources of rage, we’d be done here.  Rage generation would simply be the rage from Shield Slams plus the rage from Battle Shouts minus the rage used to maintain The Clap.  And that would be really simple. But we’re not so lucky, because warriors also generate rage from auto-attacks.  And that leads to a number of problems.

First, we need to accurately model melee swings, which means we need to care about parry-hasting.  That’s a non-trivial problem, and will be the focus of the second half of this post.  The other issue is that Enrage increases rage generation from those auto-attacks during its effect, which means we need an accurate model of Enrage uptime.  And to do that, we need to account for all sources of Enrage – critical strikes, critical blocks, and even the interaction of Ultimatum with Glyph of Incite.  We’ll deal with the Enrage modeling next time.

2. Parry Hasting

Parry-hasting as a mechanic can be extraordinarily complicated in the general case.  I’ve worked on this problem before, and the matlab code has a function that specifically models parry-hasting.  If we wanted to treat the problem completely generally, it wouldn’t be possible to come up with a nice analytical formulation.  However, we’re going to limit our scope to a single situation – tanking a single raid boss – which makes it possible to analytically represent parry-haste.

The mechanics of parry-haste work like so: When you parry an attack, your swing timer is reduced by 40% of its un-hasted value, unless that would reduce the swing timer to below 20% of that un-hasted value, in which case it reduces the swing timer to 20% exactly.  That’s a little clumsy, so let’s consider an example.  If I have a 2.00-speed weapon, my swing timer is 2 seconds.  If I parry the boss when there’s 1.9 seconds left on my swing timer, it reduces my swing timer by 40% of 2 seconds, which is 0.8 seconds, leaving 1.1 seconds remaining on my swing timer.  If I parry when there’s 0.9 seconds left on my swing timer, it can’t reduce my swing timer by the full 40% because that would drop itbelow the 20% threshold (which is 0.4 seconds).  So instead, it drops the swing timer to 0.4 seconds exactly, giving me 0.5-seconds of parry-hasting (only 25%, instead of 40%).  If I parry an attack when there’s 0.4 seconds or less left on my swing timer, there’s no effect at all.

Thus, the average amount of parry-haste you get from a single parry is 24%.  There’s an additional complication, which is something we call “Doubly Degenerate Parry Events” (DDPEs).  This is when you parry two attacks in rapid succession, and that ends up causing some attrition in parry (the second parry is less effective, necessarily).  However, we’re going to consider a boss with a swing timer between 1.5 and 2.0 seconds, which eliminates the possibility of DDPEs.  So we can ignore that problem.

Generally, one would represent parry haste by a set of coupled equations linking the boss attack rate R_{\rm att} to the player auto-attack rate R_{\rm AA}.  However, since bosses no longer benefit from parry-haste, the equations are considerably simplified, and the coupling is one-way:

R_{\rm AA} = R_{\rm AA}^{(0)}(1+s)+cR_{\rm att}

Here, c is the coupling constant that represents the effect of parry-haste.  If we ignore DDPEs, we can represent c as:

c = 0.24*{\rm binopdf}(1,1,P)*({\rm chance~of~1~attack~in~T_{\rm AA}}) \\ + 0.24*[1-{\rm binopdf}(0,2,P)-{\rm binopdf}(2,2,P)]*({\rm chance~of~2~attacks~in~T_{\rm AA}})

To get some intuition here, {\rm binopdf(k,N,P)} is the binomial probability distribution representing having k successful events out of N trials, with a success chance of P.  In our case, P = A_{p0}+A_p, the chance to parry the incoming attack.  Thus, the first term represents the parry-haste we gain if the boss only attacks once during the swing timer and we parry that attack.  The second term represents the gain when there are two boss attacks during the swing timer, and the expression in brackets is the probability that we parry exactly one of those attacks (which is 1 minus the probability that we parry neither minus the probability that we parry both).

The text expressions can be converted to analytic expressions if we define

pho=R_{\rm att}/R_{\rm AA}^{(0)}(1+s),

which is the average number of parry-haste opportunities we have in a hasted swing timer.  The chance of having one incoming attack is then just 2-pho, and the chance of having two incoming attacks is pho-1.  In addition, we can substitute in the exact form of {\rm binopdf()} since our cases are fairly simple.  This gives us an expression for c that looks like this:

c = 0.24 P (2-pho)+0.24 (1-(1-P)^2-P^2) (pho-1) \\ = 0.24 P (pho-2 P pho + 2P)

and substituting in our expression for pho, we finally have:

c = 0.24 P \left ( \frac{R_{\rm att}}{R_{\rm AA}^{(0)}(1+s)}-\frac{2R_{\rm att}P}{R_{\rm AA}^{(0)}(1+s)}+2P\right )

To give you some idea of how big an effect this is, assume a boss swing timer of 2 seconds, a 2.6-speed weapon, and 10% melee haste.  Then R_{\rm att}=1/2, R_{\rm AA}^{(0)}=1/2.6, (1+s)=1.1, and c=0.0532.  That takes a hasted swing timer of 2.3636 seconds and reduces it to 2.228 seconds, on average, or about 5.74% faster auto-attack speed (i.e. 5.74% haste).

What we’ll need for our derivation, in addition to the expression for R_{\rm AA} and c, are their derivatives:

dR_{\rm AA} = ds R_{\rm AA}^{(0)} + dc R_{\rm att}

and, saving you some algebra on dc:

dc = -ds (1-P)\frac{0.24*P*R_{\rm att}}{R_{\rm AA}^{(0)}(1+s)^2} \\ + 0.24 (da_p)\Phi_{\rm av} \left [ \frac{R_{\rm att}(1-4P)}{R_{\rm AA}^{(0)}(1+s)}+4P\right ]

If we combine these expressions, and define some abstraction variables, we get

dR_{\rm AA} = ds\pi_s + da_p\pi_p

where

\pi_s = R_{\rm AA}^{(0)} - \frac{0.24 P (1-P) R_{\rm att}}{R_{\rm AA}^{(0)}(1+s)^2}
\pi_p = 0.24 R_{\rm att} \Phi_{\rm av}\left ( \frac{R_{\rm att}(1-4P)}{R_{\rm AA}^{(0)}(1+s)}+4P\right )

Which is what we’ll need to develop our expression for rage generation, which will turn into an equation for the Shield Block cast rate R_{\rm SB}.

It’s important to include parry-haste if we want a reasonable expression for R_{\rm SB}.  Without including parry-hasting, we’d be over-valuing haste by about 10% (\pi_s \approx 0.3434, while R_{\rm AA}^{(0)} \approx 0.3846) and we wouldn’t have the parry-specific scaling of rage generation that differentiates it from dodge.

In the next post, we’ll tackle the Enrage problem, and tie it together with parry-haste and the warrior rotation developed in this post to complete our expression for rage generation.

Posted in Tanking, Theck's Pounding Headaches, Theorycrafting | Tagged , , , , , , , , , | 13 Comments

Warrior Block Calculations – Part 1

In response to my earlier work on paladin MoP Block mechanics, several of my warrior friends asked if I could perform a similar calculation for their class.  I thought that it would be a relatively straightforward calculation – after all, their active mitigation only involves one buff, so it should be simpler than the paladin version, right?

Boy was I wrong.

The warrior calculation is far, far worse than the paladin one, for subtle reasons.  It’s somewhat ironic, because the calculations for how warriors spend resources are much easier, just as I thought they would be.  The problem is that their resource generation is a tangled, unimaginably awkward mess.  Primarily because of two specific complications: the Enrage mechanic and parry-haste.  At several points within the calculation, I thought about deftly avoiding both of those problems through approximation techniques.  But in the end, my curiosity got the better of me, and I treated both of them in full, with as little approximation as necessary.  Unfortunately, that’s 8 hours of my life I’ll never get back.

But in any event, the warrior calculation is complete, and I’m going to publish it in 4 parts.  In this first part, I’ll go through the basic framework, which is very similar to the paladin calculation.  In the second part, we’ll dig into the warrior rotation and tackle the problem of parry-hasting.  The third part will deconstruct the Enrage mechanic and develop the expression for rage generation.  And then in part 4, we’ll tie everything together, plug in numbers, and see how things work out numerically.  I’ll probably skip the Monte-Carlo sim this time, so there likely won’t be a part 5.

A Word On Notation

In the paladin calculation, I was a bit haphazard with variable names.  This came back to bite me a few times, and in retrospect I would have done a few things differently to make it clearer where certain factors came from.  Luckily, I’ve since developed a slightly better set of variable-naming conventions, which turned out to be essential to making the warrior calculation manageable.  I’m going to use the new notation here, so be aware that certain variables (ex: \gamma) won’t share a meaning between the paladin and warrior calculations.  If Blizzard ends up changing our active mitigation mechanics, I’ll have to re-do the paladin calculation, and at that point I’ll update our version to the new notation.

1. The Incoming Damage Formula

As for paladins, the incoming damage formula can be written:

D = D_0 S F_{\rm ar} F_{\rm av} F_b

Where D is net damage taken, D_0 is damage intake before mitigation/avoidance effects, S is the spec-based mitigation factor (Sanctuary for paladins, Defensive Stance for warriors, S=0.9), and F_{\rm ar}, F_{\rm av}, and F_b are the armor, avoidance, and block mitigation factors, respectively.  We’ll define each of those shortly.  To see how damage varies with different stats, we’ll want to perform implicit differentiation on (1):

\frac{dD}{D_0 S} = dF_{\rm ar} F_{\rm av} F_b + dF_{\rm av} F_{\rm ar}F_b+ dF_b F_{\rm ar} F_{\rm av}

D_0 and S don’t vary with rating, so we divide through by those to normalize.  Thus, to determine how damage varies with stats, we need to define all three of the mitigation factors and differentiate them.  The armor and avoidance factors are easy, and identical to the paladin version, so we’ll start with them.

1.1 Armor Mitigation Factor

The armor factor works exactly the same way it does in Cataclysm.  An amount of armor Ar grants mitigation M_{\rm ar} equal to

M_{\rm ar} = Ar / (Ar + K),

where K is the armor constant (32573 for an 88 boss).  The armor factor is then

F_{\rm ar} = 1-M_{\rm ar} = K/(Ar+K),

and differentiating we get

dF_{\rm ar} = -dAr K / (Ar+K)^2 = \frac{-dAr}{f_{ar}}F_{\rm ar}

where we’ve defined f_{ar}=(Ar+K) to put this in a form reminiscent of the other factors we’ll encounter during the derivation.  f_{ar} can be thought of as a “rating-to-percent” conversion factor for armor, since that’s what f_i will represent for rating r_i.

1.2 Avoidance Mitigation Factor

Avoidance is only slightly trickier due to diminishing returns (DR).  In addition, we can no longer treat dodge and parry as identical, as we could in the paladin calculation.  So we’ll just keep track of them both.  Let our warrior’s total avoidance A have the form:

A = A_{m0}+A_{p0}+A_{d0}+A_p+A_d

where A_{m0}, A_{p0}, and A_{d0} are miss, parry, and dodge from sources that aren’t subject to DR, while A_p and A_d are parry and dodge gained from sources which are subject to DR.  The avoidance mitigation factor is,

F_{\rm av} = 1-A,

and differentiating this gives us

dF_{\rm av} =-dA_d -dA_p

To express the post-DR differentials in terms of rating, we need to differentiate the diminishing returns equation:

\frac{1}{A_d} = \frac{1}{C_d} + \frac{k_d}{a_d}

which gives us

dA_d = \frac{da_d}{k_d}\left ( 1-A_d/C_d \right )^2.

Here, C_d and k_d are the diminishing returns coefficients for dodge, and a_d is the pre-DR amount of dodge corresponding to A_d post-DR dodge.  Parry follows an identical equation.  If we assume that the player has balanced dodge and parry (such that A_d \approx A_p), then we can simplify this slightly by introducing an avoidance dependency factor \Phi_{\rm av}:

\Phi_{\rm av} = \frac{1}{k_d}\left ( 1-\frac{A_d}{C_d} \right )^2

such that dF_{\rm av} is

dF_{\rm av} = -\left ( \frac{dr_d}{f_d} + \frac{dr_p}{f_p} \right ) \Phi_{\rm av}.

Again, f_d and f_p are our rating-to-percent conversion factors for dodge and parry.

1.3 Block Mitigation Factor

The block mitigation factor is more complicated, just as in the paladin case.  We start from the expression:

F_b = G[1-(1+C)B_v] + (1-G)B_c[1-(1+C)B_v]+(1-G)(1-B_c),

which can be re-arranged to the simpler form

F_b = 1-G(1+C)B_v - (1-G)B_c(1+C)B_v.

In this expression, G is the probability that an un-avoided attack becomes a guaranteed block.  C is the critical block chance, as determined from mastery.  B_c is the player’s total block chance, and $B_v$ is the player’s block value.  Note that B_v=0.3 by definition, since we’re representing critical blocks explicitly with C. If we differentiate F_b, we have

dF_b = -dG \phi_{\rm G} - dC \phi_{\rm BV} - dB_c \phi_{\rm BC},

where we’ve defined the factors

\phi_{\rm G} = (1-B_c)(1+C)B_v
\phi_{\rm BV} = [G+(1-G)B_c]B_v
\phi_{\rm BC} = (1-G)(1+C)B_v.

We still need to develop explicit expressions for dG, dC, and dB_c to complete the derivation.  The last two are easy, so we’ll tackle them first; dG is where the complications turn up, so we’ll save that for later.

1.3.1 – Block Chance derivative

Block chance can be written

B_c = B_c^{(0)} + B_{cm},

where B_c^{(0)} is block chance from sources unaffected by DR (so, base block and Bastion of Defense) and B_{cm} is the post-DR block chance from any other sources (which, as far as I know, is only mastery).  The diminishing returns equation for block is identical to that of avoidance, but with different coefficients C_B and k_B:

\frac{1}{B_{cm}} = \frac{1}{C_B} + \frac{k_B}{b_{cm}}.

Just as we did for avoidance, we can relate dB_{cm} to db_{cm}:

dB_{cm} = \frac{db_{cm}}{k_B} \left (1-B_{cm}/C_B \right )^2.

db_{cm} is related to a change in mastery (dm) by db_{cm}=dm f_b , where f_b is the mastery-to-block-chance conversion factor (1.5%).  To put this in terms of rating, we use dm = dr_m / f_m, where f_m is the rating-to-mastery conversion factor.

Bringing this all together, and noting that dB_c = dB_{cm}, we have

dB_c = \left ( \frac{dr_m}{f_m} \right ) \beta_{cm},

where we’ve defined \beta_{cm} as

\beta_{cm} = \frac{f_b}{k_B}\left ( 1-\frac{B_{cm}}{C_B} \right )^2

These last two equations complete the factor dB_c.

1.3.2 – Crit Block derivative

Critical block chance can be expressed in the form,

C = C^{(0)} + C_m,

where C^{(0)} is crit block from other sources and C_m is crit block from mastery.  I’m assuming there’s no diminishing returns on crit block; luckily if this is in error it’s a simple fix.  Differentiating, we have

dC = dC_m = dm \beta_{vm} = \left ( \frac{dr_m}{f_m}\right )\beta_{vm},

where \beta_{vm} = f_{cb} is the mastery-to-crit-block conversion factor (1.5%).

1.3.3 – Shield Block derivative

dG is complicated because it depends on many different things.  The expression for G itself is not particularly onerous:

G = R_{\rm SB} T_{\rm buff}

with R_{\rm SB} representing the Shield Block cast rate, and T_{\rm buff} the duration of the Shield Block buff.  Note that G is also the uptime of the Shield Block buff.  Differentiating G, we get,

dG = dR_{\rm SB}T_{\rm buff}

The next step would be to start calculating R_{\rm SB} and dR_{\rm SB}, and plug those results into G and dG.  Unfortunately, that’s the hard part of the calculation, and requires some complicated modeling of parry-haste and Enrage mechanics.  We’ll tackle that in parts 2 and 3 of the series.  For now, we cheat: we’ll use the answers in abstracted form.  This works because we know the form of the answer, so by defining variables to represent the stuff we haven’t worked out yet, we can continue the calculation and get to the end.

Let’s assume that dR_{\rm SB} has the form:

dR_{\rm SB} = (dh+de)\rho_h + ds\rho_s + dm\rho_m + da_d \rho_d + da_p\rho_p + dx\rho_x,

where I’ve used dh to represent a small change in hit, de a small change in exp, ds a small change in haste, dm a small change in mastery, da_d a small change in dodge, da_p a small change in parry, and dx a small change in critical strike (yes, crit is a mitigation stat for warriors).  Note that these are changes in percentages; thus dh is equivalent to a rating change of dr_h/f_h, and so on for the other factors.  The variables \rho_i are just like our earlier factors of \Phi_{\rm av} or \beta_{im}, in that they represent the effect that a small change in a particular stat causes in dR_{\rm SB}.  The \rho_i can (and will) depend on any of the input variables.  Finding suitable expressions for \rho_i will be the objective of the next two blog posts.

In the meantime, we can use these results to put dG in a more convenient form:

dG = (dh+de)\gamma_h + ds\gamma_s + dm\gamma_m + dx\gamma_x + da_d\gamma_d + da_p\gamma_p,

where we’ve defined the \gamma_i as:

\gamma_h = \rho_h T_{\rm buff}
\gamma_s = \rho_s T_{\rm buff}
\gamma_m = \rho_m T_{\rm buff}
\gamma_x = \rho_x T_{\rm buff}
\gamma_p = \rho_p T_{\rm buff}
\gamma_d = \rho_d T_{\rm buff}

With these definitions, we can go back and finish defining dF_b, and subsequently finish the derivation (symbolically, at least – we’ll need the \rho_i before we can start plugging in numbers).

1.3.4 – Constructing the Block Factor derivative

Substituting our expressions for dG, dC, and dB_c into $dF_b$, we have:

dF_b = -(dh+de)\gamma_h\phi_{\rm G} - ds\gamma_s\phi_{\rm G} - dx\gamma_x\phi_{\rm G} -da_p\gamma_p\phi_{\rm G} - da_d\gamma_d\phi_{\rm G} - dm\left [ \gamma_m\phi_{\rm G} +\beta_{vm}\phi_{\rm BV} + \beta_{cm}\phi_{\rm BC} \right]

We’re going to do another round of encapsulation here to keep the expressions manageable.  Thus, we’ll write dF_b as

dF_b = -(dh+de)\Phi_{\rm bh} - ds\Phi_{\rm bs} - dx\Phi_{\rm bx}-da_p\Phi_{\rm bp}-da_d\Phi_{\rm bd} - dm\Phi_{\rm bm},

with the following definitions for the \Phi_{\rm bi}:

\Phi_{\rm bh} = \Phi_{\rm be} = \gamma_h\phi_{\rm G}
\Phi_{\rm bs} = \gamma_s\phi_{\rm G}
\Phi_{\rm bx} = \gamma_x\phi_{\rm G}
\Phi_{\rm bp} = \gamma_p\phi_{\rm G}
\Phi_{\rm bd} = \gamma_d\phi_{\rm G}
\Phi_{\rm bm} = \gamma_m\phi_{\rm G} + \beta_{vm}\phi_{\rm BV} + \beta_{cm}\phi_{\rm BC}

We can now take these definitions that describe dF_b and combine them with our earlier expressions for dF_{\rm ar} and dF_{\rm av} to come up with scaling factors for the different ratings that describe their relative value at reducing D.

1.4 – Normalized Scale Factors

Starting from dD/D_0S = dF_{\rm ar}F_{\rm av}F_b + dF_{\rm av}F_{\rm ar}F_b + dF_b F_{\rm ar}F_{\rm av}, and substituting the differentials we’ve worked out, we have:

\frac{dD}{D_0 S} = - \left (\frac{dAr}{f_{ar}}\right ) F_{\rm ar}F_{\rm av}F_b - \left ( \frac{dr_p}{f_p}+\frac{dr_d}{f_d}\right ) F_{\rm ar}\Phi_{\rm av} F_b \\- \left ( \frac{dr_h}{f_h}+\frac{dr_e}{f_e} \right ) F_{\rm ar}F_{\rm av}\Phi_{\rm bh}- \left (\frac{dr_s}{f_s}\right ) F_{\rm ar}F_{\rm av}\Phi_{\rm bs} \\- \left (\frac{dr_x}{f_x}\right ) F_{\rm ar}F_{\rm av}\Phi_{\rm bx} - \left (\frac{dr_p}{f_p}\right ) F_{\rm ar}F_{\rm av}\Phi_{\rm bp} \\- \left (\frac{dr_d}{f_d}\right ) F_{\rm ar}F_{\rm av}\Phi_{\rm bd} - \left (\frac{dr_m}{f_m} \right ) F_{\rm ar}F_{\rm av}\Phi_{\rm bm}

Combining terms, we can write this in the form:

\frac{dD}{D_0 S} = -dAr\Delta_{\rm ar} - dr_p\Delta_p -dr_d\Delta_d -(dr_h+dr_e)\Delta_h-dr_s\Delta_s-dr_x\Delta_x-dr_m\Delta_m,

where we’ve defined the normalized scale factors \Delta_i as:

\Delta_{\rm ar} = \frac{1}{f_{ar}}F_{\rm ar}F_{\rm av}F_b

\Delta_h = \Delta_e = \frac{1}{f_h}F_{\rm ar}F_{\rm av}\Phi_{\rm bh}

\Delta_s = \frac{1}{f_s}F_{\rm ar}F_{\rm av}\Phi_{\rm bs}

\Delta_x = \frac{1}{f_x}F_{\rm ar}F_{\rm av}\Phi_{\rm bx}

\Delta_m = \frac{1}{f_m}F_{\rm ar}F_{\rm av}\Phi_{\rm bm}

\Delta_p = \frac{1}{f_p}\left [ F_{\rm ar}\Phi_{\rm av} F_b + F_{\rm ar}F_{\rm av}\Phi_{\rm bp} \right ]

\Delta_d = \frac{1}{f_d}\left [ F_{\rm ar}\Phi_{\rm av} F_b + F_{\rm ar}F_{\rm av}\Phi_{\rm bd} \right ]

These are the normalized scale factors that we need to calculate to compare different stats.  At this point, it’s a simple matter of taking our initial conditions (i.e. the player’s avoidance, block, etc.), calculating all of the factors we’ve defined, and plugging them in.  Except, of course, that we need to develop expressions for the \rho_i first, which will take some work.  Next time, we’ll start figuring out how to express \rho_i by looking at the warrior rotation and the effects of parry haste.

<edit> Since publication, I’ve found that I made an error defining G.  This considerably simplifies part of the calculations.  I’ve updated to accurately reflect this new definition.  Apologies for the confusion, if it caused any.

Posted in Tanking, Theck's Pounding Headaches, Theorycrafting | Tagged , , , , , , , , , | 4 Comments

MoP Block Calculations – Part 4

In this final installment, we’re going to use a Monte Carlo simulation to see how accurately the analytical model portrays the results of a more realistic (i.e. discrete event-driven) combat scenario.  If the simulation agrees very well with the analytical results, we can feel confident that the derivation and conclusion we’ve drawn from that model are correct.  If they differ significantly, we might have to re-visit the model and the assumptions that went into it to figure out what we did wrong.

The Monte Carlo method is a very common technique in statistical simulation. The basic idea is that you have a system with a random element that makes it sensitive to fluctuations.  It could be a random process, such that the same input gives different outputs based on the state of the system (some quantum mechanical situations are like this), or it could be a deterministic process that has random inputs.  In either case, the Monte Carlo method involves generating a bunch of random numbers and looking at the output of the system.  By doing this enough times, you can determine if there are statistically significant trends in the output data.

In our case, we have a random process – combat.  We start from the same initial conditions (everything off cooldown, no buffs, etc.), but the process itself contains randomly-determined results based on combat table rolls.  Thus, our Monte Carlo simulation basically simulates combat – we track a bunch of cooldowns and buff durations, and make combat table rolls that affect the results.  By running the simulation long enough, we can get pretty accurate stochastic averages for the values we’re interested in.

Simulation Details

You can find the code for the Monte Carlo simulation here:
montecarlo.m
montecarlo2.m
montecarlo3.m

The basic idea is that it tracks the boss swing timer, the cooldowns of CS and J, the uptimes of both SotR buffs, Divine Purpose, and Grand Crusader, as well as the GCD.  It runs in timesteps of 10 milliseconds and keeps track of whether the boss attack is a hit, miss, or block (as well as the type of block – “gblock,” “sblock,” or regular block).  It makes combat rolls for avoidance and block to determine these outcomes, as well as random rolls for Divine Purpose and Grand Crusader.  The logic is such that it will prioritize CS>J>AS(only if GC available), and it won’t cast J if there’s <0.2 seconds left on the CS cooldown (I needed to do this because J scales slightly better with haste, resulting in it coming off-cooldown a little earlier and mucking up the rotation).

Finally, I’ve put some code in there to handle our assumptions from Part II.  It will cast SotR whenever we have >3 holy power or a Divine Purpose proc, but it won’t re-cast SotR until the guaranteed block is used.  This risks “wasting” some holy power generation, but in practice that happened 0 times in any of my simulations because we’re not pooling Holy Power to 5.  We can go back later and see how this changes if we do pool holy power simply by changing the conditionals on SotR, but by coding it the way I did, we should see the best possible agreement with the model.

Note that our other assumptions – namely that our S uptime is based on a single application estimate and that everything can be approximated as occuring at nicely-spaced intervals – are not being adjusted for.  So if there’s any significant inaccuracy introduced by those assumptions, they should show up in the data.

Simulation Results

Now let’s look at the results.  I’ve used the same input values as the analytical derivation.  First, let’s look at what we get with those inputs:

G= 0.417
S= 0.831
T_{\rm SotR} = 6.7
R_{\rm HPG} = 0.3845

Plot showing the distribution of hit sizes with the current model. 29.8% of attacks are avoided (0 damage), 29.3% are "gblocked" (0.25 damage), 18.7% are "sblocked" (0.5 damage), 3.8% are blocked regularly (0.7 damage), and 18.4% aren't blocked at all (1 damage). DTPS is out of a possible 50 (1 damage every 2 seconds, multiplied by 100).

If you’ll note, these values are extremely close to the ones in the analytical derivation.  With 10,000 minutes of combat, the Monte Carlo manages to match G and S to the analytical model to three decimal places.  R_{\rm HPG} differs slightly in the fourth decimal place, but T_{\rm SotR} is a little bit higher than the 6.4 predicted by the analytical model.  My guess is that this is the error due to discretization – namely that in the model, we don’t consider the coolown on SotR and use a stochastic time average for Divine Purpose.  In the numerical sim, SotR’s 1.5-second cooldown keeps it from bunching too tightly, and ends up pushing back SotR casts in cases with multiple DP procs.

The graph also shows some of the features that made mastery so weak in our analytical model.  Only 40.9% of attacks are even eligible for block as a result of the two-roll system, so that inherently almost halves block’s effectiveness.  We’re only blocking 22.5% of all attacks (18.7% “sblocks,” 3.8% “regular blocks”), giving 22.5%/40.9%=55.01% block, right in line with our input block chance.  59.1% of all attacks are avoided or end up being guaranteed blocks.

Now let’s look at what happens when we add 600 rating of various sources (I’ve skipped armor here, because I didn’t bother to include it in the Monte Carlo).  The simulation runs for 10,000 minutes of combat for each stat, which is still not enough to get wonderful accuracy for such a small change in rating, but enough to make sure we’re correct in at least the first significant digit.  Here is the reduction in DTPS (multiplied by 100 to make the numbers more reasonable) that occurs when adding 600 rating of each type:

Dodge:    0.9285
Hit:    0.6835
Exp:    0.6226
Haste:    0.4955
Mastery:    0.2948

Again, these seem to validate our values from the analytical calculation.  Dodge is well ahead of Hit/Exp.  Hit and Expertise should be identical, but even with 10k minutes per stat, we have some statistical fluctuations.  Haste lags behind hit/expertise, and mastery still brings up the rear.  So this, combined with our pin-point accuracy on G and S suggests that the analytical model is quite accurate indeed.

Finally, let’s take a look at variance.  A 10,000-minute fight isn’t realistic, because most fights last on the order of 5 minutes.  How do G, S, and T_{\rm SotR} vary from fight-to-fight for short encounters?

This plot shows the distribution of G and S values for 500 different 5-minute (combat time) simulations. G varies significantly less than S, as S is more dependent on the spacing and timing of SotR casts and boss attacks.

As we see here, G can range from about 0.32 to 0.55 depending on attempt.  But S varies quite a bit more than that – from as low as 50% to as high as 100%.  The standard deviations here are 3.69% (for G) and 9.63% (for S).  Plots for T_{\rm SotR} and R_{\rm HPG} are shown below, but they don’t really tell us much that we didn’t already know.  HPG varies based on hit/expertise and Grand Crusader procs, and SotR casts inherit that randomness and add a little Divine Purpose noise.

This histogram shows the variation of the SotR cast interval T_{SotR} under the same conditions as the previous plot.

This histogram shows the distribution of HPG rates for 500 different 5-minute (combat time) simulations.

Conclusions

This has been a short post – mostly because I’m rushing to finish this before heading out for the weekend.  But I think the point was to validate the analytical model with a numerical simulation, and I don’t think there’s any doubt that we’ve done that.  There are some interesting statistical questions we could answer with this code, but that will have to wait for another time.  In any event, we now have two conclusive pieces of evidence – an analytical model and a numerical simulation – that the current model of Paladin blocking makes mastery our dump stat.  Let’s hope that this is enough to convince Blizzard that change is needed.

Posted in Tanking, Theck's Pounding Headaches, Theorycrafting | Tagged , , , , , , , | 13 Comments

MoP Block Calculations – Part 3

Last time, we painfully ground through the necessary calculus to develop relationships between different stats – armor, avoidance rating, block rating, hit, expertise, and haste.  In this installment, we’re going to bring all of that together and start comparing those stats to see how they affect our damage intake.

First, we need to recap the equations we’ll need to proceed.  There are a lot of them, but I don’t want to make readers sift through the last few blog posts just to find the definition of a term, so we’ll summarize them here.  If you’ve been following along the whole time, feel free to skip to the next section.

The damage taken formula is:

D = D_0 F_{a} F_{\rm av} F_{b}

where D_0 is the pre-DR incoming damage, and the armor, avoidance, and block factors are defined as:

F_a = K/(Ar + K),
F_{\rm av} = (1 - A),
F_b = 1 - GB_v'' - (1-G) B_c S B_v' - (1-G)B_c(1-S)B_v.

K is the armor constant, Ar is the player’s armor, A is the player’s total avoidance, B_v'' is the player’s block value for the guaranteed block from SotR (75%), B_v' is the block value during the SotR buff (50%), B_v is the default block value (30%), and B_c is the player’s block chance as read off of the character sheet. G and S are variables that describe SotR mechanics as defined in the first installment:

G = R_{\rm SotR}/R_{\rm att}
S = R_{\rm SotR}(R_{\rm att}T_{\rm buff} - 1)/(R_{\rm att}-R_{\rm SotR})

T_{\rm buff} is the duration of the SotR increased block value buff.  The SotR cast rate R_{\rm SotR} and incoming blockable attack rate R_{\rm att} are:

R_{\rm SotR} = R_{\rm HPG}/3(1-\alpha_{\rm DP})
R_{\rm att} = (1-A) R_{\rm att}^{(0)} = F_{\rm av}/T^{(0)}_{\rm att}

where \alpha_{\rm DP} is the divine purpose proc chance (0.15) and T^{(0)}_{\rm att} is the boss’s swing timer.  R_{\rm HPG} is our Holy Power generation rate from all sources:

R_{\rm HPG} = [1 - (m-h) - (d+p-e)](1+\alpha_{\rm GC})(1+s)R_{\rm CS} \\ + [1 - (2m-h-e)](1+s)R_{\rm J}+ R_{\rm BL}.

Here, m, d, and p are miss, dodge, and parry caps, respectively, and h, e, and s are our hit, expertise, and haste percentages. R_{\rm CS} and R_{\rm J} are our CS and J cast rates, and \alpha_{\rm GC} is the Grand Crusader proc rate.  R_{\rm BL} is the HPG rate from Blessed Life, or from any other non-rotational sources.

Last week, we defined the differential of D, which represents the change in damage intake:

dD/D_0 = dF_a F_{\rm av} F_b + F_a dF_{\rm av} F_b + F_a F_{\rm av} dF_b

And calculated the differentials of each factor implicitly to find their dependence on ratings:

dF_a - dAr F_a / (Ar+K)
dF_{\rm av} =- dr_d (1-A_d/C_d)^2/(k_d f_d)
dF_b = -\left (\frac{dr_h}{f_h}+\frac{dr_e}{f_e}\right ) \beta_h - \frac{dr_s}{f_s}\beta_s - \frac{dr_d}{f_d}\beta_d -\frac{dr_m}{f_m}\beta_m

where A_d is our character’s post-DR dodge chance (ignoring base dodge), k_d and C_d are the avoidance diminishing returns formula coefficients, and f_i is the rating-to-percentage conversion factor for stat $i$ (i.e. f_s for haste, f_d for dodge, etc.).  The block factor was particularly ugly, so we defined the following additional quantities to make things a little easier to work with:

\beta_h = \frac{(1+s)[(1+\alpha_{\rm GC})R_{\rm CS}+R_{\rm J}]}{3(1-\alpha_{\rm DP})} \left [ \frac{F_{bG}}{R_{\rm att}}+ \frac{S F_{bS}}{(R_{\rm att}-R_{\rm SotR})R_{\rm SotR}} \right ]
\beta_s = \frac{(R_{\rm HPG}-R_{\rm BL})}{3(1-\alpha_{\rm DP})(1+s)} \left [ \frac{F_{bG}}{R_{\rm att}} + \frac{S F_{bS}}{(R_{\rm att}-R_{\rm SotR})R_{\rm SotR}} \right ]
\beta_d = \frac{(1-A_d/C_d)^2}{k_d T^{(0)}_{\rm att}} \left [ \frac{R_{\rm SotR} F_{bG}}{R_{\rm att}^2} + \frac{(1-R_{\rm SotR}^2T_{\rm buff}) F_{bS}}{(R_{\rm att}-R_{\rm SotR})^2} \right ]
\beta_m = \frac{(1-B^{\rm (DR)}_c/C_B)^2}{k_B}F_{bB}

F_{bG} = B_v'' - B_cSB_v' - B_c(1-S)B_v
F_{bB} = (1-G)[S B_v' - (1-S)B_v]
F_{bS} = (1-G)B_c(B_v'-B_v)

where the only new variable we’ve introduced is B^{\rm (DR)}_c, which is the portion of our post-DR block chance that was subject to DR (in other words, B^{\rm (DR)}_c = B_c - B^{(0)}_c, where B^{(0)}_c is our base block chance at 0 mastery.

Developing Relationships  Analytically

The next step is to start generating relationships between the different stats.  If we substitute in our new expressions for dF_a, dF_{\rm av}, and dF_b into the expression for dD/D_0 and group terms according to rating type, we get the following expression:

\frac{dD}{D_0} = dAr\frac{F_a F_{\rm av} F_b}{(Ar+K)} - \frac{dr_d}{f_d} F_a \left [\frac{(1-A_d/C_d)^2}{k_d} F_b + \beta_dF_{\rm av} \right ] \\ - \left (\frac{dr_h}{f_h}+\frac{dr_e}{f_e}\right )\beta_h F_a F_{\rm av} - \frac{dr_s}{f_s}\beta_s F_a F_{\rm av} - \frac{dr_m}{f_m}\beta_m F_a F_{\rm av}

In the past, we’ve then set dD/D_0 equal to zero and explicitly worked out formulas that equate one stat to another.  For example, if we wanted to see how much haste rating gives as much damage reduction as a given amount of mastery rating, we’d set all of the other dr_i to zero and solve:

\frac{dr_s}{f_s}\beta_s F_a F_{\rm av} = \frac{dr_m}{f_m}\beta_m F_a F_{\rm av}

which gives the simple solution

dr_s = dr_m \left (\frac{f_s \beta_m}{f_m \beta_s}\right )

I chose that particular combination because a lot of factors cancel and make it easy on us, but you could do the same for any of the different combinations and get a similar (albeit uglier) expression.

That was fine in Cataclysm, because we were only looking at three major stats: avoidance, mastery, and armor.  With N stats, there’s \text{N choose k} =\frac{N!}{k!(N-k)!} different combinations, which for N=3 and k=2 is just three: avoidance vs. mastery, avoidance vs. armor, and mastery vs. armor.  However, now we’re looking at six stats: armor, avoidance, hit, expertise, haste, and mastery.  For N=6, there are 15 different combinations.  At that point it becomes less efficient to derive explicit relationships between each pair of stats, and more efficient to find some way to normalize the contribution of each stat so we can look at their relative weights numerically.

In other words, it will make a lot more sense to choose realistic character stats and calculate the factors \beta_s F_a F_{\rm av}/f_s and \beta_m F_a F_{\rm av}/f_m, and see which one is bigger.  Not only will those tell us how haste and mastery compare, but if we want to compare either to a different stat (say, avoidance) we only need to calculate the equivalent factor for that stat, and we instantly know how it stacks up to both.

So we define the normalized damage reduction factors \gamma_i as follows:

\gamma_{ar} = F_a F_{\rm av} F_b / (Ar+K)
\gamma_d = F_a\left [ \frac{(1-A_d/C_d)^2}{k_d}F_b + F_{\rm av} \beta_d \right ]/f_d
\gamma_h = F_a F_{\rm av}\beta_h / f_h
\gamma_e = F_a F_{\rm av} \beta_h / f_e
\gamma_s = F_a F_{\rm av} \beta_s / f_s
\gamma_m = F_a F_{\rm av} \beta_m /f_m

What do these factors \gamma_i represent?  Well, each one is the net damage reduction dD/D_0 that you gain from one point of rating dr_i.  So \gamma_m tells you the damage reduction of one point of mastery rating, \gamma_s one point of haste rating, and so on.  The nice part about this is we can simply write a program that does the annoying work of evaluating the messy expressions in the first section of this post.  We feed the program a few easily-understood input parameters like armor, avoidance, and so on, and it spits out the six \gamma_i values we’re interested in.   So let’s do that.

Developing Relationships Numerically – Inputs

First, we need to define our inputs.  Since we don’t have stats at level 90 yet, let’s make some estimates based on stats at level 85 fighting a level 88 boss.  We’ll assume that the player has 30% avoidance 15% of which is from dodge.  We’ll also assume they have 55% block chance, 40k armor, 2% hit, 2% expertise, and 10% haste.  That gives us the following variables:
A=0.3
A_d = 0.1
B_c = 0.55
B^{\rm (DR)}_c = 0.5
Ar=40000
h=0.02
e=0.02
s=0.1

Based on the level difference, we have the following values for the hit/expertise caps and armor coefficient:
K=32573
m=0.075
d=0.075
p=0.075

We also have the block value tiers we defined earlier:
B_v''=0.75
B_v' = 0.5
B_v = 0.3

And we know the rating->percentage conversion factors for each stat (noting that the percentages are in decimal form):
f_m=179.28004455/0.0225=7968
f_d=176.71890258/0.01 = 17672
f_s=128.05715942/0.01=12806
f_e=120.10880279/0.01=12011
f_h=120.10880279/0.01=12011

The diminishing returns coefficients for avoidance and blocking are also necessary:
k_d = k_B = k = 0.9560
C_d = 0.65631440 (~65%)
C_B = 1.351 (~135%, courtesy of Pagezero at Tankspot)

And we have some assorted constants to define:
\alpha_{\rm DP} = 0.15
\alpha_{GC} = 0.2
T_{\rm buff} = 6

Finally, we need to assume a boss swing timer.  Let’s choose 2 seconds:
T_{\rm att}^{(0)} = 2

Developing Relationships Numerically – Outputs

I’ve written a MATLAB script file that defines all of these values and then calculates the expressions we’ve worked out.  First, we need to pick a rotation, because that determines R_{\rm CS} and R_{\rm J}.  For this example, we’ll choose “SotR>CS>J>AS>Cons>HW,” which tends to maximize holy power returns (thus giving the best results possible for hit/exp/haste).  In this model, you cast CS on cooldown, so

R_{\rm CS}=1/4.5=0.2222

Judgment gets pushed back every so often, however, which gives it an effective cooldown of 6.75 seconds (which you can work out by hand as well).  Thus,

R_{\rm J} = 1/6.75=0.1481.

To see how accurate our HPG modeling is, let’s compare it to the matlabadin FSM code, which gives very accurate rotation information.  Plugging those values into our formula with 10% haste gives us a HPG rate of

R_{\rm HPG} = 0.3841,

in perfect agreement with the FSM code.  Thus, we can feel certain that our formula for R_{\rm HPG} is correct.  So far so good. Based on the HPG rate, our model says we should have a SotR cast rate of

R_{\rm SotR} = 0.1506,

or one SotR every 6.64 seconds, again in agreement with the FSM code.

Our formulas for G and S give us the following uptimes:

G=0.4304
S=0.8311

That’s pretty incredible – 43% of the incoming attacks that we don’t avoid are guaranteed to be blocked by SotR at 75%, and we’ll have the +20% block buff up for 83% of the ones that aren’t avoided or guaranteed.

But I know what you’re really interested in is the normalized damage reduction factors \gamma_i.  So let’s jump right to them.  Since the values come out to be pretty small, let’s define

\Gamma_i =100000 \gamma_i

which is the net benefit of 100000 rating instead of 1 rating.  Note that we’re not really trying to model stacking 10^5 of any of these ratings – we’re just multiplying the results by 10^5 so they’re easier to compare (otherwise we’d need to use scientific notation).  The results are:

\Gamma_{ar} = 0.2299
\Gamma_d = 1.8572
\Gamma_h = 1.4717
\Gamma_e = 1.4717
\Gamma_s = 1.0564
\Gamma_m = 0.4346

Wait, hold on, is that right?  These results are saying that mastery is in dead last place, behind hit, expertise, haste, and even armor (remember that we get 4 armor per itemization point, so it’s actually got a per-itemization value of 4\Gamma_{ar}=0.9196.

Yes, sadly, it’s correct.  I’ve double-checked it a few times now, and I think I’ve caught and fixed all of the errors in the derivation.  Unless something about our mastery changes significantly between now and release, mastery is going to be our dump stat in MoP.

O Mastery, Why Hast Thou Forsaken Us?

To understand why mastery performs as badly as it does, we need to drill down a little farther.  Let’s look at the values that go into \gamma_i:

F_a = 0.4488
F_{\rm av} = 0.7
F_b = 0.5312
\beta_h = 0.5626
\beta_d = 0.7398
\beta_m = 0.1102

So there’s part of the problem: \beta_m is smaller than the others by a factor of 4 to 7, depending on what we’re comparing it to.  Note that all of the \gamma_i have an armor factor (i.e. F_a), an avoidance factor (i.e. F_{\rm av}), and a block factor (either F_b or one of the \beta_i).  So the fact that \beta_m is at least four times lower explains why block performs so poorly.

But why is \beta_m so low?  And not just in the mathematical sense – what mechanically is going on that causes \beta_m to lag the others so much?  To see that, we need to drill down further.  Let’s look at the “block prefactors,” or the factors that go into the expression for dF_b:

F_{bG} = 0.4936
F_{bS} = 0.0627
F_{bB} = 0.2656

What do these tell us?  F_{bG} is the multiplier that relates a small change in G to a small change in F_bF_{bS} serves the same purpose for S, and F_{bB} for B_c (and thus mastery).  From this vantage point, it looks like block isn’t faring so badly.  We can see that SotR’s guaranteed blocks have a fairly large effect, but good old block seems to be OK.  Of course, the devil is in the details – it’s not the F_{bi} that are the problem; the problem is that a fixed amount of rating gets you a much bigger change in G than it does in B_c.

To see why, let’s look back at our equations for \beta_i, which I’ll repeat here:

\beta_h = \frac{(1+s)[(1+\alpha_{\rm GC})R_{\rm CS}+R_{\rm J}]}{3(1-\alpha_{\rm DP})} \left [ \frac{F_{bG}}{R_{\rm att}}+ \frac{S F_{bS}}{(R_{\rm att}-R_{\rm SotR})R_{\rm SotR}} \right ]
\beta_s = \frac{(R_{\rm HPG}-R_{\rm BL})}{3(1-\alpha_{\rm DP})(1+s)} \left [ \frac{F_{bG}}{R_{\rm att}} + \frac{S F_{bS}}{(R_{\rm att}-R_{\rm SotR})R_{\rm SotR}} \right ]
\beta_d = \frac{(1-A_d/C_d)^2}{k_d T^{(0)}_{\rm att}} \left [ \frac{R_{\rm SotR} F_{bG}}{R_{\rm att}^2} + \frac{(1-R_{\rm SotR}^2T_{\rm buff}) F_{bS}}{(R_{\rm att}-R_{\rm SotR})^2} \right ]
\beta_m = \frac{(1-B^{\rm (DR)}_c/C_B)^2}{k_B}F_{bB}

Now, just look at the parts in brackets, because it turns out the factors out front are almost identical for dodge and mastery, and about half that size for haste and hit.  For hit/exp (\beta_h), haste (\beta_s), and dodge (\beta_d), they contain a term with F_{bG} (which is large) over R_{\rm att} (which is small), which subsequently makes the first term very large.  This first term is characteristic of the effects of that stat on the SotR portion of block mitigation – that factor of R_{\rm att} comes from G (or dG, depending on how you look at it).

These \beta factors also have a second term that represents the effect on the 6-second block value buff, with F_{bS}.  Even though F_{bs} is small, it turns out the second term is actually larger than the first due to the other factors.  For hit and haste, the first term evaluates to around 1.4, the second is about 1.73.  For dodge, the first term is around 0.6 and the second is 1.36.

So hit, expertise, dodge, and even haste are all strong stats because they feed into our active mitigation, both by increasing the amount of guaranteed blocks we get and increasing the uptime of the SotR block value buff.  Hit, expertise, and haste all do this in the obvious way – they increase Holy Power generation, which lets us use our active mitigation tool more often.  Dodge does it a little more subtly, by reducing R_{\rm att} – essentially, the more you dodge, the fewer incoming attacks you have that are eligible for block, increasing the percentage of them that become guaranteed blocks.

But look at \beta_m.  It’s just got a lonely factor of F_{bB}, which is 2x-4x smaller than any of the individual factors inside the brackets in the other \beta_i.  It’s propped up a little by having a better rating->percentage factor f_m, but not nearly enough to let it catch up to the other stats.  And the reason is because it has only a very weak interaction with our active mitigation.  In fact, our active mitigation actively undermines mastery by making it less useful.  The more guaranteed blocks we get, the less important mastery becomes.  If you bump up the hit and expertise values used in the calculation, you increase the importance of G and the value of \beta_m goes down by a few percent.

That said, we can’t blame it all on active mitigation.  Certainly diminishing returns plays a part, as does the two-roll system itself.  The diminishing returns on block are about twice as bad as the diminishing returns on avoidance; (1-B_c/C_B)^2/k_b is almost half as large as (1-A_d/C_d)^2/k_d, because we’re much higher on the DR curve for block than we are for avoidance (partly because avoidance is split between two DR mechanisms – dodge and parry).  And the two-roll system reduces the effectiveness of block chance inherently, like we’ve discussed before.

But the dominant factor in mastery’s demise as a damage mitigation stat is our active mitigation.  It introduces an interesting feedback mechanism between avoidance and our active mitigation that allows avoidance to double-dip (hence the second term in \gamma_d – note that each term of \gamma_d is larger than \gamma_h, \gamma_s, or \gamma_m).  The more we avoid, the stronger SotR’s guarateed blocks get, so adding dodge rating gives us less damage due to avoided attacks and less damage due to a higher percentage of 75% blocks.

Where do we go from here?

I’ve already been advocating a change in our mastery, partly because I was worried it would be too weak (little did I know – even I didn’t expect it to be this bad), and partly because block chance isn’t really all that exciting.  It wasn’t exciting in Cataclysm either, but the presence of a block cap made for interesting gearing choices, much like the hit or expertise cap does for a DPS.  Mastery in MoP fails to even have that level of “interesting.”  It’s just another passive total damage reduction stat, like dodge or parry, but less efficient.

The solutions I’ve been advocating on maintankadin have all included mechanisms that couple mastery with our active mitigation.  At the time, I didn’t realize how critical that coupling would be, but the number-crunching we’ve done in this post shows that it’s even more important than I could have guessed.  It’s essential, in fact, because our active mitigation accounts for a much larger part of our survivability than our “last resort” block chance does.  In a future blog post, I’ll look at some of the possible ways we could introduce this coupling and make mastery an interesting and useful stat.

In the next and final part of this series of blog posts, we’re going to build a numerical Monte-Carlo simulation to validate our analytical model and figure out how reasonable the various approximations we’ve made along the way really are.  That may not happen until Friday, though, both because I’ve yet to write the simulation and because I’ve been monopolizing the blog for the better part of a week.

Posted in Tanking, Theck's Pounding Headaches, Theorycrafting | Tagged , , , , , , , | 29 Comments