I have made a small script for my D&D group that, when a spell is cast, chooses whether or not another random spell gets cast, the ordinal spell gets cast, or nothing happens (with a 50%, 25%, 25% ratio, respectively).
In order to do this, I made a pd.DataFrame
of every possible spell in the game (using data from http://dnd5e.wikidot.com/spells), and then appended two data frames (one that says "Original Cast" in the spell name and the other that says "Nothing Happens"), each with len == len(df)/2
so as to have a full df
twice the size of the original, as shown in the code below.
import pandas as pdimport numpy as npfrom os import urandomdef create_df(): df = pd.read_csv("all_spells.csv", encoding="utf-8") df = df.dropna(axis=0, subset="Spell Name") df_b = df[~df["Spell Name"].str.contains(r"\((.*?)\)")].reset_index(drop=True) OG_cast = ['Orginal Cast' for i in range(int(len(df.index)/2))] No_Magic = ['Nothing Happens' for i in range(int(len(df.index)/2))] Nadda = [None for i in range(int(len(df.index)/2))] df_same = pd.DataFrame(columns=df.columns, data={ df.columns[0]: OG_cast, df.columns[1]: Nadda, df.columns[2]: Nadda, df.columns[3]: Nadda, df.columns[4]: Nadda }) df_nothing = pd.DataFrame(columns=df.columns, data={ df.columns[0]: No_Magic, df.columns[1]: Nadda, df.columns[2]: Nadda, df.columns[3]: Nadda, df.columns[4]: Nadda }) df_full = pd.concat([df_b, df_same, df_nothing], axis=0).reset_index(drop=True) return df_full
df_full.sample(n=10)
is shown below, for reference.
+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| | Spell Name | School | Casting Time | Range | Duration | Components |+=====+============================+===============+================+=========+================================+==============+| 12 | Psychic Scream | Enchantment | 1 Action | 90 feet | Instantaneous | S |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| 18 | True Polymorph | Transmutation | 1 Action | 30 feet | Concentration up to 1 hour | V S M |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| 670 | Orginal Cast | | | | | nan |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| 193 | Conjure Woodland Beings | Conjuration | 1 Action | 60 feet | Concentration up to 1 hour | V S M |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| 795 | Orginal Cast | | | | | nan |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| 218 | Otilukes Resilient Sphere | Evocation | 1 Action | 30 feet | Concentration up to 1 minute | V S M |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| 353 | Levitate | Transmutation | 1 Action | 60 feet | Concentration up to 10 minutes | V S M |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| 839 | Nothing Happens | | | | | nan |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| 459 | Silent Image | Illusion | 1 Action | 60 feet | Concentration up to 10 minutes | V S M |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+| 719 | Orginal Cast | | | | | nan |+-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
I then call the script below to get what happens when a spell is cast.
df = create_df() seed = int(np.random.uniform(0, len(df.index)*10)) spell = df.sample(1, random_state=seed)['Spell Name'].values[0] print("The spell cast is:", spell)
To test if this was giving me the distribution I wanted (50% of the time, there is a random spell cast, 25% nothing happens, and 25% the spell works as intended), I ran
OC = 0 NH = 0 N = 0 for i in range(100000): seed = int(np.random.uniform(0, len(df.index)*10)) arb = df.sample(1, random_state=seed)['Spell Name'].values[0] # print(arb) if arb == 'Orginal Cast': OC += 1 elif arb == 'Nothing Happens': NH += 1 else: N += 1 print(OC, NH, N)
And instead of getting 50/25/25 (for the things stated above), I get an extremely consistent 47/26.5/26.5. Does anyone know why this is happening? And does anyone have a better idea and random sampling so as to more consistently get the correct ratio?