Elitist Jerks (http://elitistjerks.com/forums.php)
-   -   FCFS (Retribution) modeling script (http://elitistjerks.com/f76/t45098-fcfs_retribution_modeling_script/)

 Left 02/03/09 1:03 PM

FCFS (Retribution) modeling script

Following some discussion of late in the Ret Paladin thread, I decided that I was interested enough in the philosophical problem of prioritizing FCFS rotations that I would take a crack at some scripting to model different scenarios for Retribution FCFS rotations. The purpose of this script is not to provide another DPS calculator for Ret paladins, but rather to serve as a validation tool for Exemplar, Redcape, and anyone else who is trying to develop an FCFS priority system. The script runs a simulation which can be used to verify or disprove various assumptions about FCFS. However, I have no intention of developing it into a full-featured, user-friendly program. (If someone else wants to take it and do that, more power to them.)

Currently posted is version 2 of the script.

The script is written in Python, and can be edited and executed relatively easily by downloading and installing the free Python development environment. (I don't really know much of the innards of Python; I'm just using it because it is very easy to script in and because I used it last summer for some rogue theorycraft work regarding poison application.)

Basically, what the script does is use some user-defined values to simulate a basic FCFS rotation over a period of time. So far, what I have is a hard-coded priority list with no delay logic built in. To change the FCFS priority, you have to edit the code of the script. Latency is now incorporated as well as damage estimatation. The FCFS priority is still hardcoded.

Retribution FCFS Simulation Script v002:
Code:

```print 'FCFS Cycle Modeling for Retribution Paladins' print 'v002' print '' ############################################################### #                      USER VARIABLES # # Set the value of the variables in this section according to # your particular gear setup, target, and personal preference. ############################################################### # FCFS abbreviations: #  J = Judgement #  Hammer = Hammer of Wrath #  CS = Crusader Strike #  Cons = Consecrate #  DS = Divine Storm #  Exo = Exorcism #  HW = Holy Wrath #  DP = Divine Plea #  SoB = Reseal w/ Seal of Blood # Damage values of each ability ## All but HW pulled from Redcape's spreadsheet ## HW is a total guess dmgJ = 13229 dmgCS = 5024        ## Includes Seal of Blood damage dmgCons = 5809 dmgDS = 6381        ## Includes Seal of Blood damage dmgExo = 3802 dmgHW = 2800 dmgHammer = 6818 dmgDP = 0 dmgSoB = 0 # Undead/demon target? undead = True # Below 20%? (IE, use hammer of wrath?) hammer = False # Gear options fourPieceT7 = True consGlyph = True # Latency options [in seconds] latency = 0.000 # Simulation options [in seconds] step = 0.1 runTime = 30.0 ############################################################### #                      PROGRAM VARIABLES # # Don't change these unless you know what you're doing. # ############################################################### # Global cooldown gcd = 1.5 + latency # Cooldowns for each ability if fourPieceT7:     cooldownJ = 7 + latency else:     cooldownJ = 8 + latency cooldownHammer = 6 + latency cooldownCS = 6 + latency if consGlyph:     cooldownCons = 10 + latency else:     cooldownCons = 8 + latency cooldownDS = 10 + latency cooldownExo = 15 + latency cooldownHW = 30 + latency cooldownDP = 60 + latency cooldownSoB = 90 # Time variable time = 0 # Variables for tracking when each ability was last executed #  Initialized to the negative of their cooldown + 1, ie, #  they are off cooldown to start with #  (Exception is Seal of Blood, which is assumed to be used #    ~5 seconds before combat actually begins) lastJ = -(cooldownJ + 1) lastCS = -(cooldownCS + 1) lastCons = -(cooldownCons + 1) lastDS = -(cooldownDS + 1) lastExo = -(cooldownExo + 1) lastHW = -(cooldownHW + 1) lastHammer = -(cooldownHammer + 1) lastDP = -(cooldownDP + 1) lastSoB = -5.0 # Variables for tracking when each ability was first executed #  Initialized to 0.0 seconds to create proper variable type #  (Exception is Seal of Blood, which is assumed to be used #    ~5 seconds before combat actually begins) firstJ = 0.0 firstCS = 0.0 firstCons = 0.0 firstDS = 0.0 firstExo = 0.0 firstHW = 0.0 firstHammer = 0.0 firstDP = 0.0 firstSoB = -5.0 # Time at which the last ability, of any sort, was used #  Initialized to be one GCD before simulation start lastAbilityTime = -gcd # Name of last ability used lastAbility = '' # Variables for tracking how many times each ability is used numJ = 0 numCS = 0 numCons = 0 numDS = 0 numExo = 0 numHW = 0 numHammer = 0 numDP = 0 numSoB = 0 ############################################################### #                      SIMULATION # # Here's where the simulation actually occurs. # ############################################################### # Here's a more complex test, using a hard coded priority system print 'Simulating', runTime, 'seconds of FCFS' while time <= runTime:     # Check for Global Cooldown     if round(time-lastAbilityTime,2) < gcd:         time += step         continue     # Priority 0.5: Reseal if past 120 seconds     if round(time-lastSoB,2) >= 120.0:         print 'Seal of Blood Expired! Refreshing...'         numSoB += 1         lastSoB = time         lastAbility = 'Seal of Blood'     # Priority 1: Judgement     elif round(time-lastJ,2) >= cooldownJ:         numJ += 1         if numJ == 1: firstJ = time         lastJ = time         lastAbility = 'Judgement' ##    # Test case: Delay for judgement if within 0.5 sec ##    elif round(time-lastJ,2) >= (cooldownJ-0.5): ##        # Just wait it out ##        time += step ##        continue     # Priority 1.5: Hammer of Wrath (below 20%)     elif (round(time-lastHammer,2) >= cooldownCS) and hammer:         numHammer += 1         if numHammer == 1: firstHammer = time         lastHammer = time         lastAbility = 'Hammer of Wrath'     # Priority 2: Crusader Strike     elif round(time-lastCS,2) >= cooldownCS:         numCS += 1         if numCS == 1: firstCS = time         lastCS = time         lastAbility = 'Crusader Strike'     # Priority 3: Consecrate     elif round(time-lastCons,2) >= cooldownCons:         numCons += 1         if numCons == 1: firstCons = time         lastCons = time         lastAbility = 'Consecrate'     # Priority 4: Divine Storm     elif round(time-lastDS,2) >= cooldownDS:         numDS += 1         if numDS == 1: firstDS = time         lastDS = time         lastAbility = 'Divine Storm'     # Priority 5: Exorcism     elif (round(time-lastExo,2) >= cooldownExo) and undead:         numExo += 1         if numExo == 1: firstExo = time         lastExo = time         lastAbility = 'Exorcism'     # Priority 6: Holy Wrath     elif (round(time-lastHW,2) >= cooldownHW) and undead:         numHW += 1         if numHW == 1: firstHW = time         lastHW = time         lastAbility = 'Holy Wrath'     # Priority 7: Divine Plea     elif round(time-lastDP,2) >= cooldownDP:         numDP += 1         if numDP == 1: firstDP = time         lastDP = time         lastAbility = 'Divine Plea'     # Priority 8: Reseal     elif round(time-lastSoB,2) >= cooldownSoB:         numSoB += 1         lastSoB = time         lastAbility = 'Seal of Blood'      # If nothing was off cooldown...     else:         time += step         continue     # Clean up after using an ability     # Comment this next line if you want to improve execution speed     print 'Used', lastAbility, 'at', time, 'sec'     lastAbilityTime = time      time += step gcdUsed = numJ + numCS + numCons + numDS + numExo + numHW + numHammer + numDP + numSoB print '' print 'END SIMULATION' print '---Cooldowns Remaining---' print 'Judgement:', max(round(cooldownJ-(runTime-lastJ),2),0), 'sec' print 'Crusader Strike:', max(round(cooldownCS-(runTime-lastCS),2),0), 'sec' print 'Consecrate:', max(round(cooldownCons-(runTime-lastCons),2),0), 'sec' print 'Divine Storm:', max(round(cooldownDS-(runTime-lastDS),2),0), 'sec' print 'Exorcism:', max(round(cooldownExo-(runTime-lastExo),2),0), 'sec' print 'Holy Wrath:', max(round(cooldownHW-(runTime-lastHW),2),0), 'sec' print 'Hammer of Wrath:', max(round(cooldownHammer-(runTime-lastHammer),2),0), 'sec' print 'Divine Plea:', max(round(cooldownDP-(runTime-lastDP),2),0), 'sec' print '' print 'RESULTS:' print 'GCD density:', round((gcdUsed*gcd)/(runTime),4)*100,'%' print '' print '---Abilities Used---' print 'Judgement:', numJ print 'Crusader Strike:', numCS print 'Consecrate:', numCons, '(',numCons*cooldownCons, 'ticks)' print 'Divine Storm:', numDS print 'Exorcism:', numExo print 'Holy Wrath:', numHW print 'Hammer of Wrath:', numHammer print 'Divine Plea:', numDP print 'Seal of Blood:', numSoB print '' print '---Average Usage Interval---' if numJ > 1:     print 'Judgement:', round((lastJ-firstJ)/(numJ-1),2), 'sec' if numCS > 1:     print 'Crusader Strike:', round((lastCS-firstCS)/(numCS-1),2), 'sec' if numCons > 1:     print 'Consecrate:', round((lastCons-firstCons)/(numCons-1),2), 'sec' if numDS > 1:     print 'Divine Storm:', round((lastDS-firstDS)/(numDS-1),2), 'sec' if numExo > 1:     print 'Exorcism:', round((lastExo-firstExo)/(numExo-1),2), 'sec' if numHW > 1:     print 'Holy Wrath:', round((lastHW-firstHW)/(numHW-1),2), 'sec' if numHammer > 1:     print 'Hammer of Wrath:', round((lastHammer-firstHammer)/(numHammer-1),2), 'sec' if numDP> 1:     print 'Divine Plea:', round((lastDP-firstDP)/(numDP-1),2), 'sec' if numSoB> 0:      # Seal of Blood is a special case     print 'Seal of Blood:', round((lastSoB-firstSoB)/(numSoB),2), 'sec' print '' print '---Damage---' print 'Judgement:', numJ*dmgJ print 'Crusader Strike:', numCS*dmgCS print 'Consecrate:', numCons*dmgCons print 'Divine Storm:', numDS*dmgDS print 'Exorcism:', numExo*dmgExo print 'Holy Wrath:', numHW*dmgHW print 'Hammer of Wrath:', numHammer*dmgHammer totDamage = numJ*dmgJ + numCS*dmgCS + numCons*dmgCons + numDS*dmgDS + numExo*dmgExo + numHW*dmgHW + numHammer*dmgHammer avgDPS = (totDamage*1.0)/runTime print 'TOTAL:', totDamage print 'DPS:', round(avgDPS,2) print '(NOTE: Does not include any estimate of white DPS)'```
Changelog
v001
• Created script
v002
• Added latency modeling via increased GCD and other cooldowns, as per Exemplar's suggestion
• Added Divine Plea and the capability to reseal
• Added a crude damage estimator
• Improved usage interval calculations as per Dokb's suggestion
• Hard coded resealing-specific priority into the FCFS system
• Hard coded an optional delay-to-judge option in the FCFS system (uncomment code to enable)

Sample Output:
• 30 second runtime
• J > CS > Cons > DS > Exo > HW
• Hammer of Wrath not used
• 4pT7 = TRUE
• Consecrate Glyph = TRUE

Code:

```>>> FCFS Cycle Modeling for Retribution Paladins v002 Simulating 30.0 seconds of FCFS Used Judgement at 0 sec Used Crusader Strike at 1.5 sec Used Consecrate at 3.0 sec Used Divine Storm at 4.5 sec Used Exorcism at 6.0 sec Used Judgement at 7.5 sec Used Crusader Strike at 9.0 sec Used Holy Wrath at 10.5 sec Used Divine Plea at 12.0 sec Used Consecrate at 13.5 sec Used Judgement at 15.0 sec Used Crusader Strike at 16.5 sec Used Divine Storm at 18.0 sec Used Exorcism at 21.0 sec Used Judgement at 22.5 sec Used Crusader Strike at 24.0 sec Used Consecrate at 25.5 sec Used Divine Storm at 28.0 sec Used Judgement at 29.5 sec END SIMULATION ---Cooldowns Remaining--- Judgement: 6.5 sec Crusader Strike: 0.0 sec Consecrate: 5.5 sec Divine Storm: 8.0 sec Exorcism: 6.0 sec Holy Wrath: 10.5 sec Hammer of Wrath: 0 sec Divine Plea: 42.0 sec RESULTS: GCD density: 95.0 % ---Abilities Used--- Judgement: 5 Crusader Strike: 4 Consecrate: 3 ( 30.0 ticks) Divine Storm: 3 Exorcism: 2 Holy Wrath: 1 Hammer of Wrath: 0 Divine Plea: 1 Seal of Blood: 0 ---Average Usage Interval--- Judgement: 7.38 sec Crusader Strike: 7.5 sec Consecrate: 11.25 sec Divine Storm: 11.75 sec Exorcism: 15.0 sec ---Damage--- Judgement: 66145 Crusader Strike: 20096 Consecrate: 17427 Divine Storm: 19143 Exorcism: 7604 Holy Wrath: 2800 Hammer of Wrath: 0 TOTAL: 133215 DPS: 4440.5 (NOTE: Does not include any estimate of white DPS)```
Average interval problem went away in v002 due to a new way to calculate it.

I'll post some runtime results from v002 further down in the thread.

Please feel free to take and modify this code as needed, or to request specific setups to be run, etc. Please post bug reports, etc, to this thread. If I get some time, I'll try to refine the modeling in a useful way, but since this is currently my lunch break project at work it won't necessarily happen very fast.

Note that you may also be interested in Zengir's work, as he has put together a similar program in .NET which more fully supports rotations and priority systems. At the time of this writing, this post links to his latest version.

 Dokb 02/03/09 2:42 PM

Average usage interval

Regarding the average usage interval, I see a simple way to estimate it.

You'd have to record the first time an ability is used and the last time it is used. Then you would calculate : (timeLastUsed - timeFirstUsed)/(numberUsed-1). The -1 is to prevent what is happening right now, since you will always end up calculating the average use on a time period where the last ability used is the one concerned.

To do so in your script, you would have to add two variables per ability. One for the timeFirstUsed, and one to inform the script if the ability has been used.

For example, if you want to do it for J, you could add in variables :

Code:

```timeFirstUsedJ = 0 firstJ = 1```
Then when you simulate the J priority :

Code:

```if round(time-timeJ,2) >= cooldownJ:         numJ += 1         timeJ = time         #Records when you use J for the first time         if firstJ = 1               timeFirstUsedJ = time               firstJ = 0         lastAbility = 'Judgement'```
The simulated average usage interval would then be in the script :
Code:

`(timeJ-timeFirstUsedJ)/(numJ-1)`
By the way, I have never coded anything in python, I might be wrong with how I write the code, but the idea behind is quite clear I believe.

 Left 02/03/09 3:03 PM

That's an excellent idea. Thanks for pointing it out. After seeing your code (and yes, Python really is that simple; it's why I like it) I think I'd modify it as follows:

Code:

```    # Priority 1: Judgement     if round(time-timeJ,2) >= cooldownJ:         numJ += 1         if numJ = 1               timeFirstUsedJ = time         timeJ = time         lastAbility = 'Judgement'```
numJ provides a natural tracking parameter.

I don't have time to do this now, but I'll see if I can fix it for later.

 Janraea 02/03/09 3:21 PM

Quote:
 Originally Posted by Dokb (Post 1085940) Regarding the average usage interval, I see a simple way to estimate it. You'd have to record the first time an ability is used and the last time it is used. Then you would calculate : (timeLastUsed - timeFirstUsed)/(numberUsed-1). The -1 is to prevent what is happening right now, since you will always end up calculating the average use on a time period where the last ability used is the one concerned. (code) By the way, I have never coded anything in python, I might be wrong with how I write the code, but the idea behind is quite clear I believe.
That hardly seems necessary. Running the script with length = 6 hours takes less than two seconds on my (not terribly powerful) computer, and that dwarfs the end-point errors (which are all plus or minus 2, maximum).

I have altered a segment of the code to allow it to avoid CS immediately after Judgement (as some prefer):

Code:

```while time <= runTime:     # Check for Global Cooldown     if round(time-lastAbilityTime,2) < gcd:         time += step         continue     # Priority 1: Judgement     if round(time-timeJ,2) >= cooldownJ:         numJ += 1         timeJ = time         lastAbility = 'Judgement'     # Priority 1.5: Hammer of Wrath (below 20%)     elif (round(time-timeHammer,2) >= cooldownCS) and hammer:         numHammer += 1         timeHammer = time         lastAbility = 'Hammer of Wrath'     # Priority 2: Crusader Strike     elif round(time-timeCS,2) >= cooldownCS and not (lastAbility == 'Judgement' and fourPieceT7 and dontCSafterJ):         numCS += 1         timeCS = time         lastAbility = 'Crusader Strike'     # Priority 3: Consecrate     elif round(time-timeCons,2) >= cooldownCons:         numCons += 1         timeCons = time         lastAbility = 'Consecrate'     # Priority 4: Divine Storm     elif round(time-timeDS,2) >= cooldownDS:         numDS += 1         timeDS = time         lastAbility = 'Divine Storm'     # Priority 5: Exorcism     elif (round(time-timeExo,2) >= cooldownExo) and undead:         numExo += 1         timeExo = time         lastAbility = 'Exorcism'     # Priority 6: Holy Wrath     elif (round(time-timeHW,2) >= cooldownHW) and undead:         numHW += 1         timeHW = time         lastAbility = 'Holy Wrath'     # Priority 2: Crusader Strike just after Judgement with 4T7     elif round(time-timeCS,2) >= cooldownCS:         numCS += 1         timeCS = time         lastAbility = 'Crusader Strike'     # If nothing was off cooldown...     else:         time += step         continue     # Clean up after using an ability     # Comment this next line if you want to improve execution speed     print 'Used', lastAbility, 'at', time, 'sec'     lastAbilityTime = time     time += step```
Behavior is controlled by the dontCSafterJ variable - if it's True, CS is given much lower priority if Judgement was the previous ability.

Some interesting conclusions from using that variable:

1) If Exorcism isn't being used, noCSafterJ increases the number of Judgements used by a few percent, because the partial cds line up better.
2) If Exorcism IS being used, noCSafterJ decreases both CS use and Judgement use.
(With No Exorcism, Consecrate is up 100% of the time if noCSafterJ is activated, by happenstance of gcd timing).

I will leave net numeric conclusions for when I have more time, but here are ability counts for the four cases for a 24-hour long fight:

1) Exo:
Code:

```Judgement: 11782 Crusader Strike: 11782 Consecrate: 7855 ( 78550 ticks) Divine Storm: 7854 Exorcism: 5237 Holy Wrath: 2618```
2) Exo, noCSafterJ:
Code:

```Judgement: 11520 Crusader Strike: 11520 Consecrate: 8640 ( 86400 ticks) Divine Storm: 8640 Exorcism: 5760 Holy Wrath: 2880```
3) No Exo:
Code:

```Judgement: 12056 Crusader Strike: 12056 Consecrate: 8037 ( 80370 ticks) Divine Storm: 8037```
4) No Exo, NoCSafterJ:
Code:

```Judgement: 12343 Crusader Strike: 12343 Consecrate: 8229 ( 82290 ticks) Divine Storm: 8229```

Edit: Something seems wrong with those number, I'm checking into it >.<
Edit again: Nope, they're fine.

The thing that had me worried was how the judgement count matches up with the cs count on every run. That actually is what should be happening though - we lose a 192 each of DS and Cons for 287 each of Judgement and CS. Seems like a good trade to me.

Might be worthwhile to make the loop occasionally skip chunks of gcds (out of range, mechanics of a fight type thing), and see how well the 'noCSafterJ' concept copes with rotation interruptions.. I'll get something on that later.

No Exo, noCSafterJ repeats after 21 seconds, with J>CS>Cons>DS (as I believe is common knowledge now in the main ret thread):
Code:

```Used Judgement at 0 sec Used Consecrate at 1.5 sec Used Crusader Strike at 3.0 sec Used Divine Storm at 4.5 sec Used Judgement at 7.0 sec Used Crusader Strike at 9.0 sec Used Consecrate at 11.5 sec Used Judgement at 14.0 sec Used Divine Storm at 15.5 sec Used Crusader Strike at 17.0 sec Used Judgement at 21.0 sec```

 Exemplar 02/03/09 5:02 PM

Off the cuff idea:
You should be able to get a rough approximation of latency by adding a latency variable. Add this variable to cooldown time of all abilities and to the GCD used to cast abilities.

If you want to model Divine Plea and Re-seal, this would be easy. Just give DP a 60 sec cooldown, Seal about a 90 sec (so it starts looking early, but not too early). Then set them to only cast if nothing is under a whole GCD remaining on its cooldown.

 Dokb 02/03/09 6:41 PM

To include latency you could just model it by a fraction of the step.

Say you have 200 ms latency, when you judge, it occurs 200ms later or 2 step later.

You could write it like this (using the judge portion) :
Code:

```#First line is when you see the J button off cd if round(time-timeJ,2) >= cooldownJ:         #This is the time it takes the game to actually cast the J and reset the cd         time += latency*step         #Now that the spell is cast, you can record the time it went off, after latency         numJ += 1         timeJ = time         lastAbility = 'Judgement'```
latency would be between 0 and whatever. It has to be an integer so that you don't mess up your step. If you need a smaller latency, you'd have to use a smaller step.

 zengir 02/04/09 2:49 PM

Hi!

I've been fiddling around with different rotations for a while, and trying to make the best of it with excel-sheets and such, but I got tired of having to type it all in everytime, so I made a simple application for it in C# .NET.

You enter your average damage on JoM, CS, DS, Consecration, melee and SoM, weapon speed, the length of the fight AND your preferred rotation AND your cooldowns on your spells (allowing for 10/8 sec consecration and 7/8 sec judgement)

The output looks like this with very simple numbers, JoM: 7000, CS: 3000, DS: 4500, Cons: 5000, Melee: 3000, SoM: 1000, 3.0 weapon speed (after haste) and fight length 240 seconds:

--------------------------------------
Simulation time: 240 seconds.
Judgement cooldown: 8 seconds.
Priority: jom > cs > ds > cons
Total DPS: 3741,67

Number of casts:
JoM: 29
CS: 36
DS: 22
Cons: 22

Damage and DPS per ability:
JoM DPS: 845,83 - DMG: 203000
CS DPS: 450 - DMG: 108000
DS DPS: 412,5 - DMG: 99000
Cons DPS: 458,33 - DMG: 110000
Melee DPS: 1000 - DMG: 240000
SoM DPS: 575 - DMG: 138000

--------------------------------------

Simulation time: 240 seconds.
Judgement cooldown: 8 seconds.
Priority: cs > jom > ds > cons
Total DPS: 3762,5

Number of casts:
JoM: 28
CS: 39
DS: 22
Cons: 22

--------------------------------------

Simulation time: 240 seconds.
Judgement cooldown: 8 seconds.
Priority: cs > ds > jom > cons
Total DPS: 3772,92

Number of casts:
JoM: 28
CS: 37
DS: 23
Cons: 23

--------------------------------------

The same calculations with a 7 second judgement shows the following:

--------------------------------------
Simulation time: 240 seconds.
Judgement cooldown: 7 seconds.
Priority: jom > cs > ds > cons
Total DPS: 3877,08

Number of casts:
JoM: 34
CS: 34
DS: 23
Cons: 22

--------------------------------------

Simulation time: 240 seconds.
Judgement cooldown: 7 seconds.
Priority: cs > jom > ds > cons
Total DPS: 3908,33

Number of casts:
JoM: 33
CS: 39
DS: 22
Cons: 22

--------------------------------------

Simulation time: 240 seconds.
Judgement cooldown: 7 seconds.
Priority: cs > ds > jom > cons
Total DPS: 3877,08

Number of casts:
JoM: 31
CS: 38
DS: 23
Cons: 23

--------------------------------------

Theese numbers might not be correct, since I'm not really sure about the average damage on the different abilites. I haven't really raided much, so I don't have much WWS-data to go by, and the numbers I get from Redcape's sheet seem a bit too high. They are suggesting that I'd do around 5k DPS, but I'm only in heroic/emblem/10-man gear, and I'm usually ending up around 3,5k DPS in 25-mans. I'd be happy to run a few simulations with some "real" numbers if anyone has any good data.

I'm hoping I'll have the time to finish this little application in the weekend, so I might be uploading somewhere if someone is interested in giving it a go. There's just a few things that needs to be sorted out before I can let someone else use it :)

(Woah, that's a long first post :) )

 dlaiyre 02/04/09 7:44 PM

Am I right assuming that we could add Latency to the GCD? (ex gcd=1.8 for 300ms)

 Dokb 02/04/09 9:59 PM

Not really, latency influences the time when the cooldown actually starts. There are three important events when you cast a spell :

-The time your client tells you the cd is over
-The time you launch the spell (client side)
-The time it takes for the server to confirm to your client that the spell is cast (this is where latency comes into play)

The important thing to know is that when you are almost off the gcd or the cd of a spell, your client can send a cast request to the server (this change was done recently, during BC). However, the server still has to confirm that you can (and did) cast the spell, this is the latency we experience. This is also why people like me with an average of 8 ms ping, sometimes have between 100 ms and 300ms of latency while casting a spell. It comes from the server taking time to process the information my client sends.

PS: forgive my english, I am french. If I am not clear enough, I can try to explain it differently.

 zengir 02/05/09 2:02 AM

Well, the simple way would be to just make the GCD a bit longer, and I think it would actually be pretty close to the truth.

What happens is probably something like this: (Let's assume 200ms latency and lightning reflexes)

0.00 You press a button to cast a spell.
0.20 The spell get's casted (This includes sending a message to the server, and getting a response)
1.70 You press a button to cast a spell
1.90 The spell get's casted (This includes sending a message to the server, and getting a response)

The best way to implement it would simply be to add your latency to the timer right before every cast, and only then. That would solve most problems with CD's and GCD, although it will mess things up if you're using melee dmg in your script.

 zengir 02/06/09 9:01 AM

I added latency to my application, and some simple calculations shows that there's quite a large amount of DPS lost due to latency. I added latency right before a spell is cast, which should be pretty close to reality.

With average numbers from my latest WWS my tool suggests 4190DPS with a CS>JoM>DS>Cons rotation and 0ms latency in a 240sec fight. With 100ms latency I'm down to 4017DPS, 200ms puts me down another step to 3820DPS.

With a few extra calculations on different sets of gears, different latencies and different lengths of fights my calculations suggests there is about 3-5% of DPS loss for every 100ms latency added, which is quite substantial.

 Left 02/06/09 1:36 PM

Zengir, I've noticed a similar loss progression with my version of the script. I'd be interested to run some identical test cases with your script and my script and verify that they come up with essentially the same thing. Do you have a link posted to where your script can be downloaded/used? Or do you want to run some cases, let me know the results, and then I can run the same ones?

---------------

As for other tests, I was asked to do a couple of things with the script. One was to verify that any amount of delay prior to judgment was a bad idea in the long run. (IE, to verify that FCFS has no exceptions to the "if something is off cooldown, use it" rule.) To do this, I ran eight tests: with/without 4pT7 vs. with/without delay before judgement, at latency = 0 sec and latency = 0.200 sec. Damage numbers were pulled straight from Redcape's spreadsheet. (Exemplar, I checked yours but I couldn't find where in all those cells you had a damage-per-cast number for the various abilities; all I saw was DPS. Am I missing something?)

All tests were run for a 300 second (5 minute) fight duration. I commented out the simulation log, so only the results came through. In all test cases I assumed an undead target, ie, Exorcism and HW were valid attacks. (This also more accurately simulates 3.1, given the upcoming Exorcism change.) I was using my updated version, v002, of the script, as currently displayed in the first post.

Here are the results (sorry for the wall of text):

Test 1
4pT7 = False
Delay before Judgement (if within 0.5 seconds) = False
Latency = 0.000 sec

Code:

```RESULTS: GCD density: 85.0 % ---Abilities Used--- Judgement: 36 Crusader Strike: 45 Consecrate: 27 ( 270.0 ticks) Divine Storm: 27 Exorcism: 18 Holy Wrath: 9 Hammer of Wrath: 0 Divine Plea: 5 Seal of Blood: 3 ---Average Usage Interval--- Judgement: 8.56 sec Crusader Strike: 6.66 sec Consecrate: 11.04 sec Divine Storm: 11.21 sec Exorcism: 16.88 sec Holy Wrath: 33.5 sec Divine Plea: 63.0 sec Seal of Blood: 97.0 sec ---Damage--- Judgement: 476244 Crusader Strike: 226080 Consecrate: 156843 Divine Storm: 172287 Exorcism: 68436 Holy Wrath: 25200 Hammer of Wrath: 0 TOTAL: 1125090 DPS: 3750.3 (NOTE: Does not include any estimate of white DPS)```
Test 2
4pT7 = False
Delay before Judgement (if within 0.5 seconds) = True
Latency = 0.000 sec

Code:

```RESULTS: GCD density: 83.0 % ---Abilities Used--- Judgement: 37 Crusader Strike: 37 Consecrate: 28 ( 280.0 ticks) Divine Storm: 28 Exorcism: 19 Holy Wrath: 9 Hammer of Wrath: 0 Divine Plea: 5 Seal of Blood: 3 ---Average Usage Interval--- Judgement: 8.12 sec Crusader Strike: 8.12 sec Consecrate: 10.91 sec Divine Storm: 10.93 sec Exorcism: 16.11 sec Holy Wrath: 32.56 sec Divine Plea: 65.12 sec Seal of Blood: 95.83 sec ---Damage--- Judgement: 489473 Crusader Strike: 185888 Consecrate: 162652 Divine Storm: 178668 Exorcism: 72238 Holy Wrath: 25200 Hammer of Wrath: 0 TOTAL: 1114119 DPS: 3713.73 (NOTE: Does not include any estimate of white DPS)```

Test 3
4pT7 = True
Delay before Judgement (if within 0.5 seconds) = False
Latency = 0.000 sec

Code:

```RESULTS: GCD density: 86.0 % ---Abilities Used--- Judgement: 41 Crusader Strike: 41 Consecrate: 27 ( 270.0 ticks) Divine Storm: 27 Exorcism: 19 Holy Wrath: 9 Hammer of Wrath: 0 Divine Plea: 5 Seal of Blood: 3 ---Average Usage Interval--- Judgement: 7.35 sec Crusader Strike: 7.35 sec Consecrate: 11.04 sec Divine Storm: 11.25 sec Exorcism: 16.28 sec Holy Wrath: 32.5 sec Divine Plea: 66.12 sec Seal of Blood: 95.83 sec ---Damage--- Judgement: 542389 Crusader Strike: 205984 Consecrate: 156843 Divine Storm: 172287 Exorcism: 72238 Holy Wrath: 25200 Hammer of Wrath: 0 TOTAL: 1174941 DPS: 3916.47 (NOTE: Does not include any estimate of white DPS)```
Test 4
4pT7 = True
Delay before Judgement (if within 0.5 seconds) = True
Latency = 0.000 sec

Code:

```RESULTS: GCD density: 84.5 % ---Abilities Used--- Judgement: 42 Crusader Strike: 42 Consecrate: 26 ( 260.0 ticks) Divine Storm: 25 Exorcism: 17 Holy Wrath: 9 Hammer of Wrath: 0 Divine Plea: 5 Seal of Blood: 3 ---Average Usage Interval--- Judgement: 7.24 sec Crusader Strike: 7.24 sec Consecrate: 11.58 sec Divine Storm: 12.06 sec Exorcism: 17.59 sec Holy Wrath: 35.62 sec Divine Plea: 72.0 sec Seal of Blood: 96.83 sec ---Damage--- Judgement: 555618 Crusader Strike: 211008 Consecrate: 151034 Divine Storm: 159525 Exorcism: 64634 Holy Wrath: 25200 Hammer of Wrath: 0 TOTAL: 1167019 DPS: 3890.06 (NOTE: Does not include any estimate of white DPS)```
Test 5
4pT7 = False
Delay before Judgement (if within 0.5 seconds) = False
Latency = 0.200 sec

Code:

```RESULTS: GCD density: 91.8 % ---Abilities Used--- Judgement: 35 Crusader Strike: 43 Consecrate: 26 ( 265.2 ticks) Divine Storm: 25 Exorcism: 17 Holy Wrath: 9 Hammer of Wrath: 0 Divine Plea: 5 Seal of Blood: 2 ---Average Usage Interval--- Judgement: 8.76 sec Crusader Strike: 7.09 sec Consecrate: 11.64 sec Divine Storm: 11.77 sec Exorcism: 17.34 sec Holy Wrath: 34.89 sec Divine Plea: 68.92 sec Seal of Blood: 97.9 sec ---Damage--- Judgement: 463015 Crusader Strike: 216032 Consecrate: 151034 Divine Storm: 159525 Exorcism: 64634 Holy Wrath: 25200 Hammer of Wrath: 0 TOTAL: 1079440 DPS: 3598.13 (NOTE: Does not include any estimate of white DPS)```
Test 6
4pT7 = False
Delay before Judgement (if within 0.5 seconds) = True
Latency = 0.200 sec

Code:

```RESULTS: GCD density: 90.67 % ---Abilities Used--- Judgement: 36 Crusader Strike: 36 Consecrate: 27 ( 275.4 ticks) Divine Storm: 26 Exorcism: 18 Holy Wrath: 9 Hammer of Wrath: 0 Divine Plea: 5 Seal of Blood: 3 ---Average Usage Interval--- Judgement: 8.46 sec Crusader Strike: 8.46 sec Consecrate: 11.39 sec Divine Storm: 11.5 sec Exorcism: 16.72 sec Holy Wrath: 34.04 sec Divine Plea: 64.25 sec Seal of Blood: 99.8 sec ---Damage--- Judgement: 476244 Crusader Strike: 180864 Consecrate: 156843 Divine Storm: 165906 Exorcism: 68436 Holy Wrath: 25200 Hammer of Wrath: 0 TOTAL: 1073493 DPS: 3578.31 (NOTE: Does not include any estimate of white DPS)```
Test 7
4pT7 = True
Delay before Judgement (if within 0.5 seconds) = False
Latency = 0.200 sec

Code:

```RESULTS: GCD density: 94.07 % ---Abilities Used--- Judgement: 38 Crusader Strike: 38 Consecrate: 27 ( 275.4 ticks) Divine Storm: 26 Exorcism: 19 Holy Wrath: 10 Hammer of Wrath: 0 Divine Plea: 5 Seal of Blood: 3 ---Average Usage Interval--- Judgement: 7.91 sec Crusader Strike: 7.91 sec Consecrate: 11.38 sec Divine Storm: 11.7 sec Exorcism: 15.78 sec Holy Wrath: 31.56 sec Divine Plea: 65.05 sec Seal of Blood: 95.77 sec ---Damage--- Judgement: 502702 Crusader Strike: 190912 Consecrate: 156843 Divine Storm: 165906 Exorcism: 72238 Holy Wrath: 28000 Hammer of Wrath: 0 TOTAL: 1116601 DPS: 3722.0 (NOTE: Does not include any estimate of white DPS)```
Test 8
4pT7 = True
Delay before Judgement (if within 0.5 seconds) = True
Latency = 0.200 sec

Code:

```RESULTS: GCD density: 89.53 % ---Abilities Used--- Judgement: 41 Crusader Strike: 41 Consecrate: 22 ( 224.4 ticks) Divine Storm: 21 Exorcism: 17 Holy Wrath: 8 Hammer of Wrath: 0 Divine Plea: 5 Seal of Blood: 3 ---Average Usage Interval--- Judgement: 7.39 sec Crusader Strike: 7.39 sec Consecrate: 13.73 sec Divine Storm: 14.41 sec Exorcism: 18.02 sec Holy Wrath: 36.71 sec Divine Plea: 64.87 sec Seal of Blood: 97.17 sec ---Damage--- Judgement: 542389 Crusader Strike: 205984 Consecrate: 127798 Divine Storm: 134001 Exorcism: 64634 Holy Wrath: 22400 Hammer of Wrath: 0 TOTAL: 1097206 DPS: 3657.35 (NOTE: Does not include any estimate of white DPS)```
In other words, delaying for judgement does cause a small DPS loss in every single case tested. This is a confirmation of FCFS "conventional wisdom." One interesting result is that doing a judgement delay when not using 4pT7 seems to consistently conflict with Crusader Strike coming off cooldown immediately before judgement, thus Crusader Strike's cooldown gets artificially set to Judgement's cooldown. Without the delay, CS and J interweave more easily in a non-4pT7 environment.

Also note that latency in and of itself causes ~100 DPS difference per 0.1 second of latency, at least as modeled here.

 Exemplar 02/06/09 3:07 PM

Brilliant. Left, sent you a PM with directions to find average dam per cast in my spreadsheet.

I'm going to try to install Python and run some tests with your script (not that I don't trust your results). Start modeling the new Exo but no HW (3.1 assumptions) in 7 and 8 seconds. Also some borderline cases I was considering while I was staring at manual models. Like if Judge is under 0.5 seconds and Exo is up, skip Exo, but not the other abilities. Or CS. Etc. If I find anything unusual I'll post it here. I suspect it may sometimes be a win to pause that half second, but this is a gut instinct and against common wisdom and all our math to date.

Great tool, I plan to use it to enhance the next version of the spreadsheet that I release (with yet more accurate rotation information - probably extrapolating out to 6hrs as recommended earlier in this thread). I highly recommend those using Redcape's excellent spreadsheet also use this to figure out their own real-world rotations.

 zengir 02/06/09 7:37 PM