Skip to content

Instantly share code, notes, and snippets.

@elistevens
Created August 5, 2014 21:08
Show Gist options
  • Save elistevens/9fbcffb3f33e26eb475c to your computer and use it in GitHub Desktop.
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.
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)
print
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