Skip to content

Commit

Permalink
add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
zhudotexe committed Feb 19, 2020
1 parent b3329d4 commit 66a0908
Show file tree
Hide file tree
Showing 14 changed files with 636 additions and 23 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ A fast, powerful, and extensible dice engine for D&D, d20 systems, and any other
## Installing
**Requires Python 3.6+**.

```
```bash
python3 -m pip install -U d20
```

Expand Down Expand Up @@ -197,7 +197,7 @@ objects, which can be accessed as such:
<Expression roll=<BinOp left=<BinOp left=<Dice num=3 size=6 values=[<Die size=6 values=[<Literal 4>]>, <Die size=6 values=[<Literal 6>]>, <Die size=6 values=[<Literal 6>]>] operations=[]> op=+ right=<Dice num=1 size=4 values=[<Die size=4 values=[<Literal 1>]>] operations=[]>> op=+ right=<Literal 3>> comment=None>
```
or, in a easier-to-read format,
```
```text
<Expression roll=
<BinOp left=
<BinOp left=
Expand Down
40 changes: 34 additions & 6 deletions d20/dice.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,56 @@


class CritType(IntEnum):
"""
Integer enumeration representing the crit type of a roll.
"""
NONE = 0
CRIT = 1
FAIL = 2


class AdvType(IntEnum):
"""
Integer enumeration representing at what advantage a roll should be made at.
"""
NONE = 0
ADV = 1
DIS = -1


class RollContext:
"""
A class to track information about rolls to ensure all rolls halt eventually.
To use this class, pass an instance to the constructor of :class:`d20.Roller`.
"""

def __init__(self, max_rolls=1000):
self.max_rolls = max_rolls
self.rolls = 0
self.reset()

def reset(self):
"""Called at the start of each new roll."""
self.rolls = 0

def count_roll(self, n=1):
"""
Called each time a die is about to be rolled.
:param int n: The number of rolls about to be made.
:raises d20.TooManyRolls: if the roller should stop rolling dice because too many have been rolled.
"""
self.rolls += n
if self.rolls > self.max_rolls:
raise TooManyRolls("Too many dice rolled.")


class RollResult:
"""
Holds information about the result of a roll. This should generally not be constructed manually.
"""

def __init__(self, the_ast, the_roll, stringifier):
"""
:type the_ast: ast.Node
Expand All @@ -54,9 +77,9 @@ def __init__(self, the_ast, the_roll, stringifier):
@property
def crit(self):
"""
If the leftmost node was Xd20kh1, returns :type:`CritType.CRIT` if the roll was a 20 and
:type:`CritType.FAIL` if the roll was a 1.
Returns :type:`CritType.NONE` otherwise.
If the leftmost node was Xd20kh1, returns :class:`CritType.CRIT` if the roll was a 20 and
:class:`CritType.FAIL` if the roll was a 1.
Returns :class:`CritType.NONE` otherwise.
:rtype: CritType
"""
Expand Down Expand Up @@ -94,7 +117,12 @@ def __repr__(self):

# noinspection PyMethodMayBeStatic
class Roller:
def __init__(self):
"""The main class responsible for parsing dice into an AST and evaluating that AST."""

def __init__(self, context=None):
if context is None:
context = RollContext()

self._nodes = {
ast.Expression: self._eval_expression,
ast.AnnotatedNumber: self._eval_annotatednumber,
Expand All @@ -107,8 +135,8 @@ def __init__(self):
ast.OperatedDice: self._eval_operateddice,
ast.Dice: self._eval_dice
}
self.context = RollContext()
self._parse_cache = cachetools.LFUCache(256)
self.context = context

def roll(self, expr, stringifier=None, allow_comments=False, advantage=AdvType.NONE):
"""
Expand Down Expand Up @@ -146,7 +174,7 @@ def parse(self, expr, allow_comments=False):
:param expr: The dice to roll.
:type expr: str
:param bool allow_comments: Whether to parse for comments after the main roll expression (potential slowdown)
:rtype: ast.Node
:rtype: ast.Expression
"""
try:
if not allow_comments:
Expand Down
2 changes: 1 addition & 1 deletion d20/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


class RollError(Exception):
"""Generic exception happened in the roll."""
"""Generic exception happened in the roll. Base exception for all library exceptions."""

def __init__(self, msg):
super().__init__(msg)
Expand Down
49 changes: 37 additions & 12 deletions d20/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

# ===== ast -> expression models =====
class Number(abc.ABC, ast.ChildMixin): # num
"""
The base class for all expression objects.
"""

__slots__ = ("kept", "annotation")

def __init__(self, kept=True, annotation=None):
Expand Down Expand Up @@ -43,7 +47,7 @@ def set(self):
"""
Returns the set representation of this object.
:rtype: list of Number
:rtype: list[Number]
"""
raise NotImplementedError

Expand All @@ -53,7 +57,7 @@ def keptset(self):
Returns the set representation of this object, but only including children whose values
were not dropped.
:rtype: list of Number
:rtype: list[Number]
"""
return [n for n in self.set if n.kept]

Expand All @@ -75,18 +79,22 @@ def __repr__(self):
# overridden methods for typechecking
def set_child(self, index, value):
"""
:type index: int
Sets the ith child of this Number.
:param int index: Which child to set.
:param value: The Number to set it to.
:type value: Number
"""
super().set_child(index, value)

@property
def children(self):
""":rtype: list of Number"""
""":rtype: list[Number]"""
raise NotImplementedError


class Expression(Number):
"""Expressions are usually the root of all Number trees."""
__slots__ = ("roll", "comment")

def __init__(self, roll, comment, **kwargs):
Expand Down Expand Up @@ -118,6 +126,7 @@ def __repr__(self):


class Literal(Number):
"""A literal integer or float."""
__slots__ = ("values", "exploded")

def __init__(self, value, **kwargs):
Expand Down Expand Up @@ -154,6 +163,7 @@ def __repr__(self):


class UnOp(Number):
"""Represents a unary operation."""
__slots__ = ("op", "value")

UNARY_OPS = {
Expand Down Expand Up @@ -191,6 +201,7 @@ def __repr__(self):


class BinOp(Number):
"""Represents a binary operation."""
__slots__ = ("op", "left", "right")

BINARY_OPS = {
Expand Down Expand Up @@ -246,12 +257,13 @@ def __repr__(self):


class Parenthetical(Number):
"""Represents a value inside parentheses."""
__slots__ = ("value", "operations")

def __init__(self, value, operations=None, **kwargs):
"""
:type value: Number
:type operations: list of SetOperator
:type operations: list[SetOperator]
"""
super().__init__(**kwargs)
if operations is None:
Expand Down Expand Up @@ -279,12 +291,13 @@ def __repr__(self):


class Set(Number):
"""Represents a set of values."""
__slots__ = ("values", "operations")

def __init__(self, values, operations=None, **kwargs):
"""
:type values: list of Number
:type operations: list of SetOperator
:type values: list[Number]
:type operations: list[SetOperator]
"""
super().__init__(**kwargs)
if operations is None:
Expand All @@ -309,14 +322,15 @@ def __repr__(self):


class Dice(Set):
"""A set of Die."""
__slots__ = ("num", "size", "_context")

def __init__(self, num, size, values, operations=None, context=None, **kwargs):
"""
:type num: int
:type size: int
:type values: list of Die
:type operations: list of SetOperator
:type operations: list[SetOperator]
:type context: dice.RollContext
"""
super().__init__(values, operations, **kwargs)
Expand All @@ -340,6 +354,7 @@ def __repr__(self):


class Die(Number): # part of diceexpr
"""Represents a single die."""
__slots__ = ("size", "values", "_context")

def __init__(self, size, values, context=None):
Expand Down Expand Up @@ -400,13 +415,14 @@ def __repr__(self):
# noinspection PyUnresolvedReferences
# selecting on Dice will always return Die
class SetOperator: # set_op, dice_op
"""Represents an operation on a set."""
__slots__ = ("op", "sels")
OPERATIONS = {"k", "p", "rr", "ro", "ra", "e", "mi", "ma"}

def __init__(self, op, sels):
"""
:type op: str
:type sels: list of SetSelector
:type sels: list[SetSelector]
"""
self.op = op
self.sels = sels
Expand All @@ -417,8 +433,12 @@ def from_ast(cls, node):

def select(self, target, max_targets=None):
"""
Selects the operands in a target set.
:param target: The source of the operands.
:type target: Number
:type max_targets: int or None
:param max_targets: The maximum number of targets to select.
:type max_targets: Optional[int]
"""
out = set()
for selector in self.sels:
Expand All @@ -433,8 +453,9 @@ def select(self, target, max_targets=None):

def operate(self, target):
"""
Operates in place on the values in a base set.
Operates in place on the values in a target set.
:param target: The source of the operands.
:type target: Number
"""
operations = {
Expand Down Expand Up @@ -539,6 +560,7 @@ def __repr__(self):


class SetSelector: # selector
"""Represents a selection on a set."""
__slots__ = ("cat", "num")

def __init__(self, cat, num):
Expand All @@ -555,8 +577,11 @@ def from_ast(cls, node):

def select(self, target, max_targets=None):
"""
Selects operands from a target set.
:param target: The source of the operands.
:type target: Number
:type max_targets: len
:param int max_targets: The maximum number of targets to select.
:return: The targets in the set.
:rtype: set of Number
"""
Expand Down

0 comments on commit 66a0908

Please sign in to comment.