TC401: Avoidance Diminishing Returns in WoD

Avoidance and diminishing returns are topics we’ve spent a fair amount of time discussing in the past. In 2012, I wrote a series of three blog posts discussing avoidance diminishing returns in Mists of Pandaria, chronicling the background of the formulas the community uses, numerically fitting data sets to determine the diminishing returns coefficients, and eventually discussing what those equations mean for gearing. We used similar fitting techniques to find values for druids and monks.

Later that year, we used a more precise data set collected with the Statslog addon to fine-tune the values for paladins and uncover some wonky rounding in the block calculation. And in 2013, we performed an exhaustive analysis including lots of pretty surface plots to get accurate values for all of the tank specs.

It seemed at first like Warlords of Draenor was going to be an easy expansion in that regard. Celestalon has outright given us the strength-to-parry and agility-to-dodge coefficients and the diminishing returns coefficients for each class. While the provided coefficients are in the format of their internal diminishing returns formula, which is a little different from the version the community usually uses, translating between the two is not difficult.

And yet….

First, some quick tests uncovered a bug in the calculation of base parry, which Blizzard subsequently fixed. And in the past two weeks, I’ve discovered another oddity, which I’ve detailed below.

As usual with TC101 articles, I’ll give the results up front for reference (and those that don’t want to scroll to the bottom to get them), and then start talking about how we performed the tests.

What’s Changed

The diminishing returns (DR) formulas for WoD have changed very little from the Mists versions. In fact, the most significant changes are to the contributions that are not affected by DR.

In Mists, base strength and agility add an amount of parry or dodge, respectively, that is unaffected by DR. In Warlords, that contribution has changed in the following ways:

  • Strength no longer grants parry for druids and monks, and agility no longer grants dodge for paladins, warriors, and death knights.
  • They have subtracted out the “non-DR” parry and dodge contributions from base strength and agility, such that base parry and dodge should be exactly 3.00% (the default for all players and NPCs) plus any race- or spec-based passives. For example, a night elf would have 5.00% base dodge thanks to Quickness, as would a paladin thanks to Sanctuary. However… there are a few quirks.
  • They have subtracted out the contribution due to the class portion of base strength, but not the portion due to the racial strength modifier. That racial strength modifier’s contribution is also not affected by DR. So a gnome warrior (-5 racial strength modifier) would have 2.97% base parry (-0.0283% due to the -5 strength racial modifier). The same is true for the racial agility modifier’s contribution to dodge.
  • Finally, there’s probably a small error in the strength subtraction, because all strength tanks (regardless of race) are getting a “phantom” 0.0004178% parry, or about 0.0739 strength worth of parry, that isn’t affected by diminishing returns.

The last quirk is the interesting one, because it’s a bug. Not a game-breaking bug, mind you; we’re talking about about 0.0004% parry. For most people, they’ll never even be perceptible.

Nonetheless, it’s worth noting because it’s just barely large enough that it could cause a slight discrepancy between calculated and character sheet parry values if not accounted for. For example, if your theorycrafted parry is 21.0199%, but the actual value is 21.0203%, your character sheet will read 21.02% instead of 21.01% (as we mentioned last post, character sheet values are almost always floored, not rounded).

I’ve informed Blizzard of the bug, but I’ve been told that given their programmer’s workload and the tiny magnitude of the effect, we should not expect to see it fixed.

Warlords of Draenor Diminishing Returns Formulas

Since Blizzard has kindly provided us with the diminishing returns coefficients, I’ve chosen to use their formula rather than the community’s traditional one. In the next section I’ll show how to convert the coefficients provided by Blizzard into the conventional ones you might be familiar with.

In the following equations, $\text{ParryFactor}$, $\text{DodgeFactor}$, $\text{BlockFactor}$, $\text{VerticalStretch}$, and $\text{HorizontalShift}$ are the class-specific Blizzard DR coefficients given in the table at the end of this section. $Q_S$ and $Q_A$ are the level-dependent and class-specific strength-to-parry and agility-to-dodge conversion factors. $\text{classBaseStr}$ and $\text{classBaseAgi}$ are class-dependent base stat values, and $\text{raceStrMod}$ and $\text{raceAgiMod}$ are the racial stat modifiers. $\text{Strength}$, $\text{Agility}$, $\text{Mastery}$, $\text{parryRating}$, and $\text{dodgeRating}$ are your character sheet values for each of these stats.

Note that I’m giving these equations in percent form. In other words, a $\text{bonusParry}$ of 15.3% is 15.3 in the equation. If using decimal form (e.g. $\text{bonusParry}=0.153$) there is an extra factor of 100 in the term with $\text{VerticalStretch}$. The easiest way to accommodate this is to just multiply $\text{VerticalStretch}$ by 100.

 

The diminishing returns equations for parry are:

$\text{baseParry} = 3.00 + \left ( \text{raceStrMod}+ 0.0739 \right )\times Q_S$

$\text{bonusParry} = \left ( \text{Strength} – \text{classBaseStr} – \text{raceStrMod} \right ) \times Q_S + \text{parryRating} / 162$

$\begin{align} \text{totalParry} & = \text{baseParry}  \\ &+ \text{bonusParry}/\left ( \text{bonusParry} \times \text{ParryFactor}\times \text{VerticalStretch} + \text{HorizontalShift} \right ) \end{align}$

The diminishing returns equations for dodge are:

$\text{baseDodge} = 3.00 + \text{raceAgiMod}\times Q_A + \text{racial/spec passives}.$

$\text{bonusDodge} = \left ( \text{Agility} – \text{classBaseAgi} – \text{raceAgiMod} \right ) \times Q_A + \text{dodgeRating} / 162$

$\begin{align} \text{totalDodge} &= \text{baseDodge} \\ &+ \text{bonusDodge}/\left ( \text{bonusDodge}\times \text{DodgeFactor}\times \text{VerticalStretch}+\text{HorizontalShift} \right ) \end{align}$

The diminishing returns equations for block are:

$\text{baseBlock} = 3.00 + \text{spec passives}.$

$\text{bonusBlock} = \text{round}[~\text{Mastery}\times Q_M \times 128 ~]/128$

$\begin{align} \text{totalBlock} &= \text{baseBlock} \\ &+ \text{bonusBlock}/\left ( \text{bonusBlock}\times \text{BlockFactor}\times \text{VerticalStretch}+\text{HorizontalShift} \right ) \end{align}$

 

The tables below summarize the constants in these formulas for different tank specs.

Constants By Class
Constant Death Knight Druid Monk Paladin Warrior
ParryFactor 0.634 1 1.659 0.634 0.634
DodgeFactor 1.659 1 0.3 2.259 1.659
BlockFactor 1 1 1 1 1
VerticalStretch 0.00665 0.00665 0.00665 0.00665 0.00665
HorizontalShift 0.956 1.222 1.422 0.886 0.956
classBaseStr 1455 626 626 1455 1455
classBaseAgi 1071 1284 1284 455 889

$Q_S = \begin{cases} 1/176.3760684 & \text{ Death Knight, Paladin, Warrior} \\ 0 & \text{ otherwise}\end{cases}$

$Q_A = \begin{cases} 1/176.3760684 & \text{Druid, Monk} \\ 0 & \text{otherwise}\end{cases}$

$Q_M = \begin{cases} 1 & \text{Paladin} \\ 0.5/2.2 & \text{Warrior} \\ 0 & \text{otherwise}\end{cases}$

Race Stat Modifiers
Race raceStrMod raceAgiMod
Human 0 0
Dwarf 5 -4
Night Elf -4 4
Orc 3 -3
Tauren 5 -4
Undead -1 -2
Gnome -5 2
Troll 1 2
Blood Elf -3 2
Draenei 1 -3
Goblin -3 2
Worgen 3 2
Pandaren 0 -2

 

Background

Blizzard’s formula for diminishing returns looks a little different than the one the community generally uses. If you read community guides (including my old posts on the subject), we tend to use a two-parameter formula:

$\text{totalParry} = \text{baseParry} +1 / \left ( \frac{1}{C_p} + \frac{k}{\text{bonusParry}} \right )$

where $C_p$ is the parry cap (or dodge cap $C_d$ or block cap $C_b$ for the other formulas) and $k$ is a class-dependent constant. It’s not hard to show that this formula is identical in form to the Blizzard ones: multiply both numerator and denominator of the second term on the right-hand side by $\text{bonusParry}$ to get:

$\text{totalParry} =\text{baseParry} + \text{bonusParry} / \left ( \text{bonusParry} /C_p+k \right ) $

In this form it’s clear that $k = \text{HorizontalShift}$, and that there’s a very simple relationship between $C_p$ and the remaining two constants:

$C_p = 1/ \left (\text{ParryFactor} \times \text{VerticalShift} \right )$

and similarly for $C_d$ and $C_b$, substituting the appropriate “Factor” constant. There’s also a fourth $C_m$ and $\text{MissFactor}$ that I’ve omitted since we don’t have ways to change our miss chance.

Despite the fact that Blizzard’s formula has three parameters, each individual formula only really uses two. $\text{ParryFactor}$ and $\text{VerticalShift}$ are redundant parameters, since they only ever show up multiplied together. In theory, they could eliminate one of them entirely in favor of the other; e.g. eliminate $\text{VerticalShift}$ and merge that into the $\text{Factor}$ variable. Then every equation would have a different $\text{Factor}$ variable (just as ours had a different cap constant $C_p$, $C_d$, etc.), but the same class-based $\text{HorizontalShift}$.

We can also plug in the values Celestalon gave us to see how accurate our previous fitting session was:

Calcualted Cap Values
Class $k$ $C_d$ $C_p$ $C_b$
Death Knight 0.956 90.6425 237.1860 150.3759
Druid 1.222 150.3759 150.3759 150.3759
Monk 1.422 501.2531 90.6425 150.3759
Paladin 0.886 66.5675 237.1860 150.3759
Warrior 0.956 90.6425 237.1860 150.3759
Empirically Determined Cap Values (from Aug 2013)
Class $k$ $C_d$ $C_p$ $C_b$
Death Knight $0.956$ $90.6425(74) \pm 0.000010$ $237.186(14) \pm 0.00015$ -
Druid $1.222$ $150.3759(38) \pm 0.000041$ - -
Monk $1.422$ $501.253(48) \pm 0.00032$ $90.642(44) \pm 0.00014$ -
Paladin $0.886$ $66.56744(62) \pm 0.0000060$ $237.1860(40)\pm 0.000055$ $150.3759(469)\pm 0.0000094$
Warrior $0.956$ $90.64254(65) \pm 0.0000052$ $237.1860(91) \pm 0.000057$ $150.375(68) \pm 0.00015$

Comparing the tables, it’s clear that our fitting system nailed all of these. Most were accurate out to 4 decimal places, and the remaining ones out to three decimal places. Almost all of them were accurate to the reported precision in the table. That gives us a lot of confidence in our fitting algorithms and techniques, which is why we’re able to believe that we can detect a systematic error of 0.0004% parry. Now, let’s see how we figured that out.

Testing The Parry Equation

Based on several discussions with Celestalon, we know that the original intent was something like this:

$\text{baseParry} = 3.00\%$ (exact)
$\text{bonusParry} = (\text{Strength} – \text{classBaseStr} – \text{raceStrMod})\times Q_S + \text{parryRating}/162$
$\text{totalParry} = \text{baseParry}+\text{bonusParry} / \left ( \text{bonusParry}\times\text{ParryFactor}\times\text{VerticalStretch}+\text{HorizontalShift}\right ) $

In other words, the parry from the class contribution to base strength (which is not affected by DR) is completely negated, such that base parry is exactly 3% for each class. However, we also discovered that this wasn’t the case, and were subsequently told that the racial strength modifier is not negated, and that this racial modifier is affected by DR. The “edit” and follow-up post he’s provided since are actually in error, as we’ll see shortly.

So, taking his word from before the edits, what we should be seeing is:

$\text{bonusParry} = (\text{Strength} – \text{classBaseStr} )\times Q_S + \text{parryRating}/162$

Unfortunately, that’s not what the game is doing. For example, let’s take a naked Gnome Warrior. Here are the relevant values:

$\text{classBaseStr} = 1455$
$\text{ raceStrMod} = -5$
$\text{ Strength} = 1450$
$\text{ parryRating} = 0$
$\text{ ParryFactor} = 0.634$
$\text{ VerticalStretch} = 0.00665$
$\text{ HorizontalShift} = 0.956$

If you plug those values into the formulas above, you get (keeping only 10 decimal places):

2.9703430316%

If you use GetParryRating() in-game though, you get (again, to 10 decimal places):

2.9720702171%

A difference of ~0.002%. Not that far off… but this should be nearly exact, we’re using the same formula the game does. At first, I thought that perhaps this was because Celestalon rounded the $\text{ParryFactor}$, $\text{VerticalStretch}$, and $\text{HorizontalStretch}$ values he gave us in the Theorycrafting thread. However, last week I took a few quick data sets to test this hypothesis, and convinced myself that isn’t the issue. In fact, given the accuracy and agreement we see with the empirically-obtained values from August 2013, the values he provided in the Theorycrafting thread may well be exact, and not rounded at all!

To illustrate why, here’s the data for the naked gnome warrior, all retrieved through the the Statslog addon with minor tweaks to get it working again on beta. Statslog uses the World of Warcraft API functions UnitStat("player",1), GetCombatRatingBonus(CR_PARRY), and GetParryChance() to retrieve this information, giving us very high precision results. Again, I’ve only kept 10 decimal places for the table since we’d be ecstatic to fit to even that precision. To collect this data, I just added or removed gear to change my total stats.

Gnome Warrior Data
$\text{Strength}-\text{classBaseStr}$ $\text{parryRating}/162$ $\text{totalParry}$
-5 0.0000000000 2.9720702171
-5 1.2962962389 4.3203210831
205  1.2962962389 5.5452437401
426 2.2037036419 7.7356786728
550 2.7530863285 8.9868812561
674 2.7530863285 9.6833515167
798 3.3024692535 10.9137287140
964 3.9814815521 12.4860315323
1130 3.9814815521 13.3895244598
1351 4.8888888359 15.4365730286
1517 5.6172838211 16.9933910370
1738 6.5246915817 18.9761753082
1904 6.5246915817 19.8289909363
2028 7.0000000000 20.8874912262
2249 7.0000000000 22.0019493103
2121 6.4753084183 20.8898067474
2245 6.9876542091 21.9709510803

I put that data in MATLAB and used it to fit values for $\text{VerticalStretch}$ and $\text{HorizontalShift}$ with very strict tolerances on accuracy but very loose tolerances on $\text{VerticalStretch}$ ($\pm 0.0001$) and $\text{HorizontalShift}$ ($\pm 0.001$). This means that MATLAB’s fitting algorithms will attempt to determine the values very accurately, but will also allow them to vary anywhere within the tolerance on each constant. Note that I’ve chosen those tolerances to provide complete flexibility in rounding those values and then some – in other words, any value of those constants which rounds to the ones given by Celestalon is fair game in this fit, and even some values outside of that range (e.g. 0.00666 for $\text{VerticalStretch}$).

The formula, fit details, and plot are given below. In this fit, $x$ is our definition of $\text{bonusParry}$ above, which includes the strength contribution (in this case negative) of the racial strength modifier.

General model:
 fitresult(x) = 3+0*0.0056697+x/(x*0.634*vs+hs)
 Coefficients (with 95% confidence bounds):
 vs =    0.006668  (0.006661, 0.006675)
 hs =      0.9559  (0.9559, 0.956)
Gnome Warrior Parry Fit.

Gnome warrior parry fit.

That looks pretty good – hs ($\text{HorizontalShift}$) is about right, though the parameter vs ($\text{VerticalStretch}$) has to be higher than is reasonable to be rounded to a value of 0.00665. However, the proof that something’s wrong is in the plot of the residuals – the difference between the fitted curve and the actual data. Here are those differences in tabular form:

Gnome Warrior parry fit residuals
Fit Data Residual
2.9703 2.9721 -0.0017
4.3190 4.3203 -0.0013
5.5442 5.5452 -0.0010
7.7352 7.7357 -0.0005
8.9866 8.9869 -0.0003
9.6832 9.6834 -0.0002
10.9137 10.9137 -0.0000
12.4862 12.4860 0.0001
13.3897 13.3895 0.0002
15.4369 15.4366 0.0003
16.9937 16.9934 0.0003
18.9763 18.9762 0.0002
19.8291 19.8290 0.0001
20.8875 20.8875 -0.0000
22.0018 22.0019 -0.0002
20.8898 20.8898 -0.0000
21.9708 21.9710 -0.0002

Again, it’s not off by much… but ~0.002% is enough to cause rounding errors that lead the character sheet value to differ by 0.01% compared to calculations (i.e. SimC’s output). So it’s worrisome. Furthermore, these residuals tell us something else about the fit. A graphical representation is a little more revealing, in this case:

Gnome Warrior parry fit residuals.

Gnome warrior parry fit residuals.

It may not be clear to a layperson, but someone who’s been fitting data for years will instantly recognize the meaning of that plot. The fact that there’s curvature indicates some sort of systematic error. If we have the correct formula, the residual plot shouldn’t have curvature – it should look almost like a random scatter plot. For example, like this plot from 2012:

Dodge residuals from August 2012 post.

If you read through that post, you’ll notice that I used the same technique of looking for details and patterns in a residuals plot to identify a systematic error in our assumed value of the agility-to-dodge conversion factor, as well as to figure out that the game performs a binary rounding operation on $\text{bonusBlock}$.

The point this time around is that with such loose tolerances on the two DR coefficients, the fit should have no trouble producing a very clean residual plot. That tells us that either

  1. Our DR coefficients lie outside the region we’ve provided, which means Celestalon doesn’t know how to round (unlikely), or
  2. something is wrong with the formula.

My first thought was to try the obvious thing: assume Celestalon lied! Ok, not really, but I thought maybe he was mistaken about the racial stat modifier being affected by DR. So I moved it outside the DR calculation in my code. In other words, I now let

$\text{baseParry} = 3 + \text{raceStrMod}\times Q_S$
$x = \text{bonusParry} = ( \text{Strength} – \text{classBaseStr} – \text{raceStrMod} )\times Q_S + \text{parryRating}/162$

Doing that gives us:

Gnome Warrior parry residuals, mark II.

Gnome warrior parry residuals, mark II.

General model:
 fitresult(x) = 3+-5*0.0056697+x/(x*0.634*vs+hs)
 Coefficients (with 95% confidence bounds):
 vs =    0.006654  (0.006652, 0.006656)
 hs =      0.9559  (0.9559, 0.9559)

You might note that the residuals have gone down considerably here – the largest is now $-4\times 10^{-4}$ rather than $-1.7\times 10^{-3}$, which suggests that the hypothesis is likely correct. The parry from racial strength modifiers is probably not being affected by diminishing returns. But what bothered me is that there’s still curvature on the residuals plot – that means we still don’t have the formula right.

Just to make sure I wasn’t loony, I tried this with dwarf and draenei warriors too. Both races exhibited similar curvature in the residuals plot, so it’s not just gnomes acting oddly. Next, as a sanity check, I tried a human warrior. Since a human warrior has a racial modifier of zero, the curvature should disappear if that’s the cause of the problem.  I also added $Q_S$ as another fit parameter just in case that value was incorrect, unlikely as that was since it was also given to us by Celestalon. Adding that parameter turns our curve fit into a surface fit, so the residual plot will be 3-dimensional. Here’s what it looked like:

Human warrior parry fit residuals.

Human warrior parry fit residuals.

General model:
 fitresult(x,y) = 3+0/q+(x/q+y)/((x/q+y)*vs*0.634+hs)
 Coefficients (with 95% confidence bounds):
 vs =    0.006655  (0.006652, 0.006657)
 hs =      0.9558  (0.9556, 0.9561)
 q =       176.4  (176.3, 176.5)

The curvature there should be clear despite the viewpoint of the plot. So this curious result tells us that this isn’t a problem limited to racial strength modifiers. There’s still something else missing. And it didn’t take much guessing to stumble across the solution.

Let’s assume there’s some extra amount of strength that’s giving parry, and that it’s not subject to diminishing returns. In other words, we let

$\text{baseParry} = 3.00 + (\text{raceStrMod} + R)\times Q_S$

Let’s also assume that $Q_s$ is accurate as given, so we can go back to curve fits rather than surface fits. Then, the fit and residuals for a human warrior look like this:

Human warrior parry residuals, mark II.

Human warrior parry residuals, mark II.

General model:
 fitresult(x) = 3+(0+r)*0.0056697+x/(x*0.634*vs+hs)
 Coefficients (with 95% confidence bounds):
 vs =     0.00665  (0.00665, 0.00665)
 hs =       0.956  (0.956, 0.956)
 r =     0.07385  (0.07367, 0.07404)

This is what a residual plot should look like. It’s mostly numerical noise at the ~$1\times 10^{-6}$ level, and it’s more or less randomly distributed. It’s not entirely clear why we even have that much noise, because $\text{HorizontalShift}$ and $\text{VerticalStretch}$ are allowed to vary by $\pm 0.001$ and $\pm 0.00001$ respectively, so it’s not due to rounding of those values. It may just be some subtlety of the calculation that differs between Blizzard’s implementation and my MATLAB formula. Regardless, it isn’t that important – nobody will ever notice a 0.000001% change in parry chance.

In any event, the key point here is that Humans are magically getting around 0.07385 Strength worth of parry chance (about 0.0004187%) that isn’t affected by DR. The likely explanation is that the subtraction that negates the parry from $\text{classBaseStr}$ isn’t quite correct, since we know they’ve been fiddling with that recently. It may be something as simple as rounding – if whatever formula determines a warrior’s base strength produces a value of 1455.07385, the game could be adding 1455.07385 strength worth of parry rating, but only subtracting off the rounded 1455 strength worth of rating.

Repeating this with our gnome (-5 racial strength modifier) or dwarf (+5 racial strength modifier) revealed a few more details. If the racial strength modifier was included in $\text{bonusParry}$ (and thus affected by DR), I was only able to get a fit with “good” residuals if I was flexible with $\text{HorizontalShift}$ and $\text{VerticalStretch}$ – for example, if I let $\text{VerticalStretch}=0.00661$ and $\text{HorizontalShift}=0.9562$. And that was further complicated by the fact that those values would have to be allowed to be different for each race, which is obviously wrong since we know they’re class-dependent constants that are independent of race. If I tightened the restrictions on $\text{HorizontalShift}$ and $\text{VerticalStretch}$ such that they are nearly exact as given, the curvature started to appear again. For example:

Gnome warrior parry residuals, mark III.

Gnome warrior parry residuals, mark III.

General model:
 fitresult(x) = 3+(0+r)*0.0056697+x/(x*0.634*vs+hs)
 Coefficients (with 95% confidence bounds):
 vs =     0.00665  (fixed at bound)
 hs =       0.956  (fixed at bound)
 r =     -0.1561  (-0.3099, -0.002212)

However, if I move the racial strength bonus into $\text{baseParry}$ (outside of the DR calculation), we get good residuals:

Gnome warrior parry residuals, mark IV.

Gnome warrior parry residuals, mark IV.

General model:
 fitresult(x) = 3+(-5+r)*0.0056697+x/(x*0.634*vs+hs)
 Coefficients (with 95% confidence bounds):
 vs =     0.00665  (0.00665, 0.00665)
 hs =       0.956  (0.956, 0.956)
 r =     0.07391  (0.07368, 0.07413)

Again, recall that the $\text{bonusParry}$ value $x$ I feed to this equation is adjusted for the location of the racial modifier in the code in each case.

Note the amount of “phantom” strength $r$ being added here: 0.07391, awfully close to the value we found for humans. As it turns out, we get the same amount of “phantom” strength if we fit a gnome death knight (0.07387) or a dwarf warrior (0.07382), and a very similar value from a draenei warrior (0.07369). Loading up a human paladin (and adjusting $\text{HorizontalShift}$ appropriately) gives a phantom strength value of (0.07392), confirming that this isn’t a class-based thing and doesn’t depend on the DR calculation at all.

In short, any tank class that gets a strength-to-parry conversion is getting this phantom amount of parry, regardless of race, DR coefficients, or gear. Testing with a monk shows that they have a fixed 3.0000000% parry, suggesting that the agility tanks are not getting this bonus. This is why I’ve framed it as a phantom amount of strength rather than a phantom amount of parry. We can’t say for sure if the agility tanks still have that phantom strength contribution or not, because either way they don’t get any parry from it.

Testing The Dodge Equation

The obvious next question was whether agility tanks were getting a similar effect.

To test, I just assumed that the racial agility modifier worked the same way as the strength modifier did for the strength tanks. I created a Night Elf monk, which has a +2% dodge racial bonus and +4 racial agility modifier, and tried the formulas:

$\text{baseDodge} = 5.00 + (\text{raceAgiMod}+R)\times Q_A$
$x=\text{bonusDodge} = (\text{Agility}-\text{classBaseAgi}-\text{raceAgiMod})\times Q_A + \text{dodgeRating}/162$

along with the usual DR formula to determine $\text{totalDodge}$. The result was pretty good:

Night elf monk dodge fit residuals.

Night elf monk dodge fit residuals.

     General model:
     dfit(x) = 5+(4+R)*0.0056697+x/(x*0.3*vs+hs)
     Coefficients (with 95% confidence bounds):
       R =  2.345e-005  (-2.033e-005, 6.723e-005)
       hs =       1.422  (1.422, 1.422)
       vs =     0.00665  (0.00665, 0.00665)

Looks like our hunch was correct, and that racial base agility is granting dodge that isn’t affected by diminishing returns. Just to be sure, we should test a few more times though. Let’s double check this with a dwarf monk, which has a racial agility modifier of -4, and a base parry of 3%:

Dwarf Monk dodge fit residuals.

Dwarf Monk dodge fit residuals.

     General model:
     dfit(x) = 3+(-4+R)*0.0056697+x/(x*0.3*vs+hs)
     Coefficients (with 95% confidence bounds):
       R =   2.95e-005  (-4.765e-006, 6.376e-005)
       hs =       1.422  (1.422, 1.422)
       vs =     0.00665  (0.00665, 0.00665)

Looks good. Running a night elf druid through the fitting algorithm produces similar results:

Night elf druid dodge fit residuals.

Night elf druid dodge fit residuals.

     General model:
     dfit(x) = 5+(4+R)*0.0056697+x/(x*1*vs+hs)
     Coefficients (with 95% confidence bounds):
       R =  1.693e-005  (-1.27e-005, 4.655e-005)
       hs =       1.222  (1.222, 1.222)
       vs =     0.00665  (0.00665, 0.00665)

This pretty much confirms that we’ve got it right, and it’s reasonable to expect that it works the same way for the rest of the druid and monk races.

Testing The Block Equation

There isn’t really much to say here. The block equation is unchanged from Mists, and it appears they haven’t tinkered with it. The exact same fitting code that worked in Mists works now, and produces a great fit. Nonetheless, I updated it to match the other functions and to use the Blizzard formulation:

Human paladin block fit residuals.

Human paladin block fit residuals.

     General model:
     bfit(x) = 13+R+x/(x*1*vs+hs)
     Coefficients (with 95% confidence bounds):
       R =  7.405e-006  (-2.273e-005, 3.754e-005)
       hs =       0.886  (0.886, 0.886)
       vs =     0.00665  (0.00665, 0.00665)

Similarly good fits were obtained with all of my warrior test subjects, suggesting that the block DR equations are working the same way they have been.

Conclusions

So we’ve confirmed that

  • Racial strength modifiers grant parry that is not affected by parry DR.
  • Racial agility modifiers grant dodge that is not affected by dodge DR.
  • There’s some phantom parry being added to all strength-based tanking classes, but not to agility tanking classes.
  • Block diminishing returns seem to be working exactly like they should be.

The obvious question is, “what will Blizzard do about it?”

The parry bug is a difference of 0.0004%, which is insignificant in the grand scheme of things. It only matters to crazy people like me that want to be able to replicate the character sheet values 100% of the time and stamp down those rare 0.01% rounding errors. So it didn’t surprise me in the least that the response was that I should just build it into my models, because it was too small to matter and their programmers had better things to do. I can’t argue with that at all, really.

Similarly, I don’t think there’s any reason they need to move the racial strength modifier back out of $\text{baseParry}$ and into $\text{bonusParry}$, or to remove it altogether. It’s entirely arbitrary how that works, and it only matters insofar as theorycrafters want to know how to model it properly. It seems like a waste of time for them to change that around at this point just for the sake of cleaning up the equations.

So I think it’s safe to say that these are the diminishing returns formulas that we’ll be using throughout Warlords. I’ve already programmed the changes into Simulationcraft and run some tests with a few characters to make sure they’re working.

A Word On Theorycrafting

This installment was a little more complicated than the earlier TC101 articles. In particular, I talked a lot about “fitting” the data, but didn’t go into any detail on how that’s done. However, explaining how to fit data using MATLAB’s curve fitting toolbox or associated methods would be somewhat useless, because the likelihood is that you don’t have MATLAB at home. It’s more likely that you’d be putting the data in Excel or a Google documents spreadsheet and attempting to fit the data that way.

Unfortunately, that way is a lot less flexible (which is why I use MATLAB instead!). The built-in fitting functions are more limited, for one thing. If your data is linear, then you’re all set, but if not you often have to be creative. You can’t quickly and easily define and change a custom fitting function, at least to my knowledge.

But there are plenty of tutorials on how to do this sort of thing online. Fitting linear (i.e. $y=mx+b$) or polynomial data (i.e. $y=a + bx + cx^2 + dx^3 + …$) is incredibly easy, and you’ll find plenty of hits from a simple Google search. Fitting nonlinear data, like our diminishing returns equations, is more complicated, but there’s a great guide from California State Polytechnic University, Pomona that details how you’d go about fitting a complicated equation in Excel.

Ultimately, though, your first step should be to ask yourself whether you need that level of precision. For something like this, most players don’t, and would be satisfied with being within 0.01% of the character sheet avoidance value. If you decide you do need more accuracy, that’s when you take stock of the tools you have at hand to deal with the problem, and decide if they’re suitable. If they are (maybe you have Excel and the time to learn how to use the Solver), then great! If not, though, you might need to seek out someone who does have the knowledge and/or tools you need.

Remember, theorycrafting is a collaborative process, so there’s nothing wrong with asking for assistance. Sometimes just having another set of eyes looking at the data, or looking at it with a different tool, will crack a tricky problem wide open.

This entry was posted in Tanking, Theck's Pounding Headaches, Theorycrafting and tagged , , , , , , , , , , , , , , . Bookmark the permalink.

7 Responses to TC401: Avoidance Diminishing Returns in WoD

  1. Lakh says:

    I actually just recently asked the theorycrafting thread about a 0.01 inaccuracy with haste -> passive focus regeneration for hunters. My post describing what I’m seeing is: http://us.battle.net/wow/en/forum/topic/13087818929?page=29#566

    I could easily be doing something daft – my mathematical proficiency is a bit of a rusty blunt instrument – but it does sound like it could be something similar.

    I mention it because if there is something similar related to haste, I assume it could be of interest to you.

    Side note: I’ve always found hunter info a bit questionable & I’m quite fond of that alt, so these posts have helped inspire me to try and puzzle out some hunter theory. Cheers.

    • Theck says:

      To rule out some character sheet shenanigans, use a macro with

      /script print(GetCombatRatingBonus(CR_HASTE_MELEE))

      To get your exact haste to many (~15?) decimal places. Though if the rating conversion really is 100, this won’t be that interesting.

      You may also be able to use GetPowerRegen to get the focus regen value to similar precision, though:

      http://wowprogramming.com/docs/api/GetPowerRegen

      • Lakh says:

        You’re bang on – haste seems to be 1 rating = 0.01% +/- 0.000000001

        That wasn’t large enough to make the difference I was noticing tho, the real difference is the baseline focus regen.

        Focus regen baseline isn’t actually 4, it’s 4.0009999275208 – which is just large enough to start affecting things at 2 decimal places occasionally.

        Cheers.

  2. booi says:

    I know you’re really working for fractional benefits, but I’m pretty invested in this topic and appreciate the wok you’ve done. My spreadsheet will be just that little bit prettier for your efforts.

  3. Peter Cordes says:

    re: people not having MATLAB. GNU Octave is syntax-compatible with MATLAB, and has many of the same libraries. I haven’t used it myself for much since linear algebra classes, but it’s GPLed and well maintained. http://www.gnu.org/software/octave/

  4. Alamina-sen jin says:

    Are prot paladins still gonna be building haste in 6.0?

    • Theck says:

      I’m sure that’s Blizzard’s goal, but we’ll see if it works out in practice. At the moment (pre-tank-squish), haste wasn’t simming very well.

Leave a Reply