Skip to content

Instantly share code, notes, and snippets.

@arrdem
Created August 22, 2013 21:22
Show Gist options
  • Save arrdem/6312964 to your computer and use it in GitHub Desktop.
Save arrdem/6312964 to your computer and use it in GitHub Desktop.
#!/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