removing tables.py
This commit is contained in:
parent
32c9832daa
commit
2796d56426
|
@ -1,23 +0,0 @@
|
||||||
blinker==1.4
|
|
||||||
commonmark==0.9.1
|
|
||||||
docutils==0.19
|
|
||||||
feedgenerator==2.0.0
|
|
||||||
importlib-metadata==4.12.0
|
|
||||||
invoke==1.7.1
|
|
||||||
Jinja2==3.1.2
|
|
||||||
Markdown==3.3.7
|
|
||||||
MarkupSafe==2.1.1
|
|
||||||
pelican==4.7.2
|
|
||||||
pelican-drafts==0.1.1
|
|
||||||
pelican-sitemap==1.0.2
|
|
||||||
pelican-yaml-metadata @ git+https://github.com/pR0Ps/pelican-yaml-metadata.git@cdc1b9708916410e455e8e258e3d39a9d575c7b5
|
|
||||||
Pygments==2.12.0
|
|
||||||
python-dateutil==2.8.2
|
|
||||||
pytz==2022.1
|
|
||||||
PyYAML==5.4.1
|
|
||||||
rich==12.4.4
|
|
||||||
six==1.16.0
|
|
||||||
telisar @ file:///home/greg/dev/telisar
|
|
||||||
tornado==6.2
|
|
||||||
Unidecode==1.3.4
|
|
||||||
zipp==3.8.0
|
|
|
@ -1,245 +0,0 @@
|
||||||
import yaml
|
|
||||||
import random
|
|
||||||
from collections.abc import Generator
|
|
||||||
from typing import Optional, Mapping, List
|
|
||||||
|
|
||||||
|
|
||||||
class RollTable:
|
|
||||||
"""
|
|
||||||
Generate a roll table using weighted distributions of random options.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
Given source.yaml containing options such as:
|
|
||||||
|
|
||||||
option1:
|
|
||||||
- key1: description
|
|
||||||
- key2: description
|
|
||||||
...
|
|
||||||
...
|
|
||||||
|
|
||||||
Generate a random table:
|
|
||||||
|
|
||||||
>>> print(RollTable(path='source.yaml'))
|
|
||||||
d1 option6 key3 description
|
|
||||||
d2 option2 key2 description
|
|
||||||
d3 option3 key4 description
|
|
||||||
...
|
|
||||||
|
|
||||||
You can customize the frequency distribution, headers, and table size by
|
|
||||||
defining metadata in your source file.
|
|
||||||
|
|
||||||
Using Metadata:
|
|
||||||
|
|
||||||
By default options are given uniform distribution and random keys will be
|
|
||||||
selected from each option with equal probability. This behaviour can be
|
|
||||||
changed by adding an optional metadata section to the source file:
|
|
||||||
|
|
||||||
metadata:
|
|
||||||
frequenceis:
|
|
||||||
default:
|
|
||||||
option1: 0.5
|
|
||||||
option2: 0.1
|
|
||||||
option3: 0.3
|
|
||||||
option4: 0.1
|
|
||||||
|
|
||||||
This will guarantee that random keys from option1 are selected 50% of the
|
|
||||||
time, from option 2 10% of the time, and so forth. Frequencies should add
|
|
||||||
up to 1.0.
|
|
||||||
|
|
||||||
If the metadata section includes 'frequencies', The 'default' distribution
|
|
||||||
must be defined. Additional optional distributions may also be defined, if
|
|
||||||
you want to provide alternatives for specific use cases.
|
|
||||||
|
|
||||||
metadata:
|
|
||||||
frequenceis:
|
|
||||||
default:
|
|
||||||
option1: 0.5
|
|
||||||
option2: 0.1
|
|
||||||
option3: 0.3
|
|
||||||
option4: 0.1
|
|
||||||
inverted:
|
|
||||||
option1: 0.1
|
|
||||||
option2: 0.3
|
|
||||||
option3: 0.1
|
|
||||||
option4: 0.5
|
|
||||||
|
|
||||||
A specific frequency distribution can be specifed by passing the 'frequency'
|
|
||||||
parameter at instantiation:
|
|
||||||
|
|
||||||
>>> t = RollTable('source.yaml', frequency='inverted')
|
|
||||||
|
|
||||||
The metadata section can also override the default size of die to use for
|
|
||||||
the table (a d20). For example, this creates a 100-row table:
|
|
||||||
|
|
||||||
metadata:
|
|
||||||
die: 100
|
|
||||||
|
|
||||||
This too can be overridden at instantiation:
|
|
||||||
|
|
||||||
>>> t = RollTable('source.yaml', die=64)
|
|
||||||
|
|
||||||
Finally, headers for your table columns can also be defined in metadata:
|
|
||||||
|
|
||||||
metadata:
|
|
||||||
headers:
|
|
||||||
- Roll
|
|
||||||
- Category
|
|
||||||
- Description
|
|
||||||
- Effect
|
|
||||||
|
|
||||||
This will yield output similar to:
|
|
||||||
|
|
||||||
>>> print(RollTable(path='source.yaml'))
|
|
||||||
Roll Category Name Effect
|
|
||||||
d1 option6 key3 description
|
|
||||||
d2 option2 key2 description
|
|
||||||
d3 option3 key4 description
|
|
||||||
...
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, path: str, frequency: str = 'default',
|
|
||||||
die: Optional[int] = None, collapsed: bool = True):
|
|
||||||
"""
|
|
||||||
Initialize a RollTable instance.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
path - the path to the source file
|
|
||||||
frequency - the name of the frequency distribution to use; must
|
|
||||||
be defined in the source file's metadata.
|
|
||||||
die - specify a die size
|
|
||||||
collapsed - If True, collapse multiple die values with the same
|
|
||||||
options into a single line.
|
|
||||||
"""
|
|
||||||
self._path = path
|
|
||||||
self._frequency = frequency
|
|
||||||
self._die = die
|
|
||||||
self._collapsed = collapsed
|
|
||||||
self._metadata = None
|
|
||||||
self._source = None
|
|
||||||
self._values = None
|
|
||||||
|
|
||||||
def _load_source(self) -> None:
|
|
||||||
"""
|
|
||||||
Cache the yaml source and parsed or generated the metadata.
|
|
||||||
"""
|
|
||||||
if self._source:
|
|
||||||
return
|
|
||||||
with open(self._path, 'r') as source:
|
|
||||||
self._source = yaml.safe_load(source)
|
|
||||||
|
|
||||||
def _defaults():
|
|
||||||
num_keys = len(self._source.keys())
|
|
||||||
default_freq = num_keys/100
|
|
||||||
return {
|
|
||||||
'headers': [''] * num_keys,
|
|
||||||
'die': self._die,
|
|
||||||
'frequencies': {
|
|
||||||
'default': [(k, default_freq) for k in self._source.keys()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self._metadata = self._source.pop('metadata', _defaults())
|
|
||||||
|
|
||||||
def _collapsed_lines(self) -> Generator[list]:
|
|
||||||
"""
|
|
||||||
Generate an array of column values for each row of the table but
|
|
||||||
sort the values and squash multiple rows with the same values into one,
|
|
||||||
with a range for the die roll instead of a single die. That is,
|
|
||||||
|
|
||||||
d1 foo bar baz
|
|
||||||
d2 foo bar baz
|
|
||||||
|
|
||||||
becomes
|
|
||||||
|
|
||||||
d1-d2 foo bar baz
|
|
||||||
"""
|
|
||||||
def collapsed(last_val, offset, val, i):
|
|
||||||
(cat, option) = last_val
|
|
||||||
(k, v) = list(*option.items())
|
|
||||||
if offset + 1 == i:
|
|
||||||
return [f'd{i}', cat, k, v]
|
|
||||||
else:
|
|
||||||
return [f'd{offset+1}-d{i}', cat, k, v]
|
|
||||||
|
|
||||||
last_val = None
|
|
||||||
offset = 0
|
|
||||||
for (i, val) in enumerate(self.values):
|
|
||||||
if not last_val:
|
|
||||||
last_val = val
|
|
||||||
offset = i
|
|
||||||
continue
|
|
||||||
if val != last_val:
|
|
||||||
yield collapsed(last_val, offset, val, i)
|
|
||||||
last_val = val
|
|
||||||
offset = i
|
|
||||||
yield collapsed(last_val, offset, val, i+1)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def freqtable(self):
|
|
||||||
return self.metadata['frequencies'][self._frequency]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def source(self) -> Mapping:
|
|
||||||
"""
|
|
||||||
The parsed source data
|
|
||||||
"""
|
|
||||||
if not self._source:
|
|
||||||
self._load_source()
|
|
||||||
return self._source
|
|
||||||
|
|
||||||
@property
|
|
||||||
def metadata(self) -> Mapping:
|
|
||||||
"""
|
|
||||||
The parsed or generated metadata
|
|
||||||
"""
|
|
||||||
if not self._metadata:
|
|
||||||
self._load_source()
|
|
||||||
return self._metadata
|
|
||||||
|
|
||||||
@property
|
|
||||||
def values(self) -> List:
|
|
||||||
"""
|
|
||||||
Randomly pick values from the source data following the frequency
|
|
||||||
distrubtion of the options.
|
|
||||||
"""
|
|
||||||
if not self._values:
|
|
||||||
weights = []
|
|
||||||
options = []
|
|
||||||
for (option, weight) in self.freqtable.items():
|
|
||||||
weights.append(weight)
|
|
||||||
options.append(option)
|
|
||||||
freqs = random.choices(options, weights=weights,
|
|
||||||
k=self._die or self.metadata['die'])
|
|
||||||
self._values = []
|
|
||||||
for option in freqs:
|
|
||||||
self._values += [(option, random.choice(self.source[option]))]
|
|
||||||
return sorted(self._values, key=lambda val: list(val[1].values())[0])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lines(self) -> Generator[List]:
|
|
||||||
"""
|
|
||||||
Yield a list of table rows suitable for formatting as output.
|
|
||||||
"""
|
|
||||||
yield self.metadata['headers']
|
|
||||||
|
|
||||||
if self._collapsed:
|
|
||||||
for line in self._collapsed_lines():
|
|
||||||
yield line
|
|
||||||
else:
|
|
||||||
for (i, item) in enumerate(self.values):
|
|
||||||
(cat, option) = item
|
|
||||||
(k, v) = list(option.items())[0]
|
|
||||||
yield [f'd{i+1}', cat, k, v]
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
"""
|
|
||||||
Return the lines as a single string.
|
|
||||||
"""
|
|
||||||
return "\n".join([
|
|
||||||
'{:10s}\t{:8s}\t{:20s}\t{:s}'.format(*line) for line in self.lines
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
import sys
|
|
||||||
print(RollTable(path=sys.argv[1], die=int(sys.argv[2])))
|
|
Loading…
Reference in New Issue
Block a user