Kiting
unit a vs b
assume a.range > b.range, a.speed < b.speed
range advantage: a.range-b.range
speed advantage: a.speed-b.speed
kiting time advantage (KT): (a.range-b.range) / (b.speed-a.speed)
kiting damage advantage (KD): KT>0: KT*a.dpspc
assume a_cost, b_cost:
when a kite b, first a deal burst damage, then a deal dps for KT time, then b deam burst on a, then a b brawl
b_cost = b_cost_0 - a_cost_0* (a.burstpc + tdadv * a.dpspc) / b.hppc
then a_cost = a_cost_0 - b_cost*b.burstpc / a.hppc
compare a_cost * a.BF = b_cost *b.BF, we get who win.
by going backward, we can calculate braw percentage of a,b.
Asked ai to simplify :) b_cost_0/a_cost_0 = ( KT * a.dpspc + a.burstpc )/b.hppc + 1/ ( b.BF / a.BF + b.burstpc / a.hppc )
Had written some code to calculate.
This may soon become generic unit comparer.
[Spoiler]unit.py
from functools import cache
import math
class Unit:
hppc:float # Hp per cost
speed:float # speed
dpspc:float # dps per cost
range:float # range
burstpc:float # burst per cost. burst damage is that a unit can done instantly when enemy in range
name:str|None
def __init__(self,hppc:float,speed:float,dpspc:float,
burstpc:float,range:float,name:str|None=None) -> None:
self.hppc=hppc
self.speed=speed
self.dpspc=dpspc
self.range=range
self.burstpc=burstpc
self.name=name
pass
def to_dict(self):
return {
"hppc":self.hppc,
"dpspc":self.dpspc,
"speed":self.speed,
"range":self.range,
"burstpc":self.burstpc,
"name":self.name
}
@cache
def brawl_factor_sq(self):
return self.dpspc*self.hppc
@cache
def brawl_factor(self):
return math.sqrt(self.brawl_factor_sq())
BF=brawl_factor
@staticmethod
def unit_from_cost(cost:float,hp:float,speed:float,
dps:float,burst:float,range:float,name:str|None=None):
return Unit(
hppc=hp/cost,
speed=speed,
dpspc=dps/cost,
burstpc=burst/cost,
range=range,
name=name)
class UnitCompare:
a:Unit
b:Unit
def __init__(self,a:Unit,b:Unit):
self.a=a
self.b=b
@cache
def brawl_advantage(self):
return self.a.brawl_factor()/self.b.brawl_factor()
@cache
def range_advantage(self):
return self.a.range - self.b.range
@cache
def speed_advantage(self):
return self.a.speed - self.b.speed
@cache
def kite_time_advantage(self):
speed_adv=self.speed_advantage()
range_adv=self.range_advantage()
if range_adv==0:
return 0
elif range_adv>0:
if speed_adv<0:
return -range_adv/speed_adv
else:
return math.inf
else:
if speed_adv>0:
return range_adv/speed_adv
else:
return -math.inf
KT=kite_time_advantage
@cache
def kite_damage_advantage(self):
time_adv=self.KT()
if time_adv==0:
return 0
elif time_adv>0:
return time_adv*self.a.dpspc
else:
return time_adv*self.b.dpspc
KD=kite_damage_advantage
def predict_kite_advantage(self,a_b_cost:tuple[float,float])->tuple[float,float]:
(a_cost,b_cost)=a_b_cost
kd=self.KD()
if kd>0:
b_cost -= a_cost*self.a.burstpc / self.b.hppc
b_cost -= kd * a_cost /self.b.hppc
if b_cost<0:
b_cost=0
a_cost -= b_cost*self.b.burstpc / self.a.hppc
else:
a_cost -= b_cost*self.b.burstpc / self.a.hppc
a_cost -= - kd * b_cost /self.a.hppc
if a_cost<0:
a_cost=0
b_cost -= a_cost*self.a.burstpc / self.b.hppc
return (a_cost,b_cost)
@cache
def overall_advantage_factor_raw(self):
a_cost = 1 / self.a.BF()
b_cost = 1 / self.b.BF()
kd=self.kite_damage_advantage()
if kd>0:
a_cost += b_cost*self.b.burstpc / self.a.hppc
b_cost += kd * a_cost /self.b.hppc
b_cost += a_cost*self.a.burstpc / self.b.hppc
else:
b_cost += a_cost*self.a.burstpc / self.b.hppc
a_cost += - kd * b_cost /self.a.hppc
a_cost += b_cost*self.b.burstpc / self.a.hppc
return b_cost/a_cost
@cache
def overall_advantage_factor(self):
kd=self.KD()
if kd>0:
a = self.a
b = self.b
return \
kd / b.hppc+ a.burstpc / b.hppc \
+ 1/ ( b.BF() / a.BF() + b.burstpc / a.hppc )
else:
a = self.b
b = self.a
res = \
-kd / b.hppc+ a.burstpc / b.hppc \
+ 1/ ( b.BF() / a.BF() + b.burstpc / a.hppc )
return 1/ res
unit_from_zero_k.py load some data from zero-k
for k in typecal_units:
print((unit_from_zero_k.unitdefs[k].name or k) + "'s BF: " + str(unit_from_zero_k.unitdefs[k].brawl_factor()) )
for b_k in overall_compare_units:
cmp_locust_other = unit.UnitCompare( unit_from_zero_k.unitdefs["gunshipraid"] , unit_from_zero_k.unitdefs[b_k] )
print( "Locust vs " + ( unit_from_zero_k.unitdefs[b_k].name or b_k ) + " overall advantage: " + str(cmp_locust_other.overall_advantage_factor()) )
for b_k in brawl_compare_units:
cmp_locust_other = unit.UnitCompare( unit_from_zero_k.unitdefs["gunshipraid"] , unit_from_zero_k.unitdefs[b_k] )
print( "Locust vs " + ( unit_from_zero_k.unitdefs[b_k].name or b_k ) + " brawl advantage: " + str(cmp_locust_other.brawl_advantage()) )
Reaver's BF: 2.2406267344797315
Raptor's BF: 1.1055415967851332
Trident's BF: 0.8608781046763305
Bandit's BF: 2.2639984295243867
Glaive's BF: 2.390810810449239
Hacksaw's BF: 1.108011094956061
Scallop's BF: 2.299216490349261
Toad's BF: 1.4245841498486498
Locust's BF: 1.0020639856840274
Gremlin's BF: 1.5957517385580724
Tarantula's BF: 1.1644425790483877
Angler's BF: 1.3033274920143167
Stardust's BF: 3.7344719829897692
Locust vs Reaver overall advantage: 0.4193698094839383
Locust vs Trident overall advantage: 0.5942703442974699
Locust vs Angler overall advantage: 0.41327089064940464
Locust vs Hacksaw overall advantage: 0.32941084485932304
Locust vs Toad overall advantage: 0.5335701178767338
Locust vs Gremlin overall advantage: 0.4840650632010012
Locust vs Tarantula overall advantage: 0.5394871091728761
Locust vs Scallop overall advantage: 0.37622805053289354
Locust vs Stardust overall advantage: 0.2470960327476647
Locust vs Bandit brawl advantage: 0.4426080745535401
Locust vs Glaive brawl advantage: 0.4191314433180672
Locust vs Raptor brawl advantage: 0.906400979029632
Locust vs Angler is not very correct because angler shot 4 per 12 second while calculation assume they deal continuous dps, which overrate Angler.
Stardust heat is not counted.
Aoe are not counted.