MMO News and theorycrafting for advanced MMO gamers. News and articles that relate to your gameplay. World of Warcraft, SWTOR, Guild Wars 2, Rift, TERA, Eve Online, Star Wars the Old Republic, Diablo3, The Secret World and all Western AAA MMOs

Your login from any MMO-Mechanics forum or site will work here.

Hello There, Guest! Register

Post Reply 
Combat, Watchman Simulators
04-07-2012, 01:06 PM (This post was last modified: 07-06-2012 08:14 AM by Qed.)
Post: #1
Combat, Watchman Simulators
Inspired by the tremendous amount of work done by others on these forums, I built my trooper DPS simulator (here) into a DPS simulator for Combat and Watchman specs. I post them here for anyone whose interested, and to catch the mistakes I'm sure to have made. Both of these scripts are written in python and should be poked and prodded until they give you the numbers you want.

It is fairly straightforward to calculate stat weights out of this. A script to do this automatically is included below as well.

Now, blocks of code:

Combat:

Code:
import math
import random

#buffs
smuggler = 1
knight = 1
consular = 1

import sys

#base stats
#str = 1505
#will = 140
#fpower = 1213
#power = 345
#crit = 192
#surge = 294
#acc = 147
#alac = 0
#weapondam = 450

str = (1912 )* (1 +.05*consular)
will = 140 * (1 +.05*consular)
fpower = 1261
power = (993 + 113)
crit = 164
surge = 342
acc = 228
alac = 1
mh_dam = 405
oh_dam = 405

str    = int(sys.argv[1]) + str
will   = int(sys.argv[2]) + will
fpower = int(sys.argv[3]) + fpower
power  = int(sys.argv[4]) + power
crit   = int(sys.argv[5]) + crit
surge  = int(sys.argv[6]) + surge
acc    = int(sys.argv[7]) + acc
alac   = int(sys.argv[8]) + alac
mh_dam = int(sys.argv[9]) + mh_dam
oh_dam = int(sys.argv[10])+ oh_dam


#skills

#static bonuses
bonus_acc = 0.07
bonus_crit = 0.01 +0.05*smuggler
bonus_alac = 0.00
bonus_surge = 0.01

def diminishing_returns(x,max,lam):
    return max*(1-(1-(0.01/max))**(x/(50*lam)))

#derived stats
#armor_dr = 0.8727
#armor_dr = 0.69
armor_dr = 0.65
mh_hit = min(.82+bonus_acc+diminishing_returns(acc,0.3,0.55) ,1)
oh_hit = min(.49+bonus_acc+diminishing_returns(acc,0.3,0.55) ,1)
mcrit = 0.05+bonus_crit+diminishing_returns(crit,0.3,0.45)+diminishing_returns(str,0.3,2​.5)
critvalue = 0.50+bonus_surge+diminishing_returns(surge,0.3,0.11)
haste = bonus_alac+diminishing_returns(alac,0.3,0.55)
bonusdmg = (str*0.2+power*0.23)*(1+0.05*knight)
bonusfdmg = (str*0.2+will*0.2+power*0.23+fpower*0.23)*(1+0.05*knight)

t = 0
focus = 0
tot_damage = 0
remove_armor = 0
centering = 0
zen = 0
ataru_cd = 0
ataru_oa_buff = 0
ataru_trance = 0
br_ataru_buff = 0
zealous_strike_cd = 0
precision_slash_cd = 0
master_strike_cd = 0
blade_storm_cd = 0
force_stasis_cd = 0
dispatch_cd = 0
valorous_call_cd = 0
proc_trinket_cd = 0

n_as = 0
n_br = 0
n_s = 0
n_zs = 0
n_ps = 0
n_ms = 0
n_bs = 0
n_fs = 0
n_z = 0
n_d = 0
n_vc = 0
n_pt = 0

d_as = 0
d_br = 0
d_s = 0
d_zs = 0
d_ps = 0
d_ms = 0
d_bs = 0
d_fs = 0
d_z = 0
d_d = 0
d_vc = 0

def try_proc_trinket(delay):
    global tot_damage
    global proc_trinket_cd
    global armor_dr
    global n_pt
    return
    if (proc_trinket_cd - delay) > 0: return
    thisdmg = 0
    mhdmg = (246)
    #mhdmg = (0)
    thisdmg = thisdmg + mhdmg
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    tot_damage = tot_damage + thisdmg
    proc_trinket_cd = 4.5+delay
    n_pt = n_pt + 1
    #print "Proccy Trinket: ", thisdmg ," , "


def ataru_strike(isfree, delay):
    global focus
    global tot_damage
    global remove_armor
    global ataru_cd
    global ataru_oa_buff
    global ataru_trance
    global n_as , d_as
    thisdmg = 0
    bdmg = (1610*0.038+bonusdmg*0.38+mh_dam*0.385)*1.3
    if random.uniform(0,1) < (mh_hit+0.1):
        if isfree < 1: ataru_cd = 1.5+delay
        #ataru_cd = 1.5+delay
        if ataru_trance < 0: ataru_trance = 6.0
        if (random.uniform < 0.3): ataru_oa_buff = 1
        thisdmg = bdmg
        if random.uniform(0,1) < (mcrit):
            thisdmg =  bdmg*(1+critvalue+0.3)
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    tot_damage = tot_damage + thisdmg
    n_as = n_as + 1
    d_as = d_as + thisdmg
    #print "Ataru Proc: ", thisdmg
    
def try_ataru(isfree = False, delay = 0):
    global br_ataru_buff
    global ataru_cd
    if (ataru_cd <= delay or isfree):
        if (random.uniform(0,1) < (0.2+0.3*br_ataru_buff) or isfree):
            ataru_strike(isfree, delay)

def blade_rush():
    global focus
    global tot_damage
    global passtime
    global remove_armor
    global centering
    global zen
    global br_ataru_buff
    global ataru_oa_buff
    global n_br , d_br
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.134+bonusdmg*1.34+mh_dam*0.89)
    ohdmg = (oh_dam*0.89*0.66)
    if random.uniform(0,1) < (mh_hit+0.1):
        nhits = nhits+1
        br_ataru_buff = 6.0
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue+0.3)
        thisdmg = thisdmg+mhdmg
    if random.uniform(0,1) < (oh_hit+0.1):
        nhits = nhits + 1
        br_ataru_buff = 6.0
        if random.uniform(0,1) < (mcrit):
            ohdmg =  ohdmg*(1+critvalue+0.3)
        thisdmg = thisdmg+ohdmg
    if ataru_oa_buff == 1:
        thisdmg = thisdmg*1.1
        ataru_oa_buff = 0
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    try_ataru(True)
    #try_ataru(False)
    if zen > 0:
        focus = focus - 1
        passtime = 1
        zen = zen - 1
        for n in xrange(nhits):
            swing_delay = random.uniform(0,1)*1/nhits+1*n/nhits
            try_ataru(False, swing_delay)
            if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)

    else:        
        focus = focus - 2
        passtime = 1.5
        centering = centering + 4
        for n in xrange(nhits):
            swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
            try_ataru(False, swing_delay)
            if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    br_ataru_buff = 6
    tot_damage = tot_damage + thisdmg
    n_br = n_br + 1
    d_br = d_br + thisdmg
    #print "Blade Rush: ", thisdmg ," , " , passtime , focus

        
def zealous_strike():
    global focus
    global tot_damage
    global passtime
    global remove_armor
    global centering
    global zen
    global zealous_strike_cd
    global n_zs , d_zs
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.1+bonusdmg*1+mh_dam*0.33)
    ohdmg = (oh_dam*0.67*0.66)
    if random.uniform(0,1) < (mh_hit+0.1):
        nhits = nhits+1
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    if random.uniform(0,1) < (oh_hit+0.1):
        nhits = nhits + 1
        if random.uniform(0,1) < (mcrit):
            ohdmg =  ohdmg*(1+critvalue)
        thisdmg = thisdmg+ohdmg
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    #try_ataru(False)
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
        try_ataru(False, swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    focus = focus + 6
    zealous_strike_cd = 11.5
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_zs = n_zs + 1
    d_zs = d_zs + thisdmg
    #print "Zealous Strike: ", thisdmg ," , " , passtime , focus
    

def strike():
    global focus
    global tot_damage
    global passtime
    global remove_armor
    global centering
    global zen
    global n_s , d_s
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.0+bonusdmg*1+mh_dam*1.01)
    ohdmg = (oh_dam*0.99*0.66)
    if random.uniform(0,1) < (mh_hit):
        nhits = nhits+1
    if random.uniform(0,1) < (mh_hit):
        nhits = nhits+1
    if random.uniform(0,1) < (mh_hit):
        nhits = nhits+1
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    if random.uniform(0,1) < (oh_hit):
        nhits = nhits + 1
        if random.uniform(0,1) < (mcrit):
            ohdmg =  ohdmg*(1+critvalue)
        thisdmg = thisdmg+ohdmg
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    #try_ataru(False)
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
        try_ataru(False, swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    focus = focus + 2
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_s = n_s + 1
    d_s = d_s + thisdmg
    #print "Strike: ", thisdmg ," , " , passtime , focus

def precision_slash():
    global focus
    global tot_damage
    global passtime
    global remove_armor
    global centering
    global zen
    global ataru_oa_buff
    global precision_slash_cd
    global n_ps , d_ps
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.137+bonusdmg*1.37+mh_dam*0.91)*(.53/.91)
    #ohdmg = (oh_dam*0.91*0.66)
    ohdmg = (oh_dam*0.57*0.66)
    remove_armor = 4.5
    if random.uniform(0,1) < (mh_hit):
        nhits = nhits+1
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    if random.uniform(0,1) < (oh_hit):
        nhits = nhits + 1
        if random.uniform(0,1) < (mcrit):
            ohdmg =  ohdmg*(1+critvalue)
        thisdmg = thisdmg+ohdmg
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    if ataru_oa_buff == 1:
        thisdmg = thisdmg*1.1
        ataru_oa_buff = 0
    #try_ataru(False)
    for n in xrange(nhits):
        swing_delay = 0
        try_ataru(False, swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    focus = focus - 3
    precision_slash_cd = 15
    if zen < 1: centering = centering + 4
    passtime = 0
    tot_damage = tot_damage + thisdmg
    n_ps = n_ps + 1
    d_ps = d_ps + thisdmg
    #print "Precision Slash: ", thisdmg ," , " , passtime , focus

def master_strike():
    global focus
    global tot_damage
    global passtime
    global remove_armor
    global centering
    global zen
    global master_strike_cd
    global n_ms , d_ms
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.417+bonusdmg*4.17+mh_dam*2.775)*1.08
    oh1dmg = (oh_dam*0.925*0.66)*1.08
    oh2dmg = (oh_dam*1.85)*1.08
    if random.uniform(0,1) < (mh_hit+0.1):
        nhits = nhits+1
    if random.uniform(0,1) < (mh_hit+0.1):
        nhits = nhits+1
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    if random.uniform(0,1) < (oh_hit+0.1):
        nhits = nhits + 1
        if random.uniform(0,1) < (mcrit):
            oh1dmg =  oh1dmg*(1+critvalue)
        thisdmg = thisdmg+oh1dmg
    if random.uniform(0,1) < (oh_hit+0.1):
        nhits = nhits + 1
        if random.uniform(0,1) < (mcrit):
            oh2dmg =  oh2dmg*(1+critvalue)
        thisdmg = thisdmg+oh2dmg
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    thisdmg = thisdmg*1.15
    #try_ataru(False)
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*3/nhits+3*n/nhits
        try_ataru(False, swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    master_strike_cd = 27
    passtime = 3
    tot_damage = tot_damage + thisdmg
    n_ms = n_ms + 1
    d_ms = d_ms + thisdmg
    #print "Master Strike: ", thisdmg ," , " , passtime , focus


def blade_storm():
    global focus
    global tot_damage
    global passtime
    global remove_armor
    global centering
    global zen
    global ataru_oa_buff
    global blade_storm_cd
    global n_bs , d_bs
    thisdmg = 0
    bonuscrit = 0
    if ataru_trance > 0: bonuscrit = 1
    mhdmg = (1610*0.187+bonusfdmg*1.87)
    if random.uniform(0,1) < (mh_hit+0.1):
        if random.uniform(0,1) < (mcrit+0.06+1*bonuscrit):
            mhdmg =  mhdmg*(1+critvalue+0.3)
        thisdmg = thisdmg+mhdmg
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    if ataru_oa_buff == 1:
        thisdmg = thisdmg*1.1
        ataru_oa_buff = 0
    focus = focus - 2
    blade_storm_cd = 9
    if zen < 1: centering = centering + 4
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_bs = n_bs + 1
    d_bs = d_bs + thisdmg
    #print "Blade Storm: ", thisdmg ," , " , passtime , focus

def force_stasis():
    global focus
    global tot_damage
    global passtime
    global remove_armor
    global force_stasis_cd
    global n_fs , d_fs
    thisdmg = 0
    mhdmg = (1610*0.272+bonusfdmg*2.72)
    if random.uniform(0,1) < (mh_hit+0.1):
        if random.uniform(0,1) < (mcrit+0.06):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    focus = focus +3
    force_stasis_cd = 50
    passtime = 3*(1-haste)
    tot_damage = tot_damage + thisdmg
    n_fs = n_fs + 1
    d_fs = d_fs + thisdmg
    #print "Force Stasis: ", thisdmg ," , " , passtime , focus

def dispatch():
    global focus
    global tot_damage
    global passtime
    global remove_armor
    global centering
    global zen
    global ataru_oa_buff
    global dispatch_cd
    global n_d , d_d
    thisdmg = 0
    mhdmg = (1610*0.285+bonusdmg*2.95+mh_dam*1.9)
    if random.uniform(0,1) < (mh_hit+0.1):
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    if remove_armor < 0: thisdmg = thisdmg*armor_dr
    if ataru_oa_buff == 1:
        thisdmg = thisdmg*1.1
        ataru_oa_buff = 0
    focus = focus - 1
    if zen < 1: centering = centering + 4
    swing_delay = random.uniform(0,1)*1.5
    try_ataru(False, swing_delay)
    if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    dispatch_cd = 6
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_d = n_d + 1
    d_d = d_d + thisdmg
    #print "Dispatch: ", thisdmg ," , " , passtime , focus

def zazen():
    global passtime
    global centering
    global zen
    global n_z
    centering = 0
    zen = 6
    passtime = 0
    n_z = n_z + 1
    #print "Invoking Zen: ", thisdmg ," , " , passtime , focus

def valorous_call():
    global passtime
    global centering
    global valorous_call_cd
    global n_vc
    centering = 30
    valorous_call_cd = 150
    passtime = 0
    n_vc = n_vc + 1
    #print "Valorous Call " , thisdmg , " , " , passtime , focus

for iteration in xrange(9999):
    fight_length = 300
    iteration_t = 0

    focus = 0
    remove_armor = 0
    centering = 0
    zen = 0
    ataru_cd = 0
    ataru_oa_buff = 0
    ataru_trance = 0
    br_ataru_buff = 0
    zealous_strike_cd = 0
    precision_slash_cd = 0
    master_strike_cd = 0
    blade_storm_cd = 0
    force_stasis_cd = 0
    dispatch_cd = 0
    valorous_call_cd = 0
    proc_trinket_cd = 0

    choose_ability = 0
    
    while iteration_t < fight_length:
        thisdmg = 0
        passtime = 0
    
        ######SIMPLE PRIORITY######
        #if centering >= 30: zazen()
        #elif zealous_strike_cd <= 0: zealous_strike()
        #elif br_ataru_buff <= 0 and focus >= 3: blade_rush()
        #elif precision_slash_cd <= 0 and focus >= 3: precision_slash()
        #elif ataru_trance > 0 and focus >= 2 and blade_storm_cd <= 0: blade_storm()
        #elif master_strike_cd <= 0: master_strike()
        #elif force_stasis_cd <= 0: force_stasis()
        #elif focus >= 3: blade_rush()
        #else: strike()
        ###END SIMPLE PRIORITY####
    
        ######ZEN BURN############
        #if zealous_strike_cd <= 0: zealous_strike()
        #elif centering >= 30:
        #    if br_ataru_buff <= 0 and focus >= 3: blade_rush()
        #    elif focus < 8: strike()
        #    else: zazen()
        #elif zen > 0:
        #    if br_ataru_buff <= 0 and focus >= 3: blade_rush()
        #    elif precision_slash_cd <= 0 and focus >= 3: precision_slash()
        #    elif focus >= 3: blade_rush()
        #    else: strike()
        #else:
        #    if br_ataru_buff <= 0 and focus >= 3: blade_rush()
        #    elif precision_slash_cd <= 0 and focus >= 3: precision_slash()
        #    elif ataru_trance > 0 and focus >= 2 and blade_storm_cd <= 0: blade_storm()
        #    elif master_strike_cd <= 0: master_strike()
        #    elif force_stasis_cd <= 0: force_stasis()
        #    elif focus >= 3: blade_rush()
        #    else: strike()
        ###END ZEN BURN###########
    
        ###PRECISE BURN########
        #if centering >= 30: zazen()
        #elif zealous_strike_cd <= 0: zealous_strike()
        #elif br_ataru_buff <= 0 and focus >= 3: blade_rush()
        #elif precision_slash_cd <= 0 and focus >= 3: precision_slash()
        #elif precision_slash_cd <= 3 and focus <= 5: strike()
        #elif ataru_trance > 0 and focus >= 2 and blade_storm_cd <= 0: blade_storm()
        #elif master_strike_cd <= 0: master_strike()
        #elif force_stasis_cd <= 0: force_stasis()
        #elif focus >= 3: blade_rush()
        #else: strike()
        ##END PRECISE BURN#####
    
        ######PRECISE ZEN BURN############
        if zealous_strike_cd <= 0: zealous_strike()
        elif centering <= 5 and valorous_call_cd <= 0: valorous_call()
        elif centering >= 30: zazen()
        elif zen > 0:
            if br_ataru_buff <= 0 and focus >= 3: blade_rush()
            elif precision_slash_cd <= 0 and focus >= 3: precision_slash()
            elif iteration_t > fight_length*0.70 and focus >= 2 and dispatch_cd <=0: dispatch()
            elif focus >= 3: blade_rush()
            else: strike()
        else:
            if br_ataru_buff <= 0 and focus >= 3: blade_rush()
            elif precision_slash_cd <= 0 and focus >= 3: precision_slash()
            elif iteration_t > fight_length*0.70 and focus >= 2 and dispatch_cd <=0: dispatch()
            elif ataru_trance > 0 and focus >= 2 and blade_storm_cd <= 0: blade_storm()
            elif master_strike_cd <= 0: master_strike()
            elif force_stasis_cd <= 0: force_stasis()
            elif focus >= 7: blade_rush()
            else: strike()
        ###END PRECISE ZEN BURN###########


        #########ATARU TEST####################
        #if choose_ability == 0: strike()
        #elif choose_ability == 1: blade_rush()
        #choose_ability = 1 - choose_ability
        ####END ATARU TEST#####################
    
        t = t+passtime
        iteration_t = iteration_t + passtime
        zealous_strike_cd = zealous_strike_cd - passtime
        precision_slash_cd = precision_slash_cd - passtime
        master_strike_cd = master_strike_cd - passtime
        blade_storm_cd = blade_storm_cd - passtime
        force_stasis_cd = force_stasis_cd - passtime
        remove_armor = remove_armor - passtime
        ataru_cd = ataru_cd - passtime
        dispatch_cd = dispatch_cd - passtime
        valorous_call_cd = valorous_call_cd - passtime
        if ataru_trance > 0 and (ataru_trance - passtime) <= 0:
            focue = focus +1
        ataru_trance = ataru_trance - passtime
        br_ataru_buff = br_ataru_buff - passtime
        proc_trinket_cd = proc_trinket_cd - passtime
        tot_damage = tot_damage + thisdmg

    
print "Total Time: ", t , " seconds"
print "Total Damage: " , tot_damage
print "DPS: " , tot_damage/t
print ""
if n_as > 0: print "Ataru ECD: " , t/n_as , " : " , 100*d_as/tot_damage
if n_br > 0: print "Blade Rush ECD: " , t/n_br , " : " , 100*d_br/tot_damage
if n_zs > 0: print "Zealous Strike ECD: " , t/n_zs , " : " , 100*d_zs/tot_damage
if n_s  > 0: print "Strike ECD: ", t/n_s , " : " , 100*d_s/tot_damage
if n_ps > 0: print "Precision Stike ECD: " , t/n_ps , " : " , 100*d_ps/tot_damage
if n_z  > 0: print "Zen ECD: " , t/n_z
if n_d  > 0: print "Dispatch ECD: " , t/n_d , " : " , 100*d_d/tot_damage
if n_bs > 0: print "Blade Storm ECD: ", t/n_bs , " : " , 100*d_bs/tot_damage
if n_ms > 0: print "Master Strike ECD: ", t/n_ms , " : " , 100*d_ms/tot_damage
if n_fs > 0: print "Force Stasis ECD: ", t/n_fs , " : " , 100*d_fs/tot_damage
if n_vc > 0: print "Valorous Call ECD: ", t/n_vc
if n_pt > 0: print "Proccy Trinket ECD: ", t/n_pt

#import os
#os.environ['PYTHONINSPECT'] = '1'

Watchman

Code:
import math
import random
import array
from array import array

#buffs
smuggler = 1
knight = 1
consular = 0

import sys

#base stats
#str = 1505
#will = 140
#fpower = 1213
#power = 345
#crit = 192
#surge = 294
#acc = 147
#alac = 0
#weapondam = 450

str = 1922 * (1 +.05*consular)
will = 140 * (1 +.05*consular)
fpower = 1261
power = 944
crit = 119
surge = 342
acc = 228
alac = 1
mh_dam = 405
oh_dam = 405

str    = int(sys.argv[1]) + str
will   = int(sys.argv[2]) + will
fpower = int(sys.argv[3]) + fpower
power  = int(sys.argv[4]) + power
crit   = int(sys.argv[5]) + crit
surge  = int(sys.argv[6]) + surge
acc    = int(sys.argv[7]) + acc
alac   = int(sys.argv[8]) + alac
mh_dam = int(sys.argv[9]) + mh_dam
oh_dam = int(sys.argv[10])+ oh_dam


#skills

#static bonuses
bonus_acc = 0.01
bonus_crit = 0.01 +0.05*smuggler
bonus_alac = 0.00
bonus_surge = 0.01

def diminishing_returns(x,max,lam):
    return max*(1-(1-(0.01/max))**(x/(50*lam)))

#derived stats
#armor_dr = 0.8727
#armor_dr = 0.69
armor_dr = 0.65
mh_hit = min(.82+bonus_acc+diminishing_returns(acc,0.3,0.55) ,1)
oh_hit = min(.49+bonus_acc+diminishing_returns(acc,0.3,0.55) ,1)
mcrit = 0.05+bonus_crit+diminishing_returns(crit,0.3,0.45)+diminishing_returns(str,0.3,2​.5)
critvalue = 0.50+bonus_surge+diminishing_returns(surge,0.3,0.11)
haste = bonus_alac+diminishing_returns(alac,0.3,0.55)
bonusdmg = (str*0.2+power*0.23)*(1+0.05*knight)
bonusfdmg = (str*0.2+will*0.2+power*0.23+fpower*0.23)*(1+0.05*knight)

t = 0

cauterize_tick_cd_array = array('f',[-1,-1,-1,-1,-1,-1])
overload_saber_tick_cd_array = array('f',[-1,-1,-1])

focus = 0
juyo_stacks = 0
juyo_cd = 0
tot_damage = 0
remove_armor = 0
centering = 0
zen = 0
zealous_strike_cd = 0
master_strike_cd = 0
force_stasis_cd = 0
force_leap_cd = 0
dispatch_cd = 0
valorous_call_cd = 0
overload_saber_cd = 0
overload_saber_buff_stacks = 0
overload_saber_dot_stacks = 0
overload_saber_apply_cd = 0
merciless_slash_cd = 0
merciless_buff = 0
merciless_buff_cd = 0
burning_focus_cd = 0
mind_sear_cd = 0
cauterize_cd = 0
proc_trinket_cd = 0

n_st = 0
n_zs = 0
n_ms = 0
n_me = 0
n_fs = 0
n_fl = 0
n_z = 0
n_d = 0
n_vc = 0
n_ca = 0
n_sl = 0
n_os = 0
n_pt = 0

def try_proc_trinket(delay):
    global tot_damage
    global juyo_stacks
    global proc_trinket_cd
    global n_pt
    if (proc_trinket_cd - delay) > 0: return
    thisdmg = 0
    mhdmg = (246)
    #mhdmg = (0)
    thisdmg = thisdmg + mhdmg
    thisdmg = thisdmg*armor_dr*(1+0.02*juyo_stacks)
    tot_damage = tot_damage + thisdmg
    proc_trinket_cd = 4.5+delay
    n_pt = n_pt + 1
    #print "Proccy Trinket: ", thisdmg ," , "
    

def try_overload_saber(delay):
    global overload_saber_buff_stacks
    global overload_saber_dot_stacks
    global overload_saber_tick_cd_array
    global overload_saber_apply_cd
    if overload_saber_apply_cd <= 0 and overload_saber_buff_stacks > 0 and overload_saber_dot_stacks < 3:
        overload_saber_tick_cd_array = array('f',[0.01+delay,3.01+delay,6.01+delay])
        overload_saber_dot_stacks = overload_saber_dot_stacks + 1
        overload_saber_buff_stacks = overload_saber_buff_stacks - 1
        overload_saber_apply_cd = 1.5+delay
        #print "Applied Overload Saber"

def overload_saber():
    global focus
    global passtime
    global overload_saber_buff_stacks
    global overload_saber_cd
    global n_os
    passtime = 0
    overload_saber_buff_stacks = 3
    overload_saber_cd = 12
    focus = focus - 3
    n_os = n_os + 1
    #print "Overload Saber"

def overload_saber_tick():
    global focus
    global tot_damage
    global juyo_stacks
    global centering
    global zen
    global burning_focus_cd
    global overload_saber_dot_stacks
    thisdmg = 0
    bonuscrit = 0
    if zen > 0: bonuscrit = 1
    mhdmg = (1610*0.02+bonusfdmg*0.2)*1.15
    if random.uniform(0,1) < (mcrit + 0.06 + 0.01*juyo_stacks + bonuscrit):
        mhdmg =  mhdmg*(1+critvalue+0.3)
    thisdmg = thisdmg+mhdmg*overload_saber_dot_stacks
    thisdmg = thisdmg*(1+0.02*juyo_stacks)
    if burning_focus_cd <= 0 and random.uniform(0,1) < 0.3: focus = focus + 1
    if zen > 0: zen = zen - 1
    tot_damage = tot_damage + thisdmg
    #print "Overload Saber Tick: ", thisdmg
    
def merciless_slash():
    global focus
    global tot_damage
    global passtime
    global juyo_cd
    global juyo_stacks
    global centering
    global zen
    global merciless_slash_cd
    global merciless_buff
    global merciless_buff_cd
    global mind_sear_cd
    global cauterize_cd
    global n_me
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.285+bonusdmg*2.85+mh_dam*1.9)
    ohdmg = (oh_dam*1.9*0.66)
    if random.uniform(0,1) < (mh_hit+0.1):
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
        nhits = nhits + 1
    if random.uniform(0,1) < (oh_hit+0.1):
        if random.uniform(0,1) < (mcrit):
            ohdmg =  ohdmg*(1+critvalue)
        thisdmg = thisdmg+ohdmg
        nhits = nhits + 1
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
        try_overload_saber(swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    thisdmg = thisdmg*armor_dr*(1+0.02*juyo_stacks)
    focus = focus - 4
    if juyo_stacks < 5 and juyo_cd <= 0: juyo_stacks = juyo_stacks+1
    if zen < 1: centering = centering + 4
    merciless_slash_cd = 12-1.5*merciless_buff
    if merciless_buff < 3: merciless_buff = merciless_buff+1
    merciless_buff_cd = 15
    if mind_sear_cd <= 0 and random.uniform(0,1) < 0.66: cauterize_cd = 0
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_me = n_me + 1
    #print "Merciless Slash: ", thisdmg ," , " , passtime , focus

def slash():
    global focus
    global tot_damage
    global passtime
    global juyo_cd
    global juyo_stacks
    global centering
    global zen
    global mind_sear_cd
    global cauterize_cd
    global n_sl
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.153+bonusdmg*1.53+mh_dam*1.02)
    ohdmg = (oh_dam*1.02*0.66)
    if random.uniform(0,1) < (mh_hit+0.1):
        if random.uniform(0,1) < (mcrit+0.15):
            mhdmg =  mhdmg*(1+critvalue)
        nhits = nhits + 1
        thisdmg = thisdmg+mhdmg
    if random.uniform(0,1) < (oh_hit+0.1):
        if random.uniform(0,1) < (mcrit+0.15):
            ohdmg =  ohdmg*(1+critvalue)
        nhits = nhits + 1
        thisdmg = thisdmg+ohdmg
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
        try_overload_saber(swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)        
    thisdmg = thisdmg*armor_dr*(1+0.02*juyo_stacks)
    focus = focus - 2
    if juyo_stacks < 5 and juyo_cd <= 0: juyo_stacks = juyo_stacks+1
    if zen < 1: centering = centering + 4
    if mind_sear_cd <= 0 and random.uniform(0,1) < 0.33: cauterize_cd = 0
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_sl = n_sl + 1
    #print "Slash: ", thisdmg ," , " , passtime , focus

def cauterize():
    global focus
    global tot_damage
    global passtime
    global juyo_stacks
    global juyo_cd
    global centering
    global zen
    global cauterize_tick_cd_array
    global cauterize_cd
    global n_ca
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.06+bonusdmg*0.6+mh_dam*0.4)*1.3
    ohdmg = (oh_dam*0.6*0.66)*1.3
    if random.uniform(0,1) < (mh_hit+0.1):
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
        nhits = nhits + 1
    if random.uniform(0,1) < (oh_hit+0.1):
        if random.uniform(0,1) < (mcrit):
            ohdmg =  ohdmg*(1+critvalue)
        thisdmg = thisdmg+ohdmg
        nhits = nhits + 1
    cauterize_tick_cd_array = array('f',[1,2,3,4,5,6])
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
        try_overload_saber(swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    thisdmg = thisdmg*armor_dr*(1+0.02*juyo_stacks)
    focus = focus - 2
    if juyo_stacks < 5 and juyo_cd <= 0: juyo_stacks = juyo_stacks+1
    if zen < 1: centering = centering + 4
    cauterize_cd = 15
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_ca = n_ca + 1
    #print "Cauterize Applied: ", thisdmg ," , " , passtime , focus

def cauterize_tick():
    global focus
    global tot_damage
    global juyo_stacks
    global centering
    global zen
    global burning_focus_cd
    thisdmg = 0
    bonuscrit = 0
    if zen > 0: bonuscrit = 1
    mhdmg = (1610*0.02+bonusfdmg*0.2)*1.15
    if random.uniform(0,1) < (mcrit + 0.06 + 0.01*juyo_stacks + bonuscrit):
        mhdmg =  mhdmg*(1+critvalue+0.3)
    thisdmg = thisdmg+mhdmg
    thisdmg = thisdmg*(1+0.02*juyo_stacks)
    if burning_focus_cd <= 0 and random.uniform(0,1) < 0.3: focus = focus + 1
    if zen > 0: zen = zen - 1
    tot_damage = tot_damage + thisdmg
    #print "Cauterize Tick: ", thisdmg
    

def zealous_strike():
    global focus
    global tot_damage
    global passtime
    global juyo_stacks
    global juyo_cd
    global centering
    global zen
    global zealous_strike_cd
    global n_zs
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.1+bonusdmg*1+mh_dam*0.33)
    ohdmg = (oh_dam*0.67*0.66)
    if random.uniform(0,1) < (mh_hit+0.1):
        nhits = nhits+1
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    if random.uniform(0,1) < (oh_hit+0.1):
        nhits = nhits + 1
        if random.uniform(0,1) < (mcrit):
            ohdmg =  ohdmg*(1+critvalue)
        thisdmg = thisdmg+ohdmg
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
        try_overload_saber(swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    thisdmg = thisdmg*armor_dr*(1+0.02*juyo_stacks)
    focus = focus + 6
    if juyo_stacks < 5 and juyo_cd <= 0: juyo_stacks = juyo_stacks+1
    zealous_strike_cd = 15
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_zs = n_zs + 1
    #print "Zealous Strike: ", thisdmg ," , " , passtime , focus
    
def strike():
    global focus
    global tot_damage
    global passtime
    global juyo_stacks
    global juyo_cd
    global centering
    global zen
    global n_st
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.0+bonusdmg*1+mh_dam*1.01)
    ohdmg = (oh_dam*0.99*0.66)
    if random.uniform(0,1) < (mh_hit):
        nhits = nhits+1
    if random.uniform(0,1) < (mh_hit):
        nhits = nhits+1
    if random.uniform(0,1) < (mh_hit):
        nhits = nhits+1
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    if random.uniform(0,1) < (oh_hit):
        nhits = nhits + 1
        if random.uniform(0,1) < (mcrit):
            ohdmg =  ohdmg*(1+critvalue)
        thisdmg = thisdmg+ohdmg
    thisdmg = thisdmg*armor_dr*(1+0.02*juyo_stacks)
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
        try_overload_saber(swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    focus = focus + 2
    if juyo_stacks < 5 and juyo_cd <= 0: juyo_stacks = juyo_stacks+1
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_st = n_st + 1
    #print "Strike: ", thisdmg ," , " , passtime , focus

def master_strike():
    global focus
    global tot_damage
    global passtime
    global juyo_stacks
    global juyo_cd
    global centering
    global zen
    global master_strike_cd
    global n_ms
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.417+bonusdmg*4.17+mh_dam*2.775)*1.08
    oh1dmg = (oh_dam*0.925*0.66)*1.08
    oh2dmg = (oh_dam*1.85)*1.08
    if random.uniform(0,1) < (mh_hit+0.1):
        nhits = nhits+1
    if random.uniform(0,1) < (mh_hit+0.1):
        nhits = nhits+1
        if random.uniform(0,1) < (mcrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    if random.uniform(0,1) < (oh_hit+0.1):
        nhits = nhits + 1
        if random.uniform(0,1) < (mcrit):
            oh1dmg =  oh1dmg*(1+critvalue)
        thisdmg = thisdmg+oh1dmg
    if random.uniform(0,1) < (oh_hit+0.1):
        nhits = nhits + 1
        if random.uniform(0,1) < (mcrit):
            oh2dmg =  oh2dmg*(1+critvalue)
        thisdmg = thisdmg+oh2dmg
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*3/nhits+3*n/nhits
        try_overload_saber(swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    thisdmg = thisdmg*armor_dr*(1+0.02*juyo_stacks)
    if juyo_stacks < 5 and juyo_cd <= 0: juyo_stacks = juyo_stacks+1
    master_strike_cd = 27
    passtime = 3
    tot_damage = tot_damage + thisdmg
    n_ms = n_ms + 1
    #print "Master Strike: ", thisdmg ," , " , passtime , focus

def force_stasis():
    global focus
    global tot_damage    
    global passtime
    global force_stasis_cd
    global n_fs
    thisdmg = 0
    mhdmg = (1610*0.272+bonusfdmg*2.72)
    if random.uniform(0,1) < (mh_hit+0.1):
        if random.uniform(0,1) < (mcrit+0.06):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
    thisdmg = thisdmg*armor_dr*(1+0.02*juyo_stacks)
    if random.uniform (0,1) < 0.3: try_proc_trinket(0)
    focus = focus +3
    force_stasis_cd = 50
    passtime = 3*(1-haste)
    tot_damage = tot_damage + thisdmg
    n_fs = n_fs + 1
    #print "Force Stasis: ", thisdmg ," , " , passtime , focus

def force_leap():
    global focus
    global tot_damage
    global passtime
    global juyo_stacks
    global juyo_cd
    global force_leap_cd
    global n_fl
    thisdmg = 0
    nhits = 0
    mhdmg = (1610*0.091+bonusdmg*.91+mh_dam*0.61)
    if random.uniform(0,1) < (mh_hit+0.1):
        if random.uniform(0,1) < (mcrit+0.06):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
        nhits = nhits + 1
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
        try_overload_saber(swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    if juyo_stacks < 5 and juyo_cd <= 0: juyo_stacks = juyo_stacks+1
    thisdmg = thisdmg*armor_dr*(1+0.02*juyo_stacks)
    focus = focus + 4
    force_leap_cd = 12
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_fl = n_fl + 1
    #print "Force Leap: ", thisdmg ," , " , passtime , focus


def dispatch():
    global focus
    global tot_damage
    global passtime
    global juyo_stacks
    global juyo_cd
    global centering
    global zen
    global dispatch_cd
    global n_d
    nhits = 0
    thisdmg = 0
    bonuscrit = 0
    mhdmg = (1610*0.285+bonusdmg*2.95+mh_dam*1.9)
    if random.uniform(0,1) < (mh_hit+0.1):
        if random.uniform(0,1) < (mcrit+0.06+1*bonuscrit):
            mhdmg =  mhdmg*(1+critvalue)
        thisdmg = thisdmg+mhdmg
        nhits = nhits + 1
    for n in xrange(nhits):
        swing_delay = random.uniform(0,1)*1.5/nhits+1.5*n/nhits
        try_overload_saber(swing_delay)
        if random.uniform (0,1) < 0.3: try_proc_trinket(swing_delay)
    focus = focus - 1
    if juyo_stacks < 5 and juyo_cd <= 0: juyo_stacks = juyo_stacks+1
    if zen < 1: centering = centering + 4
    dispatch_cd = 6
    passtime = 1.5
    tot_damage = tot_damage + thisdmg
    n_d = n_d + 1
    #print "Dispatch: ", thisdmg ," , " , passtime , focus

def zazen():
    global passtime
    global centering
    global zen
    global n_z
    centering = 0
    zen = 6
    passtime = 0
    n_z = n_z + 1
    #print "Invoking Zen: ", thisdmg ," , " , passtime , focus

def valorous_call():
    global passtime
    global centering
    global valorous_call_cd
    global n_vc
    centering = 30
    valorous_call_cd = 150
    passtime = 0
    n_vc = n_vc + 1
    #print "Valorous Call " , thisdmg , " , " , passtime , focus

for iteration in xrange(30000):
    fight_length = 300
    iteration_t = 0

    focus = 0
    remove_armor = 0
    centering = 0
    zen = 0
    zealous_strike_cd = 0
    precision_slash_cd = 0
    master_strike_cd = 0
    blade_storm_cd = 0
    force_stasis_cd = 0
    dispatch_cd = 0
    valorous_call_cd = 0
    overload_saber_cd = 0
    overload_saber_buff_stacks = 0
    overload_saber_dot_stacks = 0
    overload_saber_apply_cd = 0
    merciless_slash_cd = 0
    merciless_buff = 0
    burning_focus_cd = 0
    mind_sear_cd = 0
    cauterize_cd = 0

    cauterize_tick_cd_array = array('f',[-1,-1,-1,-1,-1,-1])
    overload_saber_tick_cd_array = array('f',[-1,-1,-1])
    
    while iteration_t < fight_length:
        thisdmg = 0
        passtime = 0

        ######MASH BUTTANS######
        #if cauterize_cd <= 0 and focus >= 2: cauterize()
        #elif merciless_slash_cd <=0 and focus >= 5: merciless_slash()
        #else: strike()
        ###END MASH BUTTANS#####
    
        ######SIMPLE PRIORITY######
        #if centering >= 30: zazen()
        #elif centering <= 5 and valorous_call_cd <= 0: valorous_call()
        #elif cauterize_cd <= 0 and focus >= 2: cauterize()
        #elif overload_saber_cd <=0 and focus >= 3: overload_saber()
        #elif zealous_strike_cd <= 0: zealous_strike()
        #elif iteration_t > fight_length*0.7 and focus >= 7 and dispatch_cd <=0: dispatch()
        #elif merciless_slash_cd <=0 and focus >= 5: merciless_slash()
        #elif master_strike_cd <= 0: master_strike()
        #elif force_leap_cd <= 0: force_leap()
        #elif force_stasis_cd <= 0: force_stasis()
        #elif focus > 5: slash()
        #else: strike()
        ###END SIMPLE PRIORITY####


        ######HAND-OPTIMIZE######
        if centering >= 30: zazen()
        elif centering <= 5 and valorous_call_cd <= 0: valorous_call()
        elif merciless_slash_cd <=0 and focus >= 5 and merciless_buff_cd <= 2: merciless_slash()
        elif overload_saber_cd <=0 and focus >= 3: overload_saber()
        elif cauterize_cd <= 0 and focus >= 2: cauterize()
        elif zealous_strike_cd <= 0 and focus <= 6: zealous_strike()
        elif force_leap_cd <= 0 and focus <= 8: force_leap()
        elif iteration_t > fight_length*0.7 and focus >= 2 and dispatch_cd <=0: dispatch()
        elif merciless_slash_cd <=0 and focus >= 5: merciless_slash()        
        elif master_strike_cd <= 0 and merciless_buff_cd >= 4: master_strike()
        #elif force_stasis_cd <= 0 and merciless_buff_cd >= 4: force_stasis()
        elif focus > 8: slash()
        else: strike()
        ###END HAND-OPTIMIZE####

        t = t+passtime
        iteration_t = iteration_t + passtime
        zealous_strike_cd = zealous_strike_cd - passtime
        precision_slash_cd = precision_slash_cd - passtime
        master_strike_cd = master_strike_cd - passtime
        blade_storm_cd = blade_storm_cd - passtime
        force_stasis_cd = force_stasis_cd - passtime
        force_leap_cd = force_leap_cd - passtime
        remove_armor = remove_armor - passtime
        dispatch_cd = dispatch_cd - passtime
        valorous_call_cd = valorous_call_cd - passtime
        tot_damage = tot_damage + thisdmg
        juyo_cd = juyo_cd - passtime
        overload_saber_cd = overload_saber_cd - passtime
        overload_saber_apply_cd = overload_saber_apply_cd - passtime
        merciless_slash_cd = merciless_slash_cd - passtime
        burning_focus_cd = burning_focus_cd - passtime
        mind_sear_cd = mind_sear_cd - passtime
        cauterize_cd = cauterize_cd - passtime
        proc_trinket_cd = proc_trinket_cd - passtime
        merciless_buff_cd = merciless_buff_cd - passtime
        if merciless_buff_cd <= 0:
            merciless_buff = 0
            #print "Merciless Fell Off"
        if focus > 12: focus = 12

        #print focus, centering , zen

        for n in xrange(len(cauterize_tick_cd_array)):
            if cauterize_tick_cd_array[n] > 0 and cauterize_tick_cd_array[n] - passtime <= 0: cauterize_tick()
            cauterize_tick_cd_array[n] = cauterize_tick_cd_array[n] - passtime
        for n in xrange(len(overload_saber_tick_cd_array)):
            if overload_saber_tick_cd_array[n] > 0 and overload_saber_tick_cd_array[n] - passtime <= 0:
                overload_saber_tick()
                if n == len(overload_saber_tick_cd_array) - 1: overload_saber_dot_stacks = 0
            overload_saber_tick_cd_array[n] = overload_saber_tick_cd_array[n] - passtime
            

    
print "Total Time: ", t , " seconds"
print "Total Damage: " , tot_damage
print "DPS: " , tot_damage/t
print ""
if n_st > 0: print "Strike ECD: " , t/n_st
if n_zs > 0: print "Zealous Strike ECD:" , t/n_zs
if n_ms > 0: print "Master Strike ECD:" , t/n_ms
if n_me > 0: print "Merciless Slash ECD", t/n_me
if n_fs > 0: print "Force Stasis ECD:" , t/n_fs
if n_fl > 0: print "Force Leap ECD:" , t/n_fl
if n_z  > 0: print "Zen ECD:" , t/n_z
if n_d  > 0: print "Dispatch ECD:" , t/n_d
if n_vc > 0: print "Valorous Call ECD" , t/n_vc
if n_ca > 0: print "Cauterize ECD:" , t/n_ca
if n_sl > 0: print "Slash ECD:" , t/n_sl
if n_os > 0: print "Overload Saber ECD" , t/n_os
if n_vc > 0: print "Valorous Call ECD: ", t/n_vc
if n_pt > 0: print "Proccy Trinket ECD: ", t/n_pt
print ""
print ""

#import os
#os.environ['PYTHONINSPECT'] = '1'

One of the nicer things you can do with these macros is trivially implement any rotation you like, eg:

Code:
######MASH BUTTANS######
        if cauterize_cd <= 0 and focus >= 2: cauterize()
        elif merciless_slash_cd <=0 and focus >= 5: merciless_slash()
        else: strike()
        ###END MASH BUTTANS#####

or

Code:
######PRECISE ZEN BURN############
        if zealous_strike_cd <= 0: zealous_strike()
        elif centering <= 5 and valorous_call_cd <= 0: valorous_call()
        elif centering >= 30: zazen()
        elif zen > 0:
            if br_ataru_buff <= 0 and focus >= 3: blade_rush()
            elif precision_slash_cd <= 0 and focus >= 3: precision_slash()
            elif iteration_t > fight_length*0.8 and focus >= 2 and dispatch_cd <=0: dispatch()
            elif focus >= 3: blade_rush()
            else: strike()
        elif centering >= 24:
            if br_ataru_buff <= 0 and focus >= 3: blade_rush()
            elif precision_slash_cd <= 0 and focus >= 3: precision_slash()
            elif precision_slash_cd <= 3 and focus <= 5: strike()
            elif iteration_t > fight_length*0.8 and focus >= 7 and dispatch_cd <=0: dispatch()
            elif ataru_trance > 0 and focus >= 7 and blade_storm_cd <= 0: blade_storm()
            elif master_strike_cd <= 0: master_strike()
            elif force_stasis_cd <= 0: force_stasis()
            else: strike()
        else:
            if br_ataru_buff <= 0 and focus >= 3: blade_rush()
            elif precision_slash_cd <= 0 and focus >= 3: precision_slash()
            elif precision_slash_cd <= 3 and focus <= 5: strike()
            elif iteration_t > fight_length*0.8 and focus >= 2 and dispatch_cd <=0: dispatch()
            elif ataru_trance > 0 and focus >= 2 and blade_storm_cd <= 0: blade_storm()
            elif master_strike_cd <= 0: master_strike()
            elif force_stasis_cd <= 0: force_stasis()
            elif focus >= 3: blade_rush()
            else: strike()
        ###END PRECISE ZEN BURN###########


The rotations will let you break the games rules (not enough focus, off of cooldown), but should still track everything properly even if you do. The big exception to this is that you should only use one ability per iteration of the loop. You can get away with doing more than this, but you will very clearly break the results.

Anyway, enjoy!

For those that don't want to run this themselves, the DPS of the two specs using a simple rotation for each and the same gear is:

Combat:
Code:
python combat_cl.py 0 0 0 0 0 0 0 0 0 0
Total Time:  300978.985374  seconds
Total Damage:  450784232.403
DPS:  1497.72659989

Ataru ECD:  1.35264766854
Blade Rush ECD:  3.87589802681
Zealous Strike ECD:  12.039159415
Strike ECD:  9.27716257356
Precision Stike ECD:  16.4974230089
Zen ECD:  60.1957970748
Blade Storm ECD:  12.3549519878
Master Strike ECD:  28.3061210735
Force Stasis ECD:  50.163164229

python weights.py
acc :  0.288960221211 1502.22246814
alac :  0.0710837357245 1498.34566969
baseline :  0.0 1497.08083693
power :  1.0 1514.87439754
surge :  0.420568725621 1504.56425204
str :  0.980003381122 1514.51858649
error :  0.00570366000512 1497.18232535
crit :  0.75024980006 1510.43045222

Watchman:
Code:
python watchman_cl.py 0 0 0 0 0 0 0 0 0 0
Total Time:  301125.066877  seconds
Total Damage:  461133823.103
DPS:  1531.36976568

Strike ECD:  7.44898124617
Zealous Strike ECD: 12.1534111021
Merciless Slash ECD: 7.3411118476
Force Stasis ECD: 50.1958771256
Zen ECD: 25.1755761957
Dispatch ECD: 64.2331627296
Valorous Call ECD 150.562533438
Cauterize ECD: 8.63342030668
Slash ECD: 9.27994905472
Overload Saber ECD 12.9627665466

python weights.py
acc :  0.746977573632 1545.57999733
alac :  0.00377735501129 1533.52811283
baseline :  0.0 1533.46685848
power :  1.0 1549.68305998
surge :  0.459749576373 1540.92225025
str :  0.956113551623 1548.97138849
error :  -0.137802682089 1531.23222242
crit :  0.773390920186 1546.00832148

(n.b. The listed Watchman spec has a significantly higher return on accuracy because it isn't yet at the acc cap, while the combat buffs push it over that cap)

Clearly, one can be smarter about these rotations, eg, by conserving focus as Precision Strike is nearing its cooldown.
Find all posts by this user
Quote this message in a reply
04-19-2012, 04:03 AM (This post was last modified: 04-19-2012 04:06 AM by Qed.)
Post: #2
RE: Combat, Watchman Simulators
I've updated the OP to 1.2.

Combat dps can vary by 100 or more depending on how we model Ataru strike. At the moment, I am assuming that each ability (not each hit) gets one chance to proc Ataru if at least one swing from that ability hits, and the Ataru attack is attempted at a random time during that ability's GCD. This brings my Ataru ECD closest to what I have observed in combat logs, but I would be very happy to play around with this model given more evidence. Much better to be bad at my class and able to improve my Ataru rate then to just be capped out.

(Allowing the Ataru strike to proc at the same moment in every ability lets it proc more often, because you always get an attempt immediately after the cooldown.

Also, for those that would like to calculate stat weights using this simulator, you can use this

weights.py
Code:
import os
os.system("python combat_cl.py 0 0 0 0 0 0 0 0 0 0 > baseline.txt")
os.system("python combat_cl.py 0 0 0 0 0 0 0 0 0 0 > error.txt")
os.system("python combat_cl.py 48 0 0 0 0 0 0 0 0 0 > str.txt")
os.system("python combat_cl.py 0 0 0 48 0 0 0 0 0 0 > power.txt")
os.system("python combat_cl.py 0 0 0 0 48 0 0 0 0 0 > crit.txt")
os.system("python combat_cl.py 0 0 0 0 0 48 0 0 0 0 > surge.txt")
os.system("python combat_cl.py 0 0 0 0 0 0 48 0 0 0 > acc.txt")
os.system("python combat_cl.py 0 0 0 0 0 0 0 48 0 0 > alac.txt")

dps = {}

for stat in ["baseline","error","str","power","crit","surge","acc","alac"]:
    file = open(stat+".txt")
    for l in file:
        if l.find("DPS:") > -1:
            words = l.split()
            dps[stat] = float(words[1])
    file.close()

weight = {}

for stat in dps.keys():
    weight[stat] = (dps[stat]-dps["baseline"])/(dps["power"]-dps["baseline"])

for stat in weight.keys():
    print stat , ": " , weight[stat] , dps[stat]
Find all posts by this user
Quote this message in a reply
04-20-2012, 03:22 AM (This post was last modified: 04-20-2012 04:08 AM by Qed.)
Post: #3
RE: Combat, Watchman Simulators
Tested the one-ataru-per-ability theory, and it's untrue. Master strike can, and often does, proc twice if the blade rush buff is up and it's used in isolation. The one-ataru-per-swing model comes very close to properly describing the ataru proc rate in a simple rotation of strike>blade rush>strike>blade rush>...

I get the feeling that I'm not modeling opportune attack properly though. Logs show surprisingly few opportune attack procs, even when there are many ataru procs. I'll put a counter it in the simulator and compare procrates with the simple rotation when I get more time.

Edit: Sigh, hoodwinked by the description of opportune attack. Pretty sure the correct number is 30% from Marauders.
Find all posts by this user
Quote this message in a reply
04-28-2012, 03:33 PM
Post: #4
RE: Combat, Watchman Simulators
If only I knew how to use python. Sad
Find all posts by this user
Quote this message in a reply
04-28-2012, 04:52 PM
Post: #5
RE: Combat, Watchman Simulators
(04-28-2012 03:33 PM)Ryfe Wrote:  If only I knew how to use python. Sad

Well, if you want a good excuse to learn...

For me, it's much easier to pick apart code than a spreadsheet. Things have helpful names like cauterize_cd, instead of just cell numbers. Plus, it makes it really easy to test things like 'how does the game model ataru procs'. I can just run the same simple rotation ingame and out of game and tweak the sim parameters until things agree.

You should be able to get the above up and running as follows:

Go to http://python.org/download/releases/2.7.3/ and download the interpreter.

Make a new (text) file called, watchman.py, copy the watchman code block into it and save it. (Make sure windows doesn't give it a hidden .txt in the filename)

run python.exe, some text and a prompt should come up

type "import watchman" (without the quotes)

And, done.

Now that it runs, go and poke at all the pieces of it. The easiest thing to change is probably your stats. One of the cooler things to try is to move change the logic behind the rotation. Having an easily changeable rotation was really helpful for me in seeing what things really took priority for my attacks.

I hope that most of what I've written hear is easily understood, and that people feel free to change my code around to answer their own questions about spec and rotation and gear and such.

Granted, that all of this appeals much more to a certain kind of ocd personality, but for those people, It can be really fun to have an easily tweaked simulator on hand.
Find all posts by this user
Quote this message in a reply
05-08-2012, 07:24 PM (This post was last modified: 05-08-2012 07:26 PM by Ryfe.)
Post: #6
RE: Combat, Watchman Simulators
Thanks for the help! I managed to get it all figured out (the basics of loading it anyway). There seems to be a syntax error on line 64:

"mcrit = 0.05+bonus_crit+diminishing_returns(crit,0.3,0.45)+diminishing_returns(str,0.3,2​?.5)"

I just removed the question mark and everything seems to run just fine; however, I am not sure if "2.5" is the intended value.

I'm going to mess around with it a little more and see whats what. I just wanted to give you a heads up.
Edit: I think the syntax problem is due to moving the Unicoded text into an ANSI editor... I checked the original text and everything matches up once that pesky question mark is gone.

Cheers!
Find all posts by this user
Quote this message in a reply
05-08-2012, 08:40 PM (This post was last modified: 05-08-2012 08:43 PM by Ryfe.)
Post: #7
RE: Combat, Watchman Simulators
Alright, so after a couple of hours of trial and error, I think I have the hang of it. I've been working on a few different rotations/priority list, and have a few questions.

1: In the Combat code, how exactly is Zen calculated? Does the simulator take into account both charges and duration?
2: In the Combat code, is there anyway to account for Precision Slash's debuff similar to "ataru_trance"? {eg. elif master_strike_cd <= 0 and precision_slash_debuff > 0: master_strike()}
3: Do these simulators take skill trees into account, namely the 10% dmg to spenders following ataru procs and the focus generation from burns (Burning Focus in Watchman), Combat Trance (in Combat), and the focus return on Blade Rush, Merciless Slash, Slash, and Dispatch from Focused Slash in the watchman tree, and if so, is it possible to "respec" without having to manually change spell values?

Awesome work by the way. I'm sort of addicted to this thing now. Big Grin
Find all posts by this user
Quote this message in a reply
05-08-2012, 11:36 PM (This post was last modified: 05-08-2012 11:38 PM by Ryfe.)
Post: #8
RE: Combat, Watchman Simulators
I've been using "elif precision_slash_cd >= 9: whatever" to simulate Precision Slash's debuff... Pretty sure that should work. I also wanted to add Opportune Attack to the "is this working properly?" list. Combat seems much harder to simulate due to all the random variables, but I appreciate the feed back. Smile
Find all posts by this user
Quote this message in a reply
05-09-2012, 12:22 AM (This post was last modified: 05-09-2012 12:23 AM by Asashdor.)
Post: #9
RE: Combat, Watchman Simulators
After spending almost 2 weeks messing around with the Watchman/Annihilation part I think i'm able to answer most of your questions - although I'm not able to guarantee my answers on the Combat/Carnage part will be 100% correct.

1: There's an if-clause within the function for Blade Rush (lines 173 to 184) which checks if at least 1 Zen charge is available and then sets the focus cost and global cooldown of the ability and - if necessary - removes one charge of Zen. The maximum number of Zen charges is set in the function "zazen" in lines 433 to 442. The remaining time of Zen (for both Watchman and Combat) is not taken into account but if you're constantly using abilites it shouldn't be possible to run out before using up all charges.

2: Yes, this should be possible by checking if "remove_armor" is greater than 0. It's time is set to 6 seconds in line 279 and there is an if-clause in every ability function checking if it's active (e.g. line 290 for Precision Slash itself).

3: As far as I see it talents are completely hardcoded. If you want to change specific talents you have to change the fomulas or functions - there is no easy way of doing this.
  • Insight should be used in the Watchman module and to remove it you have to change the damage formula of both DoT's (Overload Saber and Cauterize) and other affected abilites. The relevant part for calculating Overload Saber ticks is in line 148 ("if random.uniform(0,1) < (mcrit + 0.06 + 0.01*juyo_stacks + bonuscrit):"), where the red 0.06 should account for 3 points in Insight.
  • Focus Generation from Burning Focus is in line 152 for Overload Saber ticks and in line 290 for Cauterize.
  • Focus return from Focused Slash is also in (assuming fully talented, so those abilities effectively cost 1 Focus less) while checking for the original costs within the rotation itself (e.g if focus >= 5 use Merciless Slash in line 567 but Merciless Slash only costs 4 Focus in line 188). If you want to simulate one or two points in Focused Slash you need to add an additional random function for those costs.
  • Combat trance is set in line 123 (its variable is called "ataru_trance" and the focus generation is in lines 567 and 568.
  • And also Opportune Attack is in, called "ataru_oa_buff" and checked within every affected ability (e.g. lines 167 to 169 for Blade Rush).
  • The only talent i'm not sure about is Steadfast. As far as I can tell it is currently not used but I might be wrong there - Accuracy values are consistent with my ingame (Watchman) values without talenting into it.

I hope that answers some of your questions (or even more than you asked) and I also hope I did not mess something up there. Wink
Find all posts by this user
Quote this message in a reply
05-09-2012, 01:14 AM
Post: #10
RE: Combat, Watchman Simulators
Ack! Sorry for the mashed keyboard and random question mark.

I'm really glad some other people have enjoyed playing around with these.

Yes, the talents are all completely hardcoded. It's a little obnoxious, but I haven't seen any really wild talent choices from people, so hopefully most people can just pick up and use what's there.

Steadfast should just show up as value in bonus_hit.

You can condition on combat trance via the ataru_trance, and opportune attack via the ataru_oa_buff variables (as mentioned above).

Thanks for your replies!
Find all posts by this user
Quote this message in a reply
Post Reply 


Forum Jump:


User(s) browsing this thread: 1 Guest(s)