def plot_opposed_fixed_total(ax, single_die, die_counts, die_name): coef = 4.0 * single_die.mean() * single_die.mean() / single_die.variance() legend = [] for die_count in die_counts: x = [] cdf = [] for die_count_a in range(die_count + 1): die_count_b = die_count - die_count_a die_bonus_a = numpy.sqrt( numpy.maximum(die_count_a * coef - variance_b, 0.0)) die_bonus_b = numpy.sqrt( numpy.maximum(die_count_b * coef - variance_b, 0.0)) opposed = single_die.repeat_and_sum( die_count_a) - single_die.repeat_and_sum(die_count_b) p = opposed >= Die.coin() x.append(die_bonus_b - die_bonus_a) cdf.append(p) pmf = (numpy.diff(cdf, prepend=0.0) + numpy.diff(cdf, append=1.0)) * 50.0 marker = marker_map[die_count] if die_count in marker_map else '.' ax.plot(x, pmf, marker=marker) legend.append('%dd%s' % (die_count, die_name)) ax.legend(legend, loc='upper right') ax.set_xlabel('Bonus disparity after conversion to roll-over') ax.set_ylabel('"Chance" (%)') ax.grid(which="both")
def plot_opposed_fixed_side(ax, single_die, die_counts, die_name): coef = 4.0 * single_die.mean() * single_die.mean() / single_die.variance() legend = [] for die_count_a in die_counts: x = [] ccdf = [] for die_count_b in range(0, 100): die_bonus_a = numpy.sqrt( numpy.maximum(die_count_a * coef - variance_b, 0.0)) die_bonus_b = numpy.sqrt( numpy.maximum(die_count_b * coef - variance_b, 0.0)) opposed = single_die.repeat_and_sum( die_count_a) - single_die.repeat_and_sum(die_count_b) p = opposed >= Die.coin() # p = (opposed > 0) / ((opposed > 0) + (opposed < 0)) x.append(die_bonus_b - die_bonus_a) ccdf.append(p) #pmf = (numpy.diff(cdf, prepend=0.0) + numpy.diff(cdf, append=1.0)) * 50.0 pmf = -numpy.diff(ccdf, prepend=1.0) * 100.0 marker = marker_map[die_count_a] if die_count_a in marker_map else '.' ax.plot(x + 0.5 * numpy.diff(x, append=x[-1]), pmf, marker=marker) legend.append('%dd%s' % (die_count_a, die_name)) ax.legend(legend, loc='upper right') ax.set_xlabel('Bonus disparity after conversion to roll-over') ax.set_ylabel('"Chance" (%)') ax.grid(which="both") ax.set_ylim(bottom=0)
# Minimize distance on d20 curve. def objective(sd): gaussian = Die.gaussian(10.5, sd) return Die.d20.ks_stat(gaussian) print( scipy.optimize.minimize_scalar(objective, bounds=(5.0, 8.0), method='bounded')) d20 = Die.d20 opposed_d20 = d20 - d20 - Die.coin() figsize = (8, 4.5) dpi = 150 def make_pmf_plot(die, offset=0, sd=None): if sd is None: gaussian = Die.gaussian(die) else: gaussian = Die.gaussian(die.mean(), sd) print('Var:', die.variance()) print('MAD median:', die.mad_median()) fig = plt.figure(figsize=figsize)
fig = plt.figure(figsize=figsize) ax = plt.subplot(111) ax.plot(g_x, g_ccdf * 100.0, linestyle=':') ax.plot(x_ccdf, ccdf * 100.0, marker='.') ax.grid() ax.set_xlim(left, right) ax.set_xlabel('Deviation from mean (SDs)') ax.set_ylabel('Chance to hit (%)') ax.set_ylim(0, 100.0) ax.set_title('%d pool size (KS = %0.2f%%)' % (pool_size, ks * 100.0)) ccdf_frame_path = 'output/frames/ccdf_%03d.png' plt.savefig(ccdf_frame_path % frame_index, dpi = dpi, bbox_inches = "tight") plt.close() make_webm(pmf_frame_path, 'output/success_pool_roe_%s_pmf.webm' % name) make_webm(ccdf_frame_path, 'output/success_pool_roe_%s_ccdf.webm' % name) #make_anim(Die.coin(1/6), 'd6_2plus') #make_anim(Die.coin(2/6), 'd6_3plus') make_anim(Die.coin(3/6), 'd6_4plus') #make_anim(Die.coin(4/6), 'd6_5plus') #make_anim(Die.coin(5/6), 'd6_6plus') exalted2e = Die.from_faces([0]*6 + [1]*3 + [2]) owod = Die.from_faces([-1] + [0]*5 + [1]*4) nwod = Die.from_faces([0]*7 + [1]*3).explode(10, chance=0.1) #make_anim(exalted2e, 'exalted2e') #make_anim(owod, 'owod') #make_anim(nwod, 'nwod')
legend.append('%d pips' % (pip_count, )) ax.legend(legend, loc='upper right') ax.set_xlabel('Roll-over number needed to hit') ax.set_ylabel('Chance (%)') ax.grid(which="both") die_counts_simple = [1, 2, 3, 4, 5, 6, 8, 10, 15, 20] die_counts_standard = die_counts_simple # coin fig = plt.figure(figsize=figsize) ax = plt.subplot(111) plot_pmf(ax, Die.coin(), die_counts_simple, '(d6>=4)') ax.set_xlim(left=left, right=right) ax.set_ylim(bottom=0) plt.savefig('output/sum_pool_4plus.png', dpi=dpi, bbox_inches="tight") # 2 plus on d6 fig = plt.figure(figsize=figsize) ax = plt.subplot(111) plot_pmf(ax, Die.coin(5 / 6), die_counts_simple, '(d6>=2)') ax.set_xlim(left=left, right=right) ax.set_ylim(bottom=0) plt.savefig('output/sum_pool_2plus.png', dpi=dpi, bbox_inches="tight")
def test_coin(): b = Die.bernoulli() c = Die.coin() assert b.pmf() == pytest.approx([0.5, 0.5]) assert c.pmf() == pytest.approx([0.5, 0.5])
ax.grid(which="both") ax.set_ylim(bottom=0) die_counts_simple = [1, 2, 3, 4, 5, 6, 8, 10, 15, 20] die_counts_standard = [1, 2, 3, 4, 5, 6] left = -4 right = 4 # 3+ on d6 fig = plt.figure(figsize=figsize) ax = plt.subplot(111) plot_opposed_fixed_side(ax, Die.coin(2 / 3), die_counts_simple, '(d6>=3)') ax.set_xlim(left=left, right=right) plt.savefig('output/add_pool_opposed_3plus.png', dpi=dpi, bbox_inches="tight") # coin fig = plt.figure(figsize=figsize) ax = plt.subplot(111) plot_opposed_fixed_side(ax, Die.coin(), die_counts_simple, '(d6>=4)') ax.set_xlim(left=left, right=right) plt.savefig('output/add_pool_opposed_4plus.png', dpi=dpi, bbox_inches="tight") # 5+ on d6
def make_anim(die, name): coef = 2.0 * die.mean() / die.standard_deviation() offset = offset_sd * offset_sd / (coef * coef) for frame_index, pool_size_a in enumerate(pool_sizes): die_bonus_a = coef * numpy.sqrt(pool_size_a + offset) pool_a = die.repeat_and_sum(pool_size_a) x = [] ccdf = [] for pool_size_b in range(0, 201): die_bonus_b = coef * numpy.sqrt(pool_size_b + offset) opposed = pool_a - die.repeat_and_sum(pool_size_b) p = opposed >= Die.coin() # Coin flip on ties. # p = (opposed > 0) / ((opposed > 0) + (opposed < 0)) # Reroll ties. x.append(die_bonus_b - die_bonus_a) ccdf.append(p) x = numpy.array(x) ccdf = numpy.array(ccdf) pmf = -numpy.diff(ccdf, prepend=1.0) # compute ks ks_ccdf = 0.5 * scipy.special.erfc(x / 2.0) ks = numpy.max(numpy.abs(ccdf - ks_ccdf)) # pmf plot fig = plt.figure(figsize=figsize) ax = plt.subplot(111) g_x = numpy.arange(left, right + 0.001, 0.001) g_pmf = numpy.exp(-0.5 * numpy.square(g_x / numpy.sqrt(2))) / ( 2.0 * numpy.sqrt(numpy.pi)) ax.plot(g_x, g_pmf, linestyle=':') ax.plot(x + 0.5 * numpy.diff(x, append=x[-1]), pmf * pool_a.standard_deviation() * 2.0, marker='.') ax.grid() ax.set_xlim(left, right) ax.set_xlabel('Difference in roll-over result') ax.set_ylabel('Normalized probability') ax.set_ylim(0, 0.3) ax.set_title('%d pool size for side A (KS = %0.2f%%)' % (pool_size_a, ks * 100.0)) pmf_frame_path = 'output/frames/pmf_%03d.png' plt.savefig(pmf_frame_path % frame_index, dpi=dpi, bbox_inches="tight") plt.close() # ccdf plot fig = plt.figure(figsize=figsize) ax = plt.subplot(111) g_x = numpy.arange(left, right + 0.001, 0.001) g_ccdf = 50.0 * scipy.special.erfc(g_x / 2.0) ax.plot(g_x, g_ccdf, linestyle=':') ax.plot(x, ccdf * 100.0, marker='.') ax.grid() ax.set_xlim(left, right) ax.set_xlabel('Side A roll-over disadvantage') ax.set_ylabel('Chance for side A to win (%)') ax.set_ylim(0, 100.0) ax.set_title('%d pool size for side A (KS = %0.2f%%)' % (pool_size_a, ks * 100.0)) ccdf_frame_path = 'output/frames/ccdf_%03d.png' plt.savefig(ccdf_frame_path % frame_index, dpi=dpi, bbox_inches="tight") plt.close() make_webm(pmf_frame_path, 'output/success_pool_roe_opposed_%s_pmf.webm' % name) make_webm(ccdf_frame_path, 'output/success_pool_roe_opposed_%s_ccdf.webm' % name)
from hdroller import Die result = '' base_bw_dice = 24 bw_scale = 2 for pbta_bonus in [-1, 0, 1, 2, 3]: num_bw_dice = int(bw_scale * pbta_bonus + base_bw_dice) bw_die = Die.coin(0.5).repeat_and_sum(num_bw_dice) pbta_high_chance = Die.d(2, 6) + pbta_bonus >= 11 pbta_mid_chance = Die.d(2, 6) + pbta_bonus >= 7 bw_target = base_bw_dice - (base_bw_dice // 2) bw_high_chance = bw_die >= bw_target + int(2 * bw_scale) bw_mid_chance = bw_die >= bw_target result += '\t'.join( str(x) for x in [ pbta_bonus, num_bw_dice, pbta_mid_chance, bw_mid_chance, pbta_high_chance, bw_high_chance ]) result += '\n' with open('output/pbta_bw.csv', mode='w') as outfile: outfile.write(result)
import matplotlib as mpl import matplotlib.pyplot as plt figsize = (8, 4.5) dpi = 120 def opposed_keep_highest(x, half_life=3): attack_ratio = numpy.power(0.5, x / half_life) ccdf = attack_ratio / (attack_ratio + 1.0) return Die.from_ccdf(ccdf, x[0]) left = -20 right = 20 x = numpy.arange(left * 2, right * 2+1) okh = opposed_keep_highest(x) laplace = Die.laplace(half_life=3) - Die.coin(0.5) opposed_simple = Die.d(10).explode(3) - Die.d(10).explode(3) - Die.coin(0.5) exploding = Die.d(10).explode(3) + Die.d(1, 12) opposed_exploding = exploding - exploding - Die.coin(0.5) legend = [ 'Logistic, half-life = 3', 'Opposed d10! + d12', 'Laplace, half-life = 3', 'Opposed d10!', ] # ccdf fig = plt.figure(figsize=figsize) ax = plt.subplot(111)
plt.savefig('output/explode_non_opposed_actual.png', dpi = dpi, bbox_inches = "tight") # big-small fig = plt.figure(figsize=figsize) ax = plt.subplot(111) ax.set_xlabel('Target number') ax.set_ylabel('Chance of hitting') ax.grid(which = "both") ax.set_title('Big-small') legend = [] for die_size in die_sizes: die = Die.coin(0.5).explode(100) * die_size + Die.d(die_size) y = [die >= t for t in numpy.arange(1, 41)] ax.semilogy(numpy.arange(1, 41), y) legend.append('d{0,1}!*%d+d%d' % (die_size, die_size)) ax.set_xticks(numpy.arange(0,41,5)) ax.set_xlim(left=0, right=40) ax.set_ylim(top=1, bottom=1e-2) ax.legend(legend, loc = 'lower left') plt.savefig('output/explode_non_opposed_big_small.png', dpi = dpi, bbox_inches = "tight") # ratio fig = plt.figure(figsize=figsize) ax = plt.subplot(111)
def test_mix_mixed(): die = Die.mix(Die.d4, Die.d6, mix_weights=Die.coin()) assert die.pmf() == pytest.approx([5 / 24] * 4 + [2 / 24] * 2)