Last active
January 25, 2023 11:59
-
-
Save AndyGrant/9358516c1e246d8b554c4fc99bb122e0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from random import uniform | |
from concurrent.futures import ThreadPoolExecutor | |
ITERATIONS = 1000 # Number of times to run the simulation, to reduce variance | |
LENGTH = 300 * 1000 # Fight is 300 seconds, or 300,000 milliseconds | |
AA_SPEED = LENGTH / 154.0 # We just compute your in-practice AA-speed, based on the first sim | |
BASE_CD = 45 * 1000 # This is the CD of Wake of ashes. We assume at-least 1 cast per 45s | |
INITIAL_CAST = 5.875 * 1000 # According to your first sim, you do not press Wake until this time | |
PROC_RATE = 0.042 # This is the probability of getting a Wake proc on an auto attack | |
WINDFURY_GAIN_THEORY = 0.200 # Windfury provides a 20% increase in auto-attacks | |
WINDFURY_GAIN_PRACTICE = 0.175 # However, in practice, you are only seeing a 17.5% increase | |
def simulate_single(has_windfury, use_theory): | |
total_casts = 1 # We get our first Wake cast here | |
curr_time = INITIAL_CAST # This does not happen until a few seconds into the fight | |
most_recent_cast = INITIAL_CAST # We track the last time we used Wake, for the CD timer | |
most_recent_auto = INITIAL_CAST # Lets just assume you get your first auto here. | |
# Pick a windfury rate. Either the theoetical gain, or the observed gain. | |
windfury_rate = WINDFURY_GAIN_THEORY if use_theory else WINDFURY_GAIN_PRACTICE | |
while (curr_time := curr_time + AA_SPEED) < LENGTH: | |
# We went the full duration without casting, so cast now | |
if curr_time - most_recent_cast >= BASE_CD: | |
total_casts = total_casts + 1 | |
most_recent_cast = curr_time | |
# Do we need to do an auto-attack (?) | |
if curr_time - most_recent_auto >= AA_SPEED: | |
# Roll ahead the auto-attack timer by AA_SPEED milliseconds | |
most_recent_auto = most_recent_auto + AA_SPEED | |
# We proc 42 (PROC_RATE) times per 1,000 autos. | |
if uniform(0, 1) < PROC_RATE: | |
total_casts = total_casts + 1 | |
most_recent_cast = most_recent_auto | |
continue | |
# We again proc 42 (PROC_RATE) times per 1,000 autos, if we triggered a Windfury proc. | |
got_windfury_proc = has_windfury and uniform(0, 1) < windfury_rate | |
if got_windfury_proc and uniform(0, 1) < PROC_RATE: | |
total_casts = total_casts + 1 | |
most_recent_cast = most_recent_auto | |
return total_casts | |
def simulate_whole(has_windfury=False, use_theory=False): | |
# Ignore this fancy stuff. We run the sim ITERATIONS number of times | |
# We return the fewest casts, most casts, and average casts over all sims | |
args = ((has_windfury, use_theory) for ii in range(ITERATIONS)) | |
with ThreadPoolExecutor() as executor: | |
sims = list(executor.map(lambda x: simulate_single(*x), args)) | |
return min(sims), max(sims), sum(sims) / len(sims) | |
print ('Baseline ', simulate_whole(False, None)) | |
print ('Windfury (17.5%)', simulate_whole(True, False)) | |
print ('Windfury (20.0%)', simulate_whole(True, True)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment