Created
December 17, 2012 15:13
-
-
Save badp/4319017 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
""" | |
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