d20 Dice Engine

repository·master·Indexed 41 days ago

https://github.com/avrae/d20

A fast, extensible dice engine for D&D and other d20-based systems. It features a robust parser for complex dice expressions, including set operations (keep, drop, reroll, explode), annotations, and a full Abstract Syntax Tree (AST) for detailed roll analysis. Supports Python 3.6+ and includes built-in stringifiers for custom output formatting.

Tokens
3.6K
Snippets
7
Records
22
Agent score
50%

What's inside d20

  1. Performance and Caching

    master

    d20 is optimized for speed using an LFU (Least Frequently Used) cache for the 256 most common dice expressions.

    Important Performance Notes:

    • Caching is enabled by default.
    • Caching is disabled if allow_comments=True is passed to roll().
    • For high-frequency rolls of the same expression, caching provides significant speedups.
  2. Dice Syntax: Numbers, Sets, and Operations

    master

    The d20 parser supports a rich grammar for dice expressions:

    Numbers

    • Literal: INT or DECIMAL (e.g., 1, 0.5, 3.14)
    • Dice: INT? "d" (INT | "%" ) (e.g., d20, 3d6)
    • Set: Expressions wrapped in parentheses (e.g., (1, 3+3, 1d20))

    Set Operations

    Operations are performed on sets using an operation selector pattern.

    Operators:

    • k (keep): Keeps matched values.
    • p (drop): Drops matched values.
    • rr (reroll): Rerolls matched values until none match (Dice only).
    • ro (reroll once): Rerolls matched values once (Dice only).
    • ra (reroll and add): Rerolls up to one matched value once, keeping the original (Dice only).
    • e (explode on): Rolls another die for each matched value (Dice only).
    • mi (minimum): Sets the minimum value of each die (Dice only).
    • ma (maximum): Sets the maximum value of each die (Dice only).

    Selectors:

    • X: Literal value (e.g., kh3 keeps the highest 3).
    • hX: Highest X values.
    • lX: Lowest X values.
    • >X: Values greater than X.
    • <X: Values less than X.

    Unary Operations

    • +X: Positive (no-op).
    • -X: Negative.

    Binary Operations

    Supports standard arithmetic and comparison: *, /, //, %, +, -, ==, >=, <=, >, <, !=

  3. Use Annotations and Comments in dice expressions

    master

    Annotations

    You can tag parts of a roll with visual indicators using square brackets. These are purely visual and do not affect the mathematical evaluation.

    • Example: 3d6 [fire] + 1d4 [piercing]
    • Example: (1 [one], 2 [two], 3 [three])

    Comments

    When calling roll(..., allow_comments=True), you can include a comment in the expression string. The comment is accessible via the .comment attribute on the result.

    from d20 import roll
    result = roll("1d20 I rolled a d20", allow_comments=True)
    print(result.comment)  # 'I rolled a d20'

    Note: Enabling allow_comments disables AST caching, which may impact performance.

  4. Quickstart with d20

    master

    The d20 library is a Python tool for parsing and evaluating dice rolling expressions. You can get started by installing the package and using the core evaluation logic to process dice syntax strings.

    To install the library, use pip:

    pip install d20

    Once installed, you can evaluate expressions using the library's parsing engine to handle dice rolls, set operations, and mathematical expressions.

  5. Quickstart with d20.roll()

    master

    The simplest way to use d20 is to call d20.roll() with a dice expression string. This returns an Expression object containing the total, critical status, and the abstract syntax tree (AST).

    import d20
    result = d20.roll("1d20+5")
    print(result.total)  # 15
    print(result.crit)   # <CritType.NORMAL: 0>
    print(str(result))   # '1d20 (10) + 5 = `15`'
    import d20
    result = d20.roll("1d20+5")
    >>> str(result)
    '1d20 (10) + 5 = `15`'
    >>> result.total
    15
    >>> result.crit
    <CritType.NORMAL: 0>
    >>> str(result.ast)
    '1d20 + 5'
  6. Traverse dice results using the AST

    master

    The roll() function returns an Expression object. The .expr attribute provides access to the Abstract Syntax Tree (AST), allowing you to traverse and inspect the roll components.

    Finding Operands

    You can use d20.utils to find the leftmost or rightmost operands in a binary operation.

    from d20 import roll, utils
    binop = roll("1 + 2 + 3 + 4")
    print(utils.leftmost(binop.expr))   # <Literal 1>
    print(utils.rightmost(binop.expr))  # <Literal 4>

    Searching the Tree

    Use d20.utils.dfs (Depth-First Search) to find specific nodes (e.g., a specific die type) within the tree.

    from d20 import roll, utils, Dice
    
    mixed = roll("-1d8 + 4 - (3, 1d4)kh1")
    root = mixed.expr
    # Find the 1d4 node
    d4_node = utils.dfs(root, lambda node: isinstance(node, Dice) and node.num == 1 and node.size == 4)
    from d20 import roll, utils
    
    binop = roll("1 + 2 + 3 + 4")
    left = binop.expr
    while left.children:
        left = left.children[0]
    print(left) # <Literal 1>
    
    from d20 import utils
    print(utils.leftmost(binop.expr))
    print(utils.rightmost(binop.expr))
  7. Traverse the Expression Tree using d20.Number

    master

    The d20.Number object represents a node in the expression tree returned by roll(...).expr. You can use it to traverse the tree structure and inspect the results of a roll.

    Key attributes and methods:

    • number: The numeric value.
    • total: The total value after operations.
    • set: The set of numbers involved.
    • keptset: The subset of numbers kept in the final calculation.
    • drop: The subset of numbers dropped.
    • kept (bool): Indicates whether this specific number was kept or dropped in the final calculation.
    • annotation (Optional[str]): Any annotation attached to this number.
    • children (property): A read-only list of Number objects used for traversing the tree.
    • left (property): The leftmost child node.
    • right (property): The rightmost child node.
    • set_child(...): Method to set a child node.
    # Example of accessing tree properties (conceptual)
    expr = roll("2d6").expr
    root = expr.roll
    print(f"Total: {root.total}")
    for child in root.children:
        print(f"Child value: {child.number}")
  8. Use d20.Roller to perform dice rolls

    master
    The d20.Roller class is the primary interface for parsing and executing dice expressions. It manages the roll execution and can be configured with a d20.RollContext to track roll limits.
  9. Use Set and Parenthetical nodes for complex expressions

    master

    Complex dice logic is handled via d20.Set and d20.Parenthetical nodes:

    d20.Set

    • values (list[d20.Number]): The elements contained in the set.
    • operations (list[d20.SetOperation]): The operations to be applied to the set.

    d20.Parenthetical

    Represents a grouped expression within parentheses.

    • value (d20.Number): The subtree inside the parentheses.
    • operations (list[d20.SetOperation]): If the inner value is a d20.Set, these are the operations to run on it.
  10. Analyze Dice and Die roll history

    master

    The expression tree provides detailed information about dice rolls through d20.Dice and d20.Die objects.

    d20.Dice

    Represents a collection of dice.

    • num (int): The number of dice in the set.
    • size (int): The size of each die.
    • values (list[d20.Die]): The actual die objects.
    • operations (list[d20.SetOperation]): Operations to run on the set.
    • roll_another(): Rolls an additional die of the appropriate size and adds it to the set.

    d20.Die

    Represents an individual die.

    • size (int): The number of sides on the die.
    • values (list[d20.Literal]): The history of values this specific die has rolled.
  11. Use AST Node types for expression analysis

    master

    When working with the Abstract Syntax Tree, you will encounter several specific node types that represent different parts of a dice expression:

    Numbers and Literals

    • d20.ast.Literal: Represents a constant number. Contains value (Union[int, float]).
    • d20.ast.AnnotatedNumber: A number with metadata. Contains value (d20.ast.Node) and annotations (list[str]).

    Operations

    • d20.ast.UnOp: A unary operation (e.g., negation). Contains op (str) and the value (d20.ast.Node) it operates on.
    • d20.ast.BinOp: A binary operation (e.g., addition). Contains op (str), left (d20.ast.Node), and right (d20.ast.Node).

    Dice and Sets

    • d20.ast.Dice: Represents a dice roll. Contains num (int) for the number of dice and size (int) for the number of sides.
    • d20.ast.NumberSet: A collection of values. Contains values (list[d20.ast.NumberSet]).
    • d20.ast.OperatedSet: A set with operations applied. Contains value (d20.ast.NumberSet) and operations (list[d20.SetOperation]).

    Grouping and Selection

    • d20.ast.Parenthetical: Represents an expression inside parentheses. Contains value (d20.ast.Node).
    • d20.ast.SetOperator: Defines how to select elements from a set. Contains op (str) and sels (list[d20.SetSelector]).
    • d20.ast.SetSelector: Describes how to pick elements (e.g., 'lowest', 'highest'). Contains cat (Optional[str]) for the selection type and num (int) for the count.