Created
August 5, 2014 21:08
-
-
Save elistevens/9fbcffb3f33e26eb475c to your computer and use it in GitHub Desktop.
Scratch code that calculates probabilities of dice rolls for the FFG X-Wing minis game.
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
import itertools | |
def attack(n, rerolls=0, focus=False, r='', p=1.0): | |
s = 'CHHHffxx' | |
focus = 'h' if focus else None | |
result_dict = dice(s, n, r, p) | |
result_dict = reroll(result_dict, s, rerolls, focus) | |
#print 'attack', n, rerolls, focus, repr(r), p, result_dict | |
result_dict = usefocus(result_dict, focus) | |
return result_dict | |
def defense(n, rerolls=0, focus=False, r='', p=1.0): | |
s = 'EEEffxxx' | |
focus = 'e' if focus else None | |
result_dict = dice(s, n, r, p) | |
result_dict = reroll(result_dict, s, rerolls, focus) | |
result_dict = usefocus(result_dict, focus) | |
return result_dict | |
def dice(s, n, r, p): | |
if n == 0: | |
return {r: p} | |
r_dict = {} | |
for sub_r, sub_p in dice(s, n-1, r, p).items(): | |
for result in s: | |
new_r = ''.join(sorted(result+sub_r)) | |
r_dict.setdefault(new_r, 0.0) | |
r_dict[new_r] += sub_p/float(len(s)) | |
return r_dict | |
#dice('Hfx', 1, 'H', 1.0) | |
def rerollCount(r, rerolls, focus): | |
if not focus and 'f' in r: | |
miss_index = r.index('f') | |
elif 'x' in r: | |
miss_index = r.index('x') | |
else: | |
miss_index = None | |
if miss_index is not None: | |
miss_count = len(r) - miss_index | |
return min(rerolls, miss_count) | |
return 0 | |
#rerollCount('CHfx', 0, None) | |
#rerollCount('CHfx', 1, None) | |
#rerollCount('CHfx', 2, None) | |
#rerollCount('CHfx', 3, None) | |
#rerollCount('CHfx', 0, True) | |
#rerollCount('CHfx', 1, True) | |
#rerollCount('CHfx', 2, True) | |
#rerollCount('CHfx', 3, True) | |
def reroll(result_dict, s, rerolls, focus): | |
mod_dict = {} | |
for sub_r, sub_p in result_dict.items(): | |
reroll_count = rerollCount(sub_r, rerolls, focus) | |
if reroll_count: | |
for new_r, new_p in dice(s, reroll_count, sub_r[:-reroll_count], sub_p).items(): | |
mod_dict.setdefault(new_r, 0.0) | |
mod_dict[new_r] += new_p | |
else: | |
mod_dict.setdefault(sub_r, 0.0) | |
mod_dict[sub_r] += sub_p | |
return mod_dict | |
#reroll({'C': 1.0}, 'CHfx', 1, False) | |
#reroll({'H': 1.0}, 'CHfx', 1, False) | |
#reroll({'f': 1.0}, 'CHfx', 1, False) | |
#reroll({'x': 1.0}, 'CHfx', 1, False) | |
#reroll({'C': 1.0}, 'CHfx', 1, True) | |
#reroll({'H': 1.0}, 'CHfx', 1, True) | |
#reroll({'f': 1.0}, 'CHfx', 1, True) | |
#reroll({'x': 1.0}, 'CHfx', 1, True) | |
def usefocus(result_dict, focus): | |
if focus: | |
mod_dict = {} | |
for sub_r, sub_p in result_dict.items(): | |
assert not sub_r.startswith('-'), sub_r | |
if 'f' in sub_r: | |
new_r = '-' + sub_r.replace('f', focus) | |
else: | |
new_r = sub_r | |
assert not new_r.startswith('--'), sub_r | |
mod_dict.setdefault(new_r, 0.0) | |
mod_dict[new_r] += sub_p | |
return mod_dict | |
return result_dict | |
#usefocus({'C': 1.0}, 'H') | |
#usefocus({'H': 1.0}, 'H') | |
#usefocus({'f': 1.0}, 'H') | |
#usefocus({'x': 1.0}, 'H') | |
#usefocus({'ff': 1.0}, 'H') | |
def damage(attack_r, defense_r): | |
hit_count = sum([s in 'CHh' for s in attack_r]) | |
evade_count = sum([s in 'Ee' for s in defense_r]) | |
return max(hit_count - evade_count, 0) | |
damage('H', 'E') | |
damage('H', 'EE') | |
damage('CHH', 'E') | |
damage('H', '') | |
damage('H', 'x') | |
def vs(attack_dict, defense_dict): | |
damage_dict = {} | |
for attack_r, attack_p in attack_dict.items(): | |
for defense_r, defense_p in defense_dict.items(): | |
damage_count = damage(attack_r, defense_r) | |
damage_dict.setdefault(damage_count, 0) | |
damage_dict[damage_count] += attack_p * defense_p | |
damage_avg = 0.0 | |
for damage_count, damage_p in damage_dict.items(): | |
damage_avg += damage_count * damage_p | |
return damage_avg, damage_dict | |
def scoreShip(name, atk_dice, def_dice, hull_count, shield_count, point_cost, rerolls=0): | |
output_avg = 0.0 | |
avg_count = 0 | |
for of, df, vs_dice in itertools.product([0,1], [0,1], [1]): | |
output_avg += vs(attack(atk_dice, rerolls, of), defense(vs_dice, 0, df))[0] | |
avg_count += 1 | |
output_avg /= avg_count | |
input_avg = 0.0 | |
avg_count = 0 | |
for of, df, vs_dice in itertools.product([0,1], [0,1], [4]): | |
input_avg += vs(attack(vs_dice, 0, of), defense(def_dice, 0, df))[0] | |
avg_count += 1 | |
input_avg /= avg_count | |
round_count = float(hull_count + shield_count) / input_avg | |
inflicted_count = output_avg * round_count | |
score = output_avg * round_count / point_cost | |
print name, '\t', round(score, 3), round(inflicted_count, 3), round(output_avg, 3), round(input_avg, 3), round(round_count, 3) | |
return name, score, inflicted_count, output_avg, input_avg, round_count | |
print '\t\t', 'score total out in round' | |
scoreShip('TF-Academy', 2, 3, 3, 0, 12) | |
scoreShip('TF-Acad-HR', 2, 3, 3, 0, 12, 1) | |
scoreShip('TF-Predator', 2, 3, 3, 0, 17, 1) | |
scoreShip('TI-PS1 ', 3, 3, 3, 0, 18) | |
scoreShip('TI-PS1-HR', 3, 3, 3, 0, 18, 1) | |
scoreShip('TP-Naked', 4, 2, 2, 2, 25) | |
scoreShip('TP-Cloaked', 4, 4, 2, 2, 25) | |
scoreShip('TP-N-FCS', 4, 2, 2, 2, 25, 4) | |
scoreShip('TP-C-FCS', 4, 4, 2, 2, 25, 4) | |
scoreShip('TF-Academy', 3, 3, 3, 0, 12) | |
scoreShip('TF-Acad-HR', 3, 3, 3, 0, 12, 1) | |
scoreShip('TF-Predator', 3, 3, 3, 0, 17, 1) | |
scoreShip('TI-PS1 ', 4, 3, 3, 0, 18) | |
scoreShip('TI-PS1-HR', 4, 3, 3, 0, 18, 1) | |
scoreShip('TP-Naked', 5, 2, 2, 2, 25) | |
scoreShip('TP-Cloaked', 5, 4, 2, 2, 25) | |
scoreShip('TP-N-FCS', 5, 2, 2, 2, 25, 5) | |
scoreShip('TP-C-FCS', 5, 4, 2, 2, 25, 5) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment