Skip to content

Instantly share code, notes, and snippets.

@badp
Created December 17, 2012 15:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save badp/4319017 to your computer and use it in GitHub Desktop.
Save badp/4319017 to your computer and use it in GitHub Desktop.
"""
This is terribly written but still it should give you a broad idea of whether or not a gun is better than the other or not.
This keeps track of:
* Damage
* Fire rate
* Reload time
* Magazine
* Elemental damage
* Damage type (vs flesh, shield, armor)
* Playthrough (by default the second)
* Optionally accuracy (but not by default)
* Optionally recoil (but not by default)
Example execution:
=================================
Damage:3316
Accuracy:95.7
Fire rate:8.7
Reload time:2.7
Magazine:25
Element (empty or f'ire, s'hock, c'orrosive, e'xplosive): s
Elemental DPS:3191.1
Elemental chance:10
DPS: Damage( 17,892.65 Flesh, 17,892.65 Armor, 44,731.62 Shield)
=================================
Damage:5255
Accuracy:91
Fire rate:5.5
Reload time:2.3
Magazine:24
Element (empty or f'ire, s'hock, c'orrosive, e'xplosive): s
Elemental DPS:2823.9
Elemental chance:12.9
DPS: Damage( 21,022.29 Flesh, 21,022.29 Armor, 52,555.74 Shield)
=================================
"""
# The dumbest possible approach to measuring DPS
from __future__ import division
import collections
from random import random
from operator import itemgetter
elementLength = {"fire": 8,
"shock": 2,
"corrosive": 8,
"slag": 0, #8,
"explosive": 0,
None: 0}
class Damage():
def __init__(self, flesh = 0, armor = 0, shield = 0):
self.flesh = flesh
self.armor = armor
self.shield = shield
def __iter__(self):
yield self.flesh
yield self.armor
yield self.shield
def __str__(self):
return "Damage({0.flesh:10,.2f} Flesh, {0.armor:10,.2f} Armor, {0.shield:10,.2f} Shield)".format(self)
def __add__(this, that): return Damage(*(a+b for a, b in zip(this, that)))
def __mul__(this, that): return Damage(*(a*that for a in this))
def __truediv__(this, that): return Damage(*(a/that for a in this))
def add(self, value, element, playthrough):
damage = damageMults[element][playthrough] * value
self.flesh += damage.flesh
self.armor += damage.armor
self.shield += damage.shield
damageMults = { #NESTED DICTIONARIES HO!
None: {1: Damage(1 , 0.8 , 1), 2: Damage(1 , 0.8 , 1 )},
"slag": {1: Damage(1 , 0.8 , 1), 2: Damage(1 , 0.8 , 1 )},
"fire": {1: Damage(1.5, 0.75, 0.75), 2: Damage(1.75, 0.4 , 0.4)},
"shock": {1: Damage(1 , 1 , 2), 2: Damage(1 , 1 , 2.5)},
"corrosive": {1: Damage(0.9, 1.5, 0.75), 2: Damage(0.6 , 1.75, 0.4)},
"explode": {1: Damage(1 , 1 , 0.8), 2: Damage(1 , 1 , 0.8)}
}
Weapon = collections.namedtuple("Weapon",
["damage",
"pelletCount",
"accuracy",
"fireRate",
"reloadTime",
"magazine",
"element",
"elementChance",
"elementDPS"])
##BonusStats = collections.namedtuple("BonusStats",
## ["health",
## "shield",
## "shieldDelay",
## "shieldRate",
## "melee",
## "grenade",
## "accuracy",
## "damage",
## "rate",
## "recoil",
## "reload",
## "elementChance",
## "elementDamage",
## "critical"])
#OH MY FUCKING GOD
class BonusStats(tuple):
'BonusStats(health, shield, shieldDelay, shieldRate, melee, grenade, accuracy, damage, rate, recoil, reload, elementChance, elementDamage, critical)'
__slots__ = ()
_fields = ('health', 'shield', 'shieldDelay', 'shieldRate', 'melee', 'grenade', 'accuracy', 'damage', 'rate', 'recoil', 'reload', 'elementChance', 'elementDamage', 'critical')
def __new__(_cls, health, shield, shieldDelay, shieldRate, melee, grenade, accuracy, damage, rate, recoil, reload, elementChance, elementDamage, critical):
'Create new instance of BonusStats(health, shield, shieldDelay, shieldRate, melee, grenade, accuracy, damage, rate, recoil, reload, elementChance, elementDamage, critical)'
return tuple.__new__(_cls, (health, shield, shieldDelay, shieldRate, melee, grenade, accuracy, damage, rate, recoil, reload, elementChance, elementDamage, critical))
@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
'Make a new BonusStats object from a sequence or iterable'
result = new(cls, iterable)
if len(result) != 14:
raise TypeError('Expected 14 arguments, got %d' % len(result))
return result
def __repr__(self):
'Return a nicely formatted representation string'
return 'BonusStats(health=%r, shield=%r, shieldDelay=%r, shieldRate=%r, melee=%r, grenade=%r, accuracy=%r, damage=%r, rate=%r, recoil=%r, reload=%r, elementChance=%r, elementDamage=%r, critical=%r)' % self
def _asdict(self):
'Return a new OrderedDict which maps field names to their values'
return OrderedDict(zip(self._fields, self))
def _replace(_self, **kwds):
'Return a new BonusStats object replacing specified fields with new values'
result = _self._make(map(kwds.pop, ('health', 'shield', 'shieldDelay', 'shieldRate', 'melee', 'grenade', 'accuracy', 'damage', 'rate', 'recoil', 'reload', 'elementChance', 'elementDamage', 'critical'), _self))
if kwds:
raise ValueError('Got unexpected field names: %r' % kwds.keys())
return result
def __getnewargs__(self):
'Return self as a plain tuple. Used by copy and pickle.'
return tuple(self)
def _itemgetter(x):
def g(obj):
return 1 + obj[x]/100
return g
health = property(_itemgetter(0), doc='Alias for field number 0')
shield = property(_itemgetter(1), doc='Alias for field number 1')
shieldDelay = property(_itemgetter(2), doc='Alias for field number 2')
shieldRate = property(_itemgetter(3), doc='Alias for field number 3')
melee = property(_itemgetter(4), doc='Alias for field number 4')
grenade = property(_itemgetter(5), doc='Alias for field number 5')
accuracy = property(_itemgetter(6), doc='Alias for field number 6')
damage = property(_itemgetter(7), doc='Alias for field number 7')
rate = property(_itemgetter(8), doc='Alias for field number 8')
recoil = property(_itemgetter(9), doc='Alias for field number 9')
reload = property(_itemgetter(10), doc='Alias for field number 10')
elementChance = property(_itemgetter(11), doc='Alias for field number 11')
elementDamage = property(_itemgetter(12), doc='Alias for field number 12')
critical = property(_itemgetter(13), doc='Alias for field number 13')
zeroStats = BonusStats(0,0,0,0,0,0,0,0,0,0,0,0,0,0)
#Example
stormingVexation = Weapon(1140, 1, 92.9, 8.0, 2.5, 27, "shock", 1620.5, 18.7)
myStats = BonusStats(7.6, 7.6, 6.8, 5.2, 5.6, 3.3, 6.8, 7.6, 7.6, 7.6, 7.2, 7.2, 4.3, 6.8)
def dps(weapon,
playthrough = 2,
bonusStats = zeroStats,
referenceTime = 60,
accuracyAdjusting = lambda x: 1, # lambda x: x**(1/3),
accountForRecoilInVastlyBogusWays = False):
time = 0
damage = Damage(0,0,0)
ammoCount = weapon.magazine
recoilMalus = 0
while time <= referenceTime:
time += 1/(weapon.fireRate * bonusStats.rate)
for i in xrange(weapon.pelletCount):
if random() < accuracyAdjusting(weapon.accuracy/100 * bonusStats.accuracy + recoilMalus):
damage.add(weapon.damage * bonusStats.damage, weapon.element, playthrough)
if weapon.element:
if random() < weapon.elementChance/100 * bonusStats.elementChance:
damage.add(weapon.elementDPS * bonusStats.elementDamage * elementLength[weapon.element],
weapon.element, playthrough) #are elemental ticks actually affected as well?
ammoCount -= 1
if accountForRecoilInVastlyBogusWays:
recoilMalus -= 0.01 * 1/bonusStats.recoil
if ammoCount == 0:
time += weapon.reloadTime * bonusStats.reload
ammoCount = weapon.magazine
recoilMalus = 0
return damage/time
# Weapon = collections.namedtuple("Weapon",
# ["damage",
# "pelletCount",
# "accuracy",
# "fireRate",
# "reloadTime",
# "magazine",
# "element",
# "elementChance",
# "elementDPS"])
def inputWeapon():
elements = {"": None,
"f": "fire",
"s": "shock",
"c": "corrosive",
"g": "slag",
"e": "explode"}
dmg = raw_input("Damage:")
if "x" in dmg:
dmg, pellets = dmg.split("x")
pellets = int(pellets)
else:
pellets = 1
dmg = float(dmg)
accuracy = float(raw_input("Accuracy:"))
fireRate = float(raw_input("Fire rate:"))
reloadTime = float(raw_input("Reload time:"))
magazine = int(raw_input("Magazine:"))
element = elements[raw_input("Element (empty or f'ire, s'hock, c'orrosive, e'xplosive): ")]
if element in ("fire", "shock", "corrosive"):
elDPS = float(raw_input("Elemental DPS:"))
chance = float(raw_input("Elemental chance:"))
else:
elDPS, chance = 0, 0
return Weapon(dmg, pellets, accuracy, fireRate, reloadTime, magazine, element, chance, elDPS)
while True:
print "================================="
wep = inputWeapon()
print ""
print "DPS: ", dps(wep)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment