Fix random value generator, new CLI

This commit fixes a defect in the random value generator introduced when
the random_sets module was refactored. It also introduces three useful
CLI options, for generating roll tables of random trinkets, wild magic
surges, and psychadelic effects.
This commit is contained in:
evilchili 2024-01-16 22:05:06 -08:00
parent 2b3e82114f
commit 1c0bd7357d
7 changed files with 456 additions and 222 deletions

View File

@ -13,7 +13,7 @@ Clone the repo and install the virtual env:
Invoke the CLI:
```
% poetry run roll-table example.yaml
% poetry run roll-table custom example.yaml
┏━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
┃ Roll ┃ Rarity ┃ Color ┃ Notes ┃
┡━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩

View File

@ -2,7 +2,6 @@ from rolltable.types import RollTable
import typer
from enum import Enum
from rich import print
from rich.table import Table
from pathlib import Path
from typing import List
@ -16,8 +15,35 @@ class OUTPUT_FORMATS(Enum):
markdown = 'markdown'
@app.command("roll-table")
def create(
@app.command("psychadelic-effects")
def psycheffects():
"""
Generate a roll table of psychadelic effects.
"""
rt = RollTable([(Path(__file__).parent / 'sources' / 'psychadelic_effects.yaml').read_text()])
print(rt.as_table())
@app.command("trinkets")
def trinkets():
"""
Generate a roll table of random junk.
"""
rt = RollTable([(Path(__file__).parent / 'sources' / 'trinkets.yaml').read_text()])
print(rt.as_table())
@app.command("wild-magic")
def wildmagic():
"""
Generate a wild magic surge table.
"""
rt = RollTable([(Path(__file__).parent / 'sources' / 'wild_magic.yaml').read_text()])
print(rt.as_table())
@app.command("custom")
def custom(
sources: List[Path] = typer.Argument(
...,
help="Path to one or more yaml-formatted source file."),
@ -43,7 +69,7 @@ def create(
)
):
"""
CLI for creating roll tables.
Create roll tables from custom sources.
"""
rt = RollTable([Path(s).read_text() for s in sources], frequency=frequency, die=die, hide_rolls=hide_rolls)

View File

@ -0,0 +1,40 @@
metadata:
headers:
- Effect
- Name
- Description
- Mechanic
frequencies:
default:
Effect: 1
Effect:
- Elation:
- You are consumed with love for all things
- Charmed by all creatures, +2 to AC
- Paranoia:
- Everyone is out to get you. Everyone. What are they hiding?
- Automatically succeed all insight checks vs deception; no one is your ally
- Baked:
- Everything is hilarious, and you are always hungry for snacks.
- Disadvantage on all attack rolls, Advantage on saves against psychic effects.
- Wasted:
- Your speech is slurred, you have difficulty thinking clearly, and have mild vertigo
- You cannot use reactions; disadvantage on all checks and saves using DEX. +2 to AC.
- Sleepy:
- Why do we have to keep doing things? Doing things is hard.
- Your movement speed is reduced by 10ft and you cannot use reactions. Advantage on saves against psychic effects.
- Horror:
- You have stared into the abyss of your soul and found only blackness.
- Frightened by all creatures. Disadvantage on all saves against psychic effects.
- Mania:
- You are possessed by feverish mania; ideas and words tumble out of as quickly as possible. You cannot stop them.
- Automatic critical success on all perception checks; advantage on DEX saves; disadvantage on all attack rolls.
- Rage:
- You are seething with anger at the injustice of your situation. Everything is a personal attack, a challengge.
- You are raged.
- Cosmic Awareness:
- Your mind expands to contain the cosmos, which fills you with a wonder you cannot convey. Whenever you speak it is in metaphor.
- Once per day, you can decide something about the world is true and it will become so.
- Ego-Death:
- Your sense of self, your identity, is revealed as an empty construct. You are all things and all things are you.
- Whenever an ally takes damage, you take 1 point of psychic damage. Whenever an ally heals, you regain 1 HP.

View File

@ -0,0 +1,213 @@
metadata:
headers:
- Trinket
frequencies:
default:
Trinket: 1
Trinket:
- A carved ivory mimic.
- A carved marble elephant.
- A small round cactus with two eyes.
- A pocket book of dwarven poetry.
- A bronze box containing a tiny wooden owl.
- A solid blue metal sphere, one inch in diameter, with three parallel grooves around the circumference.
- A pouch containing ten dried peas.
- A ceramic puzzle cube, with each face divided into four independently rotating squares enameled with astronomical signs.
- A sheet of vellum on which is crudely painted a herbal plant that you have yet to identify.
- A petrified frog.
- A twenty-sided die.
- A cut yellow chrysanthemum that never dies.
- A palm-sized iron cage; the door doesn't shut properly, as the tiny lock was broken from the inside.
- A blob of grey goo, slippy but safe to touch, kept in a ceramic pot.
- A glowing blue-green line, six inches long, but with no discernible radius.
- A pretty conch shell.
- A scrap of paper on which is written, in Goblin, "My dearest Bess,".
- A keychain holding the head of a broken key.
- An echo pearl from the depths of the Vibration Lake.
- A toy crossbow.
- A fossil of an extinct many-limbed critter.
- A brass prosthetic nose.
- A corkscrew.
- A dried poison gland of a jagged fish.
- A bronze gear on which is etched the word "Moon".
- A map of a labyrinth, on which is penciled a line that starts at the centre but fails to connect to the entrance.
- A cube of ice that never melts.
- A square of ironsilk sewn by the geargrubs of ancient Siclari.
- An ivory knitting needle.
- A peacock feather.
- A travel set of paints; someone has used up all the black.
- A wig of short platinum-blonde hair.
- A child's charm bracelet.
- A small bar of orichalcum, a metal only mentioned in ancient literature.
- A deed to a tower.
- An invitation to a formal ball to be held 1129 years ago.
- A smoking pipe carved from granite.
- A vial of scented oil.
- A preserved basilisk eye.
- An intricate knot that nobody seems to know how to tie or untie sand sailors believe it to be bad luck.
- Pages ripped out of an accounting journal of a local merchant.
- A ring with a poison reservoir for slipping into drinks and a tiny razor edge for cutting purse-strings.
- A glass globe of swirling green goop.
- A portfolio of pressed flowers, none of which still exist.
- A small handbook of foreign coins, for travelers to identify denominations.
- A slightly out of date guidebook to foreign inns, taverns, and transportation.
- Two false fingernails painted with mysterious symbols.
- A set of cosmetic tools for cleaning the ears.
- A harmless stage dagger with retracting blade and blood-compartment.
- A floating glass orb that follows you around and makes whirring sounds.
- A key that can lock any door, but unlock none.
- A translucent coin.
- A bronze ring engraved with dark symbols.
- A thimble on which is an enamel painting of a turtle.
- A puzzle box that cannot be solved.
- A pair of badly worn hairdressing scissors.
- A wax hand shaped to hold a large cup.
- A measuring tape, marked in ink at 23 inches.
- A seashell that is silent when held up to your ear.
- One piece of unknown paper currency with no obvious denomination.
- A bootlace entwined with gold thread.
- A dented sheriff's badge.
- A belt buckle.
- A letter of complaint to a toy shop owner written in high elvish.
- A decorative leather stud.
- A penny whistle that plays the same note no matter which holes are covered.
- A ticket admitting an adult and child onto a thing called a "semiotic tram".
- A small glass vial holding three eyelashes.
- A tub of petrified putty.
- A leather shoe made for a dog.
- A doll head with no hair and poorly applied makeup.
- A pewter spork.
- Illustrated instructions on how to make a paper hat.
- A clear glass dish with four round notches around the outside edge.
- A wire circlet that bestows upon its wearer perfect posture.
- A small hand-sized box covered with numbered buttons.
- An empty whiskey tumbler that causes any liquid poured into it to become bourbon.
- A hunk of metal which appears to be several gears jammed together at unnatural and impossible angles; attempting to turn it causes it to emit a horrible shrieking sound.
- A crystal prism that refracts shadow instead of light.
- A smokeless and odorless candle.
- A flat disc of layered metal and prismatic glass with a hole in the centre.
- An ornate pewter tankard made without a bottom.
- A wooden device designed to be gripped in two hands; two levers protrude from the top, and two triggers from the underside.
- Two perfectly identical pine cones.
- A sponge that can absorb 60 gallons of ale (and only ale).
- A pepper grinder containing an unlimited supply of pepper unless opened, at which point it becomes half empty.
- A poorly cultivated bonsai juniper in a glazed ceramic pot.
- An oval-shaped soapstone tablet inscribed with a short list of religious prohibitions.
- A wooden doll with a door that opens to reveal a slightly smaller, identical, doll; this one is empty, perhaps there are still smaller dolls that are missing?
- A stone figure of a snake that changes positions after every full moon.
- A silver key of unknown origins on a leather cord as a pendant that emits strange magical energy.
- The right half of a broken bronze circlet with a light leafy pattern that when placed on your head, stays in place as though the other half was still there.
- A small silver rod which when rolled between your hands emits sounds as though a lute were being played by a master softly nearby.
- A music box that can only be heard by someone who has wound it at least once.
- A small stone cube that, when tapped with a rod of metal, looks as though it were made of that metal for a few seconds.
- A wooden sphere with a white marking that always faces the sun, and a black marking that always faces the moon.
- An opaque jar that cannot be opened or broken, no matter how hard you try.
- A journal that details the great adventures of a hero you have never heard of, complete with vivid descriptions of nonsensical creatures and terms, all written in messy handwriting, but with impressive diction.
- A tiny sack that, when opened, is full of sand, but feels as heavy as a large stone when lifted.
- A puppet in the likeness of someone you distantly know, that echoes your movements when you (and only you) place it on the ground.
- An iron rod that bends in unusual ways when you look directly at it, but rights itself when you look away.
- A book that perfectly records the holder's dreams when held while sleeping.
- A tin pot that is just the right size for you to wear as a helmet.
- A marble sculpture of a tiny elf holding a lute seated on a chair that plays music every so often.
- A piece of pure white cloth that never gets dirty.
- A hat that never gets wet.
- A mask that copies its wearer's facial expressions.
- A humanoid skull with ethereal green orbs in its eye sockets.
- A wineskin that only holds wine. Any other liquid pours out after a few seconds.
- A leather belt that, when worn, glows with faint blue light.
- A flask that freezes any liquids stored in it at midnight, and unfreezes them at noon.
- A book with words that change every time it is read.
- A cube with tiny animals wandering on each face that change when they cross onto a different face.
- A mummified hand that wiggles when pointed at the ground.
- A terribly written novel whose plot seems to match events in the reader's life.
- A crown made out of ice that never melts.
- A piece of string that always emits smoke, like the wick of a snuffed candle.
- A rod of indeterminate metal that changes its length at random every other day.
- A stone carved to look like a head that occasionally speaks, asking questions that change every time.
- A petrified wooden stick that glows brightly when held by an undead creature.
- A flute that, when played, makes the sound of a random instrument, though never a flute.
- A copper ring that weighs literally nothing but remains precisely where it is placed.
- A towel embroidered with high elivsh writing which reads "In case of mind flayer, wear towel."
- A set of brass wind chimes that chime when still and are silent when moving.
- A grotesque statuette of a humanoid with rat-like features.
- A handbook of etiquette for nobles. Its contents can only be remembered for 6 seconds.
- A rolled parchment depicting a translation key for a ficticious language.
- A wooden plank that refuses to burn.
- A detailed guide on the anatomy of rocs.
- A compass that points towards the nearest bottle of rum.
- A stone that weighs 5 pounds but floats in liquid.
- A diagram of a forest on an island with no named artist.
- A brass locket with a strange rune carved inside.
- A green coat with numerous pockets, each with a separate piece of a moustache trimming kit inside.
- A vial of tree sap.
- A rod of metal that produces tiny sparks from a red marking when a black button on the other end is pressed.
- A wooden cane that, when placed on the ground, stands perfectly straight, and cannot be tipped over.
- A feather with a piece of red string tied on the end of the shaft.
- A book that you faintly remember from your childhood that you thought was lost for many years.
- A wooden training sword sized for a child.
- A perfectly normal brown leather glove that fits either hand.
- A golden statue of a beautiful maiden. It glows faintly in the presence of money.
- A gear that refuses to mesh with any other.
- A suit of armour made from oak wood, sized for a doll, painted in vivid red and black.
- A jug that turns liquids stored in it into goat's milk after a few minutes.
- A waxy white flower in a tiny pot that constantly moves as though in a light breeze.
- A robe with a tag on the inside that reads "to my dearest pupil" in draconic.
- A beige jar of red ointment without a label.
- A calligrapher's brush.
- A small cylinder of stone that smells faintly of blood.
- A tunic that smells of the sea.
- A curious looking pair of goggles with the words "Property of Ice! DON'T TOUCH!" scrawled into the side.
- A small blue cube that gives off a faint orange glow.
- A small orb with water and a small living jellyfish inside.
- A cat figurine.
- A hat that has a secret pocket on the inside.
- A bar of scented soap.
- A scrap of parchment with the phrase "I aint'nt dead" written on it in Common.
- An unusually sharp spoon.
- An unusually small humanoid skull.
- A bit of rock from a headstone.
- A tiny bag of purple powder.
- A box of black licorice.
- A strangely shaped bone.
- A sealed, corked bottle with a single globule of blood that never spreads or dries.
- A detailed guide to making pickled foods.
- A palm-sized coin with the carved image of a dragon's head on each side.
- A miniature shield.
- A sea shell with a strange rune carved into it.
- An arm band in the shape of a snake, with emeralds for eyes.
- An old coin, showing a hare on one side and the moon on the reverse.
- A bird carved out of lapis lazuli.
- A small pouch filled with various animal teeth.
- A tiny painting showing a vulture carrying a bone in its beak.
- An ingot of copper with an unusual hue.
- A small notebook full of drawings of food.
- A drawing of a flower with open petals that only appear open when viewed from an angle.
- A perfectly flat wheel made from terracotta.
- The journal of a philosopher, full of wise sayings and anecdotes.
- A malevolent-looking raven skull that has been charred black.
- A small glass case containing several glossy butterfly wings.
- A small glass box containing an undead frog.
- A guide to changing bowstrings written in draconic but full of grammatical errors.
- A ceramic troll statuette with no arms, just legs.
- A sliver ring that feels very slippery.
- An ornately carved figurine of a storm giant made from bone.
- A fork.
- A shoe made from crystal.
- A small wooden box of vibrantly-coloured powders, each colour in its own tiny drawer.
- A silver hand mirror with a dragon etched onto the back.
- A miniature porcelain teapot.
- A bejeweled statuette of a dragonborne knight.
- A stone carving of a piece of bacon.
- A horn that has been cut cleanly in half.
- A small clay pig.
- A small metal can containing a non-Newtonian black fluid that sparks when touched.
- A necklace strung with tiny beetles.
- A fist-sized green seed covered in brown spines.
- A fist-sized metal frog.
- A glass figurine in the shape of a giant ant.
- A petrified wooden spoon.
- A jeweled goblet that never spills.
- A bowl covered in ornate designs depicting hill giants in combat with dwarves.
- A set of ceramic castles that have a space for a candle in the middle. When the candle is lit the castle looks like it has lights on in the windows.
- A purple amulet in the shape of a frog's head.
- A vine covered in thorns that writhes occasionally.

View File

@ -0,0 +1,137 @@
# Wild Magic Table
metadata:
headers:
- Type
- Effect
- Notes
frequencies:
default:
Boon: 0.20
Bane: 0.20
Weird: 0.55
Trouble: 0.05
boon:
Boon: 0.65
Bane: 0.15
Weird: 0.15
Trouble: 0.05
bane:
Boon: 0.15
Bane: 0.65
Weird: 0.15
Trouble: 0.05
weird:
Boon: 0.05
Bane: 0.05
Weird: 0.85
Trouble: 0.05
trouble:
Boon: 0.1
Bane: 0.2
Weird: 0.3
Trouble: 0.4
Boon:
- A +1 martial weapon appears in an unoccupied space within 15ft of you: The weapon disappears if it is carried out of the Sahwat desert.
- Barkskin is cast on you: 1 minute duration.
- Stoneskin is cast on you: 1 minute duration.
- Invisibility is cast on you: 1 minute duration.
- You gain 1d4 temporary hit points:
- The Enlarge spell is cast on you: 1 minute duration.
- Echos of a celestial choir fills the air. Allies within 30ft of you gains 2d4+4 HP: Good-aligned creatures only.
- You are hasted.: 1 minute duration.
- You have truesight: 1 minute duration.
- A third eye opens in your forehead: Curse. Advantage on perception checks.
- Levitate is cast on you: 1 minute duration.
- Your next spell attack does max damage:
- You have resistance to nonmagical damage: 1 minute duration.
- You have resistance to magical damage: 1 minute duration.
- You have advantage on ability checks and saves against spells and magical effects: 1 minute duration
- d100 gold pieces appear in an unoccupied space within 30ft of you:
- You regain one 1st level spell slot: No effect if you do not have 1st level slots.
- Mirror Image is cast on you: 1 minute duration.
- No effect: If the caster dies in the next minute, they are resurrected with 1 HP
- You smell like apple pie: Every creature within 15ft of you must succeed on a DC 13 CHA save or be charmed by you for 1d4 rounds.
- Whenever you hit with a spell attack, it does an additional 1d6 radiant damage: 1d4 rounds
Bane:
- Magic Missile is cast at first level, targeting random creatures 60ft of you: One dart always hits the caster.
- Color spray is cast on a random creature within 30ft of you: 1 minute duration.
- The Reduce spell is cast on you: 1 minute duration.
- The call lightning spell appears centered on a random point 60ft away from you: Lasts for 1d4 rounds.
- A Fireball spell detonates centered on a random point 30ft away from you:
- 1d6 random creatures within 30ft of you must succeed on a Wisdom save or fall unconscious: Lasts until they take damage or someone spends an action to wake them up.
- A 50ft radius sphere of purple gas appears centered on a random point 25ft from you: The sphere lasts 1d4 rounds. Any creature which ends their turn in the sphere takes 1d10 poison damage.
- Infernal whispers fill the air. All creatures within 100ft of you must succeed on a DC14 Wisdom saving throw or take 1d6 psychic damage:
- You are slowed: 1 minute duration.
- Spectral shackles appear on the legs of each creature within 60ft of you: Speed reduced to 5ft. Lasts 1 round. Can be dispelled.
- You are vulnerable to nonmagical damage: 1 minute duration.
- You are vulnerable to magical damage: 1 minute duration.
- You have disadvantage on ability checks and saves against spells and magical effects: 1 minute duration
- You expend one additional 1st level spell slot: No effect if you do not have 1st level slots.
- Mirror Image is cast on hostile creature you can see: 1 minute duration.
- Your skin glows like a small sun. Every creature that can see you is blinded: Until the start of next turn.
- You use all your remaining movement to move in a random direction:
Weird:
- Your skin turns bright blue: Curse.
- It begins snowing from a small cloud 10ft directly over you: Duration 1d4 days. Can be dispelled.
- If it is daytime, you believes it is night. If it is night, you believe it is daytime: Curse. Duration 1d4 days.
- A Zone of Truth spell aura extends from you to a range of 300ft: Duration one hour. Can be dispelled.
- You hear every sound magnified 10x. You are weak to thunder damage: Curse. Duration 1d4 hours.
- Whatever you are holding in your left hand turns into a sticky bun: Duration 1 minute. Can be dispelled.
- You are wearing a hat shaped like your favourite animal:
- If you were alive, you are now undead. If you were undead, you are now alive: Curse.
- A surprised 3rd level wizard appears in an unoccupied space within 15ft of you: random alignment; will disappear again in 1d4 rounds.
- A planar portal opens in the fabric of spacetime in an unoccupied space within 30ft of you. 1d4 modrons appear and begin repairing it.: Disappears after 3 rounds.
- Dispel magic is cast at 4th level on you and every creature within 60ft of you:
- A random trinket falls from the sky: Roll on the trinkets table
- Desert flowers appear in the hair of a random creature within 30ft of you:
- You no longer cast a shadow: Curse. At dawn, shadow attacks as a wraith.
- You swap places with a random creature within 60ft:
- A random creature within 30ft of you is polymorphed into a bowl of petunias: Duration 1 round. 1 HP, AC 10.
- You smell like ham: Curse. Duration 1d4 days.
- A pillar of stone 15ft in diameter and 30ft tall erupts from the ground centered on you:
- A pig appears in the nearest unoccupied space: Immune to all damage. Disappears after 1d4 rounds.
- 1d4 stone statues appear depicting you at key moments in your life:
- A CR 15+ creature apperas 120ft away from you: Major Illusion.
- All plant life within 300ft of you dies:
- All plant life within 300ft of you doubles in size:
- A humanoid man appears and attacks you with 20lb fish: +6 to hit, 1d10 bludgeoning damage. Disappears afer attack.
- 1d8 ghostly figures appear, observing: Duration 1d4 rounds.
- Your body is covered in fur: Curse.
- You are teleported to an unoccupied space 15ft away:
- You age increases by 5d10 years: Can be reversed by Greater Restoration.
- 50lbs of cabbages appear stacked in a pyramid in an unoccupied space: You hear a faint voice yelling, "My cabbages!"
- You are immune to the effects of alcohol: Curse.
- A floating eyeball 1ft in diameter follows you: Disappears after 1d6 days.
- You can only shout when speaking: 1 minute duration.
- Your arms are polymorphed into tentacles: Cannot cast somatic spells. 1 minute duration.
- You are frightened of your favourite color: 1 hour duration.
- You are surrounded by magical silence: 10 minute duration.
- You are encased in a 10ft diameter sphere of glass: AC 10, 15 HP. Vulnerable to bludgeoning.
- A plant within 30ft of you sprouts limbs and runs away: walking speed 80ft.
- Invisibility is cast on every creature you can see: 1 minute duration.
- Any item held by a creature flies from its grip and lands in an unoccupied space 10ft away:
- Illusory butterflies flutter in 5ft radius about you: 1 minute duration.
- You generate a Draining Aura in a 10ft radius: Any creature ending its turn in the aura takes 1d4 necrotic damage. You heal this amount. Duration 1 minute.
- Levitate is cast on 1d4 random creatures within 120ft of you: 1 minute duration.
- No effect: Next phrase spoken by caster becomes true.
- Disguise Self is cast on you: You look like whoever you're most afraid of in life.
- Each creature that can see you must succeed on a DC 13 WIS save or laugh uncontrollably: Until the end of their next turn.
Trouble:
- A random creature within 60ft of you (including you) is banished: Duration 1 minute.
- The Resilient Sphere spell is cast on a random creature (including you) within 30ft of you:
- 1d6 zombies merge from the ground in random unoccupied spaces within 100ft of you:
- A swarm of beetles appears in your space:
- A pit 20ft deep and 5 feet wide appears directly beneath you: 2d6 fall damage.
- You swap current HP with a random creature within 30ft of you:
- 2d8 imps are summoned from the Nine Hells: Will attack closest creature. Disappear after 1d4 rounds.
- Nothing happens: Next time you speak, you breathe fire in a straight line 30ft long and 5ft wide. DEX 13 save or take 2d6 fire damage.
- Gravity reverses in a 30ft radius: Until start of next turn. Can be dispelled.
- Your left eye is a ruby worth 300 GP: You can see alignment. Eye restored with Greater Restoration.
- A random metal weapon bursts in a 60ft cloud of metallic shards: DC 13 DEX or 3d6 piercing damage.
- You sweat blood from every pore: CON 14 or your max HP is reduced by 1d4 per round.
- A random creature within 30ft becomes a pillar of sand: Effect ends if a drop of water touches the pillar.
- A random PC suffers from a random lingering injury: DMG pp272
- 1d6 random spaces within 100ft of you becomes lava 5ft deep: 10d10 Fire damage. Roll 2d10 for X/Y coords.
- Every creature and object within 120ft of you moves 10ft in a straight line towards you: Any object or creature that collides with something else takes 1d4 bludgeoning damage.
- You hear ethereal music: Lasts until dawn. You cannot long rest during this time.
- 10ft tall crystal spikes erupt from the ground in 1d10 spaces within 100ft of you: Any creature in a space must succeed on a DC 15 DEX save or take 3d6 piercing damage and be restrained.

View File

@ -56,14 +56,27 @@ class RollTable:
@property
def _values(self) -> List:
"""
For each data source, select N random values, where N is the size of the die.
we then zip those random values so that each member of the generated list
contains one value from each data source. So if _data is:
[
['axe', 'shortsword', 'dagger'],
['fire', 'ice', 'poison'],
]
and the die is 2, the resulting generated values might be:
[
['axe', 'fire'],
['dagger', 'ice'],
]
"""
if not self._generated_values:
ds_values = [t.random_values(self.die) for t in self._data]
self._generated_values = []
for face in range(self._die):
value = []
for index, ds in enumerate(ds_values):
value += ds_values[index][face]
self._generated_values.append(value)
self._generated_values = list(zip(*[
t.random_values(self.die) for t in self._data
]))
return self._generated_values
@property

View File

@ -1,217 +1,22 @@
import pytest
from rolltable import types
fixture_metadata = """
metadata:
headers:
- Header 1
- Header 2
- Header 3
die: 10
frequencies:
default:
Option 1: 0.3
Option 2: 0.5
Option 3: 0.2
nondefault:
Option 1: 0.0
Option 2: 0.1
Option 3: 0.9
"""
fixture_source = """
Option 1:
- choice 1: description 1
- choice 2: description 2
- choice 3: description 3
Option 2:
- choice 1: description 4
- choice 2: description 5
- choice 3: description 6
Option 3:
- choice 1: description 7
- choice 2: description 8
- choice 3: description 9
"""
fixture_one_choice = """
option 1:
- choice 1: description 1
"""
fixture_repeated_choices = """
option 1:
- choice 1: description 1
- choice 1: description 1
- choice 1: description 1
"""
fixture_no_descriptions = """
metadata:
headers:
- option
- choice
option 1:
- choice 1
"""
fixture_combined_A = """
A1:
- A choice 1
- A choice 2
- A choice 3
A2:
- A choice 4
- A choice 5
- A choice 6
A3:
- A choice 7
- A choice 8
- A choice 9
"""
fixture_combined_B = """
metadata:
headers:
- HeaderB
- HeaderB_Choice
B1:
- B choice 1
B2:
- B choice 2
B3:
- B choice 3
"""
fixture_no_options = """
metadata:
headers:
- headerA
- headerB
B1:
B2:
B3:
"""
fixture_lists_and_dicts = ["""
#
# category one two three four
# Category foo bar baz quz
#
metadata:
headers:
- category
- one
- two
- three
- four
list:
- foo:
- bar
- baz
- quz
""", """
#
# category one two three four
# Category foo bar baz quz
#
metadata:
headers:
- category
- one
- two
- three
- four
dict:
foo:
- bar
- baz
- quz
bar:
- a
- b
- c
"""]
from pathlib import Path
from rolltable.types import RollTable
@pytest.mark.parametrize('fixture', fixture_lists_and_dicts)
def test_lists_and_dicts(fixture):
t = types.RollTable([fixture], die=1)
assert(str(t))
sources = Path(__file__).parent / '..' / 'rolltable' / 'sources'
flat_list = (sources / 'trinkets.yaml').read_text()
dict_of_dicts = (sources / 'wild_magic.yaml').read_text()
dict_of_lists = (sources / 'psychadelic_effects.yaml').read_text()
def test_combined_tables():
combined = types.RollTable([fixture_combined_A, fixture_combined_B], die=6)
assert str(combined)
def test_table_end_to_end():
assert str(types.RollTable([fixture_source]))
def test_table_end_to_end_with_metadata():
assert str(types.RollTable([fixture_metadata + fixture_source]))
def test_table_frequency():
t = types.RollTable([fixture_metadata + fixture_source], frequency='nondefault')
assert t._data[0].frequencies['Option 1'] == 0.0
assert t._data[0].frequencies['Option 2'] == 0.1
assert t._data[0].frequencies['Option 3'] == 0.9
def test_one_option():
t = types.RollTable([fixture_one_choice], die=1)
assert t._values == [['option 1', 'choice 1', 'description 1']]
def test_collapsed():
t = types.RollTable([fixture_repeated_choices], die=6)
assert len(list(t.rows)) == 2 # (+1 for headers)
def test_not_collapsed():
t = types.RollTable([fixture_repeated_choices], die=6)
assert len(list(t.expanded_rows)) == 7 # (+1 for headers)
def test_no_descriptions():
t = types.RollTable([fixture_no_descriptions], die=1)
assert 'd1' in str(t)
assert 'option 1' in str(t)
def test_no_options():
t = types.RollTable([fixture_no_options])
assert str(t)
@pytest.mark.parametrize('table', [
types.RollTable([fixture_no_options]),
types.RollTable([fixture_one_choice]),
types.RollTable([fixture_metadata + fixture_source]),
types.RollTable([fixture_source]),
@pytest.mark.parametrize('data, expected', [
([dict_of_dicts], ['d1000 ', 'A third eye', 'Advantage on perception checks']),
([flat_list], ['d1000 ', 'ivory mimic']),
([dict_of_lists], ['d1000', 'Cosmic', 'mind expands', 'it will become so']),
])
def test_yaml(table):
assert table.as_yaml()
def test_text():
assert repr(types.RollTable([fixture_no_options]))
assert repr(types.RollTable([fixture_one_choice]))
assert repr(types.RollTable([fixture_metadata + fixture_source]))
assert repr(types.RollTable([fixture_source]))
@pytest.mark.parametrize('table', [
types.RollTable([fixture_no_options]),
types.RollTable([fixture_one_choice]),
types.RollTable([fixture_metadata + fixture_source]),
types.RollTable([fixture_source]),
types.RollTable([fixture_no_options]),
types.RollTable([fixture_lists_and_dicts]),
])
def test_as_dict(table):
for src in table.datasources:
assert src.as_dict()
def test_flat(data, expected):
rt = RollTable(data, die=1000)
for txt in expected:
assert txt in str(rt)