import copy
"""
Damage computing module.
"""
class ArmorGrid:
"""
A Starsector ship armor grid.
"""
_ARMOR_RATING_PER_CELL_FACTOR = 1 / 15
_MINIMUM_ARMOR_FACTOR = 0.05
_MINIMUM_DAMAGE_FACTOR = 0.15
WEIGHTS = [[0.0, 0.5, 0.5, 0.5, 0.0],
[0.5, 1.0, 1.0, 1.0, 0.5],
[0.5, 1.0, 1.0, 1.0, 0.5],
[0.5, 1.0, 1.0, 1.0, 0.5],
[0.0, 0.5, 0.5, 0.5, 0.0]]
def __init__(self, armor_rating: float, cell_size: float, width: int):
self._minimum_armor = ArmorGrid._MINIMUM_ARMOR_FACTOR * armor_rating
self._cells = [[armor_rating * ArmorGrid._ARMOR_RATING_PER_CELL_FACTOR
for _ in range(width + 4)] for _ in range(5)]
self._bounds = [i * cell_size for i, _ in enumerate(self[2][2:-2])]
def __getitem__(self, row) -> float:
"""
Return the armor value of the jth cell of the ith row.
"""
return self._cells[row]
def _pool(self, index: int) -> float:
"""
Return the pooled armor for the cell at this index of an armor
row.
"""
return sum([sum([self[j][i + index - 3] * ArmorGrid.WEIGHTS[j][i]
for i in range(0, 5)]) for j in range(0, 5)])
def _effective_armor(self, index: int) -> float:
"""
Return the effective armor for the cell at this index
of an armor grid.
"""
return max(self._minimum_armor, self._pool(index))
def damage_factor(self, hit_strength: float, index: int) -> float:
"""
Return the armor damage factor for a hit to the cell at
this index of an armor grid.
"""
return max(ArmorGrid._MINIMUM_DAMAGE_FACTOR,
1 / (1 + self._effective_armor(index) / hit_strength))
@property
def bounds(self):
"""
The right bound of each cell in the middle row, except the two
padding cells on both sides.
"""
return self._bounds
class Target:
"""
Holds an armor grid and potentially a shield and hull.
"""
def __init__(self, armor_grid):
self.armor_grid = armor_grid
class Shot:
"""
A shot fired at a row of armor cells protected by a shield.
base_shield_damage - starting amount of damage to be
inflicted on the target shield
base_armor_damage - starting amount of damage to be
inflicted on the target armor
strength - strength against armor for armor damage
calculation
"""
def __init__(
self,
base_shield_damage: float,
base_armor_damage: float,
strength: float):
self.base_shield_damage = base_shield_damage
self.base_armor_damage = base_armor_damage
self.strength = strength
class DamageExpectation:
"""
Expected damage to a target by a shot.
Calculates the expectation value of the damage of a shot
with a spread to a target with a shield, armor grid, and random
positional deviation.
shot - what is being fired, whether a projectile, missile, or beam tick
target - what is being hit
distribution - spread of shots across a horizontal distance
"""
def __init__(self, target: object, shot: object, distribution: object):
self.shot = shot
self.target = target
self.probabilities = [distribution(bound) for bound in
target.armor_grid.bounds]
def _expected_armor_damage_distribution(self) -> list:
"""
Return the expected damage to each cell of the targeted armor
row times the probability to hit it.
damage - damage against armor after reduction by armor
"""
return [self.shot.base_armor_damage
* self.target.armor_grid.damage_factor(self.shot.strength, i+2)
* self.probabilities[i]
for i, _ in enumerate(self.probabilities)]
def _weighted_expected_armor_damage_distributions(self) -> list:
"""
Return the weighted distribution across the surrounding armor grid
of the expected damage to each cell of the targeted armor row.
damage - damage against armor after reduction by armor
"""
return [
[[damage * weight for weight in row] for row in ArmorGrid.WEIGHTS]
for damage in self._expected_armor_damage_distribution()
]
def damage_armor_grid(self):
"""
Reduce the values of the armor grid cells of the target
by the expected value of the damage of the shot across them.
"""
for i, distribution in enumerate(
self._weighted_expected_armor_damage_distributions()):
for j, row in enumerate(distribution):
for k, damage in enumerate(row):
self.target.armor_grid[j][i+k] = (
max(0, self.target.armor_grid[j][i+k] - damage))
def hit_probability(bound: float): return 0.1 #dummy for test
def main():
armor_rating, cell_size, width = 100, 10, 10
base_armor_damage, base_shield_damage, hit_strength = 10, 40, 10
target = Target(ArmorGrid(armor_rating, cell_size, width))
shot = Shot(base_armor_damage, base_shield_damage, hit_strength)
expectation = DamageExpectation(target, shot, hit_probability)
for row in expectation.target.armor_grid: print([round(x) for x in row])
expectation.damage_armor_grid()
print()
for row in expectation.target.armor_grid: print([round(x) for x in row])
main()
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
[7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7]
[6, 6, 5, 5, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6]
[6, 6, 5, 5, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6]
[6, 6, 5, 5, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6]
[7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7]
I have discovered that the code was not even calling the right methods and have refactored it, even removing the armor_damage_factors.