In [49]:
import math
class Weapon(object):
def __init__(self, name, s, w_inf, a, ap = 0, rngd = False):
# Mandatory fields
self.name = name
self.s = float(s)
self.w_inf = float(w_inf)
self.a = float(a)
self.ap = float(ap)
self.rngd = rngd
# Optional fields
self.is_psn = False
self.psn = 7.0
self.is_rndm_inf = False
self.rndm_inf = 6.0
self.is_rr_hits = False
self.is_rr_w = False
# weapon can reroll to wounds
def set_rr_w(self):
self.is_rr_w = True
# weapon can reroll to hits
def set_rr_hits(self):
self.is_rr_hits = True
# the weapon always wounds on a roll of psn
def set_psn(self, psn):
self.psn = psn
self.is_psn = True
def avg_w(self):
if self.is_rndm_inf:
return (1.0 + self.rndm_inf) / 2.0
return self.w_inf
# str >= 2* tough => 2+
# str > tough => 3+
# str = tough => 4+
# str < tough => 5+
# 2*str <= tough => 6+
def probability_to_wound(self, t):
s = self.s
wound_on = 5.0
if self.is_psn:
wound_on = self.psn
elif s >= 2*t:
wound_on = 2.0
elif s > t:
wound_on = 3.0
elif s == t:
wound_on = 4.0
elif 2*s < t:
wound_on = 6.0
basic = (7.0 - wound_on) / 6.0
p_wound = basic
if self.is_rr_w:
p_wound = 1.0 - (1.0 - basic) * (1.0 - basic)
return p_wound
# Define the stat sheet for offense
class Stats(Weapon):
def __init__(self, name, ws, bs, s, t, w, a, sv):
# weapon fields
self.name = name
self.s = float(s)
self.w_inf = 1.0
self.a = float(a)
self.ap = 0.0
self.rngd = False
# Optional weapon fields
self.is_psn = False
self.psn = 7.0
self.is_rndm_inf = False
self.rndm_inf = 6.0
self.is_rr_hits = False
self.is_rr_w = False
# required fields
self.ws = float(ws)
self.bs = float(bs)
self.t = float(t)
self.w = float(w)
self.sv = float(sv)
# invulnerable save
self.inv = 7.0
self.has_inv = False
# wound recovery
self.has_w_ig = False
self.w_ig = 7.0
# by default we assume melee so use characters stat block
self.weapon = self
def set_inv(self, inv):
self.inv = inv
self.has_inv = True
def set_weapon(self, weapon):
self.weapon = weapon
# set the chance to ignore the wound
def set_w_ig(self, w_ig):
self.has_w_ig = True
self.w_ig = w_ig
def probability_to_hit(self):
atk = self.ws
if self.weapon.rngd:
atk = self.bs
basic = (7.0 - atk) / 6.0
p_hit = basic
if self.weapon.is_rr_hits:
p_hit = 1.0 - (1.0 - basic) * (1.0 - basic)
return p_hit
def probability_to_save(self, attacker):
# determine save
save = self.sv + attacker.weapon.ap
if self.has_inv:
if self.inv < save:
save = self.inv
return (7.0 - save) / 6.0
def probability_to_ignore_wound(self):
if self.has_w_ig:
return (7.0 - self.w_ig) / 6.0
return 0.0
def mean_attacks_to_kill(self, defender):
# probability of hitting
prob_hit = self.probability_to_hit()
# probability of wounding
t = defender.t
p_w = self.probability_to_wound(t)
prob_ignore = defender.probability_to_ignore_wound()
prob_wound = p_w - p_w * prob_ignore
# probability of saving against the wound
prob_save = defender.probability_to_save(self)
attacks_to_dmg = int(1.0 / (prob_hit * prob_wound * (1.0 - prob_save)))
dmg_to_kill = int(math.ceil(defender.w / self.weapon.avg_w()))
attacks_to_kill = attacks_to_dmg * dmg_to_kill
print "Probability of " + self.name + " hitting " + defender.name + ": " + str(prob_hit) + "\nProbability of wounding: " + str(prob_wound) + "\nProbability of saving: " + str(prob_save) + "\nProbability of ignoring: " + str(prob_ignore) + "\nTotal attacks to kill: " + str(attacks_to_kill)
return attacks_to_kill
def models_to_kill(self, defender):
matk = self.mean_attacks_to_kill(defender)
mtk = int(math.ceil(matk / self.weapon.a))
print "Number of " + str(self.name) + " models requied to kill one " + str(defender.name) + " in one turn is " + str(mtk)
return mtk
In [45]:
wych_in_combat = Stats(name = "Wych", ws = 3, bs = 3, s = 3, t = 3, w = 1, a = 2, sv = 6)
wych_in_combat.set_inv(4)
wych_in_combat.set_w_ig(6)
necron_warrior = Stats(name = "Necron Warrior", ws = 3, bs = 3, s = 4, t = 4, w = 1, a = 1, sv = 4)
In [46]:
wych_in_combat.models_to_kill(necron_warrior)
Out[46]:
In [47]:
necron_warrior.models_to_kill(wych_in_combat)
Out[47]:
In [52]:
hekatrix = Stats(name = "Wych Hekatrix", ws = 3, bs = 3, s = 3, t = 3, w = 1, a = 3, sv = 6)
hekatrix.set_inv(4)
hekatrix.set_w_ig(6)
agonizer = Weapon(name = "Agonizer", s = 3, w_inf = 1, a = 3, ap = 2)
agonizer.set_psn(4)
hekatrix.set_weapon(agonizer)
In [53]:
hekatrix.mean_attacks_to_kill(necron_warrior)
Out[53]:
In [ ]: