Exemple #1
0
def make_ccdf_plot(die, offset=0, sd=None):
    if sd is None:
        gaussian = Die.gaussian(die)
    else:
        gaussian = Die.gaussian(die.mean(), sd)

    print('KS: %0.2f%%' % (die.ks_stat(gaussian) * 100.0))
    print('Above end: %0.2f%%' % ((gaussian > die.max_outcome()) * 100.0))
    gaussian_clipped = gaussian.clip(die)
    for outcome, die_chance, gaussian_chance in zip(die.outcomes(), die.ccdf(),
                                                    gaussian_clipped.ccdf()):
        print(
            '%d: %0.2f%%, %0.2f%%, %0.3f, %0.3f, %+0.2f%%' %
            (outcome, 100.0 * die_chance, 100.0 * gaussian_chance, die_chance /
             gaussian_chance, gaussian_chance / die_chance, 100.0 *
             (gaussian_chance - die_chance)))

    fig = plt.figure(figsize=figsize)
    ax = plt.subplot(111)
    ax.grid()

    ax.plot(die.outcomes() + offset, die.ccdf() * 100.0, marker='.')
    ax.plot(gaussian.outcomes() + offset, gaussian.ccdf() * 100.0, marker='.')
    ax.set_ylim(bottom=0, top=100)
    ax.set_ylabel('Chance (%) of rolling at least')

    return ax
Exemple #2
0
def iter_explode(die_size):
    for i in range(1, die_size - 1):
        for j in range(i + 1, die_size - 1):
            name = 'success on %d+, explode on %d+' % (i + 1, j + 1)
            explode_chance = (die_size - j) / die_size
            yield Die.from_faces([0] * i + [1] * (die_size - i)).explode(
                10, chance=explode_chance).rename(name)
Exemple #3
0
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")
Exemple #4
0
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)
Exemple #5
0
def iter_negative_success(die_size):
    for i in range(1, die_size - 1):
        for j in range(i + 1, die_size - 1):
            name = 'success on %d+, negative success on %d or lower' % (j + 1,
                                                                        i)
            yield Die.from_faces([-1] * i + [0] * (j - i) + [1] *
                                 (die_size - i - j)).rename(name)
Exemple #6
0
def bf_keep_highest(die, num_dice, num_keep):
    if num_keep == 0: return Die(0)
    counter = brute_force.BruteForceCounter()
    for rolls in numpy.ndindex((len(die), ) * num_dice):
        total = sum(sorted(rolls)[-num_keep:]) + num_keep * die.min_outcome()
        mass = numpy.product(die.pmf()[numpy.array(rolls)])
        counter.insert(total, mass)
    return counter.die()
Exemple #7
0
 def die(self):
     min_outcome = min(self.data.keys())
     max_outcome = max(self.data.keys())
     pmf = numpy.zeros((max_outcome - min_outcome + 1, ))
     for outcome, mass in self.data.items():
         pmf[outcome - min_outcome] += mass
     die = Die(pmf, min_outcome)
     return die
Exemple #8
0
def iter_standard_dice():
    for die_size in [3, 4, 5, 6, 7, 8, 9, 10, 12]:
        metadata = {
            'size' : die_size,
            'threshold' : 0,
            'feature' : 'standard die',
            'faces' : [str(x) for x in range(1, die_size+1)],
            'notes' : '',
        }
        yield Die.d(die_size), metadata
Exemple #9
0
def bf_keep_lowest(num_keep, *dice):
    if num_keep == 0: return Die(0)
    counter = brute_force.BruteForceCounter()
    shape = tuple(len(die) for die in dice)
    min_outcome = sum(sorted(die.min_outcome() for die in dice)[:num_keep])
    for rolls in numpy.ndindex(shape):
        total = sum(sorted(rolls)[:num_keep]) + min_outcome
        mass = numpy.product(
            [die.pmf()[roll] for die, roll in zip(dice, rolls)])
        counter.insert(total, mass)
    return counter.die()
Exemple #10
0
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)
    ax = plt.subplot(111)
    ax.grid()

    print('Gaussian mode:', gaussian.mode())

    ax.plot(die.outcomes() + offset, die.pmf() * 100.0, marker='.')
    ax.plot(gaussian.outcomes() + offset, gaussian.pmf() * 100.0, marker='.')

    ax.set_ylim(bottom=0)
    ax.set_ylabel('Chance (%) of rolling exactly')

    return ax
Exemple #11
0
def make_mos_plot(die, offset=0, sd=None):
    if sd is None:
        gaussian = Die.gaussian(die)
    else:
        gaussian = Die.gaussian(die.mean(), sd)

    fig = plt.figure(figsize=figsize)
    ax = plt.subplot(111)
    ax.grid()

    x = numpy.arange(min(0, die.min_outcome()), die.max_outcome() + 1)
    y_die = [die.margin_of_success(t).mean() for t in x]
    y_gaussian = [gaussian.margin_of_success(t).mean() for t in x]

    ax.plot(x + offset, y_die, marker='.')
    ax.plot(x + offset, y_gaussian, marker='.')

    ax.set_xlim(left=x[0], right=x[-1])

    ax.set_ylim(bottom=0)
    ax.set_ylabel('Mean margin of success')

    return ax
Exemple #12
0
def iter_dice(die_size, explode):
    for faces in iter_faces(die_size):
        base_die = Die.from_faces(faces)
        if not explode:
            yield base_die.rename('d' + str(die_size) + str(faces))
        else:
            if faces[0] == -1: return
            max_explode = min(
                faces.count(1) // 2, faces.count(0), die_size // 4)
            for explode_faces in range(1, max_explode + 1):
                if faces[-explode_faces] != 1: break
                explode_chance = explode_faces / die_size
                yield base_die.explode(10, chance=explode_chance).rename(
                    'd%d%s!%s' % (die_size, faces, explode_faces))
Exemple #13
0
def iter_exploding_dice():
    for die_size in [4, 6, 8, 10, 12]:
        for i in range(1, die_size):
            faces = [0]*i + [1]*(die_size-i)
            for num_explode in range(0, die_size-i+1):
                metadata = {
                    'size' : die_size,
                    'threshold' : i+1,
                    'feature' : 'none',
                    'faces' : [str(x) for x in faces],
                    'notes' : '',
                    }
                if num_explode == 1:
                    metadata['feature'] = 'explode on %d' % die_size
                elif num_explode == (die_size-i):
                    metadata['feature'] = 'explode on any success'
                elif num_explode > 0:
                    metadata['feature'] = 'explode on %d+' % (die_size - num_explode + 1)
                for j in range(num_explode):
                    metadata['faces'][-1-j] += '!'
                if die_size == 10 and i == 7 and num_explode == 1: metadata['notes'] = 'New World of Darkness'
                yield Die.from_faces(faces).explode(200, chance=num_explode/die_size), metadata
Exemple #14
0
dpi = 120


def plot_extremeness(ax, die, **kwargs):
    extremeness = numpy.minimum(die.cdf(), die.ccdf())
    ax.plot(die.outcomes(), extremeness, **kwargs)


def semilogy_extremeness(ax, die, **kwargs):
    print('%0.3f | %0.3f' % (die.mean(), die.standard_deviation()))
    extremeness = numpy.minimum(die.cdf(), die.ccdf())
    extremeness[extremeness > 0.5] = 1.0
    ax.semilogy(die.outcomes(), 1.0 / extremeness, **kwargs)


die_3d6 = Die.d(3, 6)
points_3d6 = die_3d6.relabel(point_buy_to_use).repeat_and_sum(6)

die_3d6r1 = Die.d(3, 5) + 3
points_3d6r1 = die_3d6r1.relabel(point_buy_to_use).repeat_and_sum(6)

die_3d6r2 = Die.d(3, 4) + 6
points_3d6r2 = die_3d6r2.relabel(point_buy_to_use).repeat_and_sum(6)

die_4d6kh3 = Die.d(6).repeat_and_keep_and_sum(4, keep_highest=3)
points_4d6kh3 = die_4d6kh3.relabel(point_buy_to_use).repeat_and_sum(6)

die_4d6r1kh3 = Die.d(5).repeat_and_keep_and_sum(4, keep_highest=3) + 3
points_4d6r1kh3 = die_4d6r1kh3.relabel(point_buy_to_use).repeat_and_sum(6)

die_5d6kh3 = Die.d(6).repeat_and_keep_and_sum(5, keep_highest=3)
Exemple #15
0
 def hitch_chance(self):
     return Die.min(*self.dice) <= 0
Exemple #16
0
        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')
Exemple #17
0
        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")
Exemple #18
0
def test_d_multiple():
    assert Die.d(1, 2, 2).weights() == pytest.approx([2, 3, 2, 1])
Exemple #19
0
def objective(sd):
    gaussian = Die.gaussian(10.5, sd)
    return Die.d20.ks_stat(gaussian)
Exemple #20
0
    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
Exemple #21
0
import matplotlib as mpl
import matplotlib.pyplot as plt

figsize = (8, 4.5)
dpi = 120

right = 4

max_success = 4

x = numpy.arange(0.0, right + 1e-6, 0.1)
y = [numpy.zeros_like(x) for i in range(max_success + 1)]

for i, half_life in enumerate(x):
    mean = half_life * numpy.log(2)
    die = Die.poisson(mean, max_outcome=max_success + 1)
    ccdf = die.ccdf()
    for num_successes in range(max_success + 1):
        y[num_successes][i] = ccdf[num_successes]

fig = plt.figure(figsize=figsize)
ax = plt.subplot(111)
ax.grid()

legend = []

for num_successes in range(1, max_success + 1):
    ax.plot(x, 100.0 * y[num_successes])
    if num_successes == 1:
        legend.append('%d success' % num_successes)
    else:
Exemple #22
0
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)
Exemple #23
0
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])
Exemple #24
0
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)
Exemple #25
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)
Exemple #26
0
        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)


make_anim(Die.coin(3 / 6), 'd6_4plus')
Exemple #27
0
# pf2e

fig = plt.figure(figsize=figsize)
ax = plt.subplot(111)

ax.set_xlabel('Number needed to hit')
ax.set_ylabel('Mean damage')
ax.grid(which='both')

legend = []

x = numpy.arange(-35, 20)

semilogy_mean_damage(ax,
                     Die.d(12) + 4,
                     x=x,
                     damage=6.5,
                     linestyle='--',
                     zorder=2.1)
legend.append('d12+4, damage = 6.5')

semilogy_mean_damage_mos(ax, Die.d(24) - 1, damage_mult=1.0, x=x)
legend.append('d24-2, damage = MoS + 1')

semilogy_mean_damage_pf2e_mos(ax, Die.d(20) + 1, damage_mult=6.5, x=x)
legend.append('d20, damage = 6.5 if hit, double if MoS >= 10')

ax.set_xlim(left=-20, right=20)
ax.set_ylim(bottom=1, top=50)
ax.legend(legend, loc='upper right')
def test_ks_stat_flat_number():
    assert Die(10).ks_stat(Die(10)) == 0.0
    assert Die(10).ks_stat(Die(9)) == 1.0
Exemple #29
0
def iter_simple_dice(die_size):
    # standard die
    metadata = {
        'size' : die_size,
        'threshold' : 0,
        'feature' : 'standard die',
        'faces' : [str(x) for x in range(1, die_size+1)],
        'notes' : '',
    }
    yield Die.d(die_size), metadata
    # exploding standard die (problem: VMR too high to be useful)
    """
    metadata = {
        'size' : die_size,
        'threshold' : 0,
        'feature' : 'exploding standard die',
        'faces' : [str(x) for x in range(1, die_size+1)],
        'notes' : '',
    }
    metadata['faces'][-1] += '!'
    yield Die.d(die_size).explode(10), metadata
    """
    # odd standard dice
    if die_size % 2 == 0 and die_size >= 6 and die_size <= 10:
        metadata = {
            'size' : die_size-1,
            'threshold' : 0,
            'feature' : 'standard die',
            'faces' : [str(x) for x in range(1, die_size-1+1)],
            'notes' : '',
        }
        yield Die.d(die_size-1), metadata
    # no feature
    for i in range(1, die_size):
        faces = [0]*i + [1]*(die_size-i)
        metadata = {
            'size' : die_size,
            'threshold' : i+1,
            'feature' : 'none',
            'faces' : [str(x) for x in faces],
            'notes' : '',
            }
        if die_size == 6 and i == 3: metadata['notes'] = 'Burning Wheel'
        if die_size == 6 and i == 4: metadata['notes'] = 'Shadowrun 4e'
        yield Die.from_faces(faces), metadata
    # negative on 1
    for i in range(2, die_size):
        faces = [-1] + [0]*(i-1) + [1]*(die_size-i)
        metadata = {
            'size' : die_size,
            'threshold' : i+1,
            'feature' : '-1 success on bottom face',
            'faces' : [str(x) for x in faces],
            'notes' : '',
            }
        if die_size == 10 and i == 6: metadata['notes'] = 'Old World of Darkness'
        yield Die.from_faces(faces), metadata
    # double on max
    for i in range(1, die_size-1):
        faces = [0]*i + [1]*(die_size-i-1) + [2]
        metadata = {
            'size' : die_size,
            'threshold' : i+1,
            'feature' : '2 successes on top face',
            'faces' : [str(x) for x in faces],
            'notes' : '',
            }
        if die_size == 10 and i == 6: metadata['notes'] = 'Exalted 2e'
        yield Die.from_faces(faces), metadata
    # explode
    for i in range(1, die_size):
        faces = [0]*i + [1]*(die_size-i)
        metadata = {
            'size' : die_size,
            'threshold' : i+1,
            'feature' : 'explode on top face',
            'faces' : [str(x) for x in faces],
            'notes' : '',
            }
        metadata['faces'][-1] += '!'
        if die_size == 10 and i == 7: metadata['notes'] = 'New World of Darkness'
        yield Die.from_faces(faces).explode(100, chance=1/die_size), metadata
    """
Exemple #30
0
def iter_double_success(die_size):
    for i in range(1, die_size - 1):
        for j in range(i + 1, die_size - 1):
            name = 'success on %d+, double success on %d+' % (i + 1, j + 1)
            yield Die.from_faces([0] * i + [1] * (j - i) + [2] *
                                 (die_size - i - j)).rename(name)