Created
August 22, 2013 21:22
-
-
Save arrdem/6312964 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
#!/usr/bin/env python3 | |
# Authored by Reid "arrdem" McKenzie, 10/22/2013 | |
# Licenced under the terms of the EPL 1.0 | |
import cortex.stats as stats | |
def attack_perm_generator(weapons, src_model, tgt_model, | |
focus=0, can_boost_hit=True): | |
"""Recursively compute attack options over two models, focus and weapons! | |
This function defines a recursive itterator which will eventially yield all | |
legal combinations of weapon uses as a sequence of structured maps | |
describing each step of the attack sequence with whether or not it was | |
boosted and its projected damage output. | |
WARNING: | |
In order to support models such as B13 and unit leader attachments which | |
feature attack boosting options, the can_boost_hit argument exists. If | |
this flag is Falsey, then all permutations wherein an attack is hit | |
boosted will be discarded. The flag defaults to Truthy, so make sure | |
that if you have to set it that it gets set! Otherwise output will be | |
garbage! | |
""" | |
# FIXME: | |
# Some constraint system for preventing weapons from being boosted is | |
# also probably in order. | |
# | |
# FIXME: | |
# As of the first cut, this code does not explicitly consider charges, so | |
# the concept that a 'jack or beast may charge for free cannot be | |
# expressed without increasing the focus allocation and generating | |
# garbage possibilities. | |
weapon_of_choice = weapons[0] # with apologies to Fatboy Slim | |
other_weapons = None | |
if 'cumbersome' in w['attrs']: | |
other_weapons = [] | |
elif "virtuoso" in src_model.attrs: | |
other_weapons = weapons[1:] | |
else: | |
other_weapons = [_w for _w in weapons[1:] | |
if _w['type'] == w['type']] | |
for attacks in range(1, weapon_of_choice['rof'] + 1 | |
if 'rof' in weapon_of_choice | |
else 2 + focus): | |
_focus = (focus - attacks + 1) | |
for boost_hit in ([True, False] if (_focus > 0 | |
and can_boost_hit) | |
else [False]): | |
__focus = (_focus if not boost_hit else (_focus - 1)) | |
dmg_boost_options = [True, False] | |
if ('powerfull' in weapon_of_choice['attrs'] | |
and boost_hit): | |
dmg_boost_options = [True] | |
elif focus == 0: | |
dmg_boost_options = [False] | |
for boost_dmg in dmg_boost_options: | |
___focus = (__focus if ((not boost_dmg) | |
or ('powerfull' in | |
weapon_of_choice['attrs'])) | |
else (__focus - 1)) | |
# compute the odds of a hit first | |
atk = (src_model.rat if weapon_of_choice['type'] == 'ranged' | |
else src_model.mat) | |
hit_dice = (2 + (1 if boost_hit else 0)) | |
hitp = stats.odds_of_beating(hit_dice, (tgt_model.defense - atk)) | |
# compute the damage of a hit second | |
pow = (weapon_of_choice['pow'] | |
+ (src_model.strength | |
if weapon_of_choice['type'] == 'melee' else 0)) | |
dmg_dice = (2 + (1 if 'weaponmaster' in src_model.attrs else 0) | |
+ (1 if boost_dmg else 0)) | |
dmg = stats.avg_damage(dmg_dice, (pow - tgt_model.armor)) | |
# and the projected hit damage is... | |
prob_dmg = float(hitp) * float(dmg) | |
result = {"weapon":weapon_of_choice['name'], | |
"boost hit":boost_hit, | |
"hit":hitp, | |
"boost dmg":boost_dmg, | |
"dmg":dmg, | |
"avg":prob_dmg} | |
if other_weapons: | |
# now recur over the other weapons with remaining focus | |
for (outcome in | |
_r_attack_perm_generator(other_weapons, | |
src_model, | |
tgt_model, | |
focus=___focus, | |
can_boost_hit=can_boost_hit)): | |
yield outcome.insert(0, result) | |
else: | |
yield [result] | |
def optimize_attack(attacker, defender, | |
strategy=lambda x, y: (x['avg'] > y['avg']), | |
count=2): | |
"""This code uses some <strategy> fn to find the top <count> attack permutations | |
of one model attacking another. | |
Strategy is a function of two arguments, both of which will be seqs of | |
attack actions. By default, the strategy used is a naive damage | |
maximizer. Due to the exceedingly small search space of Warmachine attack | |
permutations, the real question is that of boosting which this code should | |
be able to solve. Strategies such as a filter for all melee attacks or | |
ranged attacks will probably reduce the results list to a singelton but such | |
behavior is not guranteed. | |
""" | |
window = [] | |
for option in attack_perm_generator(weapons, src_model, tgt_model, | |
focus=0, can_boost_hit=True): | |
window.append(option) | |
if(len(window) < count): | |
continue | |
else: | |
window = sorted(window, key=strategy)[0:3] | |
return window |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment