Skip to content

dyce.r package reference

Experimental

This package is an attempt to provide primitives for producing weighted randomized rolls without the overhead of enumeration. Rolls can be inspected to understand how specific values are derived. It should be considered experimental. Be warned that future release may introduce incompatibilities or remove this package altogether. Feedback, suggestions, and contributions are welcome and appreciated.

Roller class hierarchy

Roller class hierarchy

BasicOpRoller (R)

A roller for applying op to some variation of outcomes from sources. Any RollOutcomes returned by op are used directly in the creation of a new Roll.

__slots__: Union[str, Iterable[str]] special

op: BasicOperatorT property readonly

The operator this roller applies to its sources.

__eq__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __eq__(self, other) -> bool:
    return super().__eq__(other) and (_callable_cmp(self.op, other.op))

__init__(self, op: BasicOperatorT, sources: Iterable[_SourceT], annotation: Any = '', **kw) special

Source code in dyce/r.py
@beartype
def __init__(
    self,
    op: BasicOperatorT,
    sources: Iterable[_SourceT],
    annotation: Any = "",
    **kw,
):
    r"Initializer."
    super().__init__(sources=sources, annotation=annotation, **kw)
    self._op = op

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        return f"""{type(self).__name__}(
  op={self.op!r},
  sources=({_seq_repr(self.sources)}),
  annotation={self.annotation!r},
)"""

roll(self) -> Roll

Source code in dyce/r.py
@beartype
def roll(self) -> Roll:
    r""""""
    source_rolls = list(self.source_rolls())
    res = self.op(
        self,
        (
            roll_outcome
            for roll_outcome in chain.from_iterable(source_rolls)
            if roll_outcome.value is not None
        ),
    )

    if isinstance(res, RollOutcome):
        roll_outcomes = (res,)
    else:
        roll_outcomes = res  # type: ignore [assignment]  # TODO(posita): WTF?

    return Roll(
        self,
        roll_outcomes=roll_outcomes,
        source_rolls=source_rolls,
    )

BinarySumOpRoller (NarySumOpRoller)

An NarySumOpRoller for applying a binary operator bin_op to the sum of all outcomes from its left_source and the sum of all outcomes from its right_source.

__slots__: Union[str, Iterable[str]] special

bin_op: _RollOutcomeBinaryOperatorT property readonly

The operator this roller applies to its sources.

__eq__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __eq__(self, other) -> bool:
    return super().__eq__(other) and (_callable_cmp(self.bin_op, other.bin_op))

__init__(self, bin_op: _RollOutcomeBinaryOperatorT, left_source: _SourceT, right_source: _SourceT, annotation: Any = '', **kw) special

Source code in dyce/r.py
@beartype
def __init__(
    self,
    bin_op: _RollOutcomeBinaryOperatorT,
    left_source: _SourceT,
    right_source: _SourceT,
    annotation: Any = "",
    **kw,
):
    r"Initializer."

    def _op(
        r: R,
        roll_outcomes: Iterable[RollOutcome],
    ) -> Union[RollOutcome, Iterable[RollOutcome]]:
        left_operand, right_operand = roll_outcomes

        return bin_op(left_operand, right_operand)

    super().__init__(
        op=_op, sources=(left_source, right_source), annotation=annotation, **kw
    )
    self._bin_op = bin_op

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        def _source_repr(source: _SourceT) -> str:
            return indent(repr(source), "  ").strip()

        left_source, right_source = self.sources

        return f"""{type(self).__name__}(
  bin_op={self.bin_op!r},
  left_source={_source_repr(left_source)},
  right_source={_source_repr(right_source)},
  annotation={self.annotation!r},
)"""

FilterRoller (R)

A roller for applying predicate to filter outcomes its sources.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
>>> r_d6 = R.from_value(H(6))
>>> filter_r = (2@r_d6).filter(
...   lambda outcome: outcome.value is not None and outcome.value > 3,  # type: ignore
... )
>>> (filter_r).roll()
Roll(
  r=FilterRoller(
    predicate=<function <lambda> at ...>,
    sources=(
      RepeatRoller(
        n=2,
        source=ValueRoller(value=H(6), annotation=''),
        annotation='',
      ),
    ),
    annotation='',
  ),
  roll_outcomes=(
    RollOutcome(
      value=None,
      sources=(
        RollOutcome(
          value=2,
          sources=(),
        ),
      ),
    ),
    RollOutcome(
      value=5,
      sources=(),
    ),
  ),
  source_rolls=(
    Roll(
      r=RepeatRoller(
        n=2,
        source=ValueRoller(value=H(6), annotation=''),
        annotation='',
      ),
      roll_outcomes=(
        RollOutcome(
          value=2,
          sources=(),
        ),
        RollOutcome(
          value=5,
          sources=(),
        ),
      ),
      source_rolls=(
        Roll(
          r=ValueRoller(value=H(6), annotation=''),
          roll_outcomes=(
            RollOutcome(
              value=2,
              sources=(),
            ),
          ),
          source_rolls=(),
        ),
        Roll(
          r=ValueRoller(value=H(6), annotation=''),
          roll_outcomes=(
            RollOutcome(
              value=5,
              sources=(),
            ),
          ),
          source_rolls=(),
        ),
      ),
    ),
  ),
)

See the section on “Filtering and substitution” more examples.

__slots__: Tuple[str, ...] special

predicate: _PredicateT property readonly

The predicate this roller applies to filter its sources.

__eq__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __eq__(self, other) -> bool:
    return super().__eq__(other) and _callable_cmp(self.predicate, other.predicate)

__init__(self, predicate: _PredicateT, sources: Iterable[R], annotation: Any = '', **kw) special

Source code in dyce/r.py
@beartype
def __init__(
    self,
    predicate: _PredicateT,
    sources: Iterable[R],
    annotation: Any = "",
    **kw,
):
    r"Initializer."
    super().__init__(sources=sources, annotation=annotation, **kw)
    self._predicate = predicate

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        return f"""{type(self).__name__}(
  predicate={self.predicate!r},
  sources=({_seq_repr(self.sources)}),
  annotation={self.annotation!r},
)"""

roll(self) -> Roll

Source code in dyce/r.py
@beartype
def roll(self) -> Roll:
    r""""""
    source_rolls = list(self.source_rolls())

    def _filtered_roll_outcomes() -> Iterator[RollOutcome]:
        for roll_outcome in chain.from_iterable(source_rolls):
            if roll_outcome.value is not None:
                if self.predicate(roll_outcome):
                    yield roll_outcome
                else:
                    yield roll_outcome.euthanize()

    return Roll(
        self,
        roll_outcomes=_filtered_roll_outcomes(),
        source_rolls=source_rolls,
    )

NarySumOpRoller (BasicOpRoller)

A BasicOpRoller for applying op to the sum of outcomes grouped by each of sources.

__slots__: Union[str, Iterable[str]] special

roll(self) -> Roll

Source code in dyce/r.py
@beartype
def roll(self) -> Roll:
    r""""""
    source_rolls = list(self.source_rolls())

    def _sum_roll_outcomes_by_rolls() -> Iterator[RollOutcome]:
        for source_roll in source_rolls:
            if len(source_roll) == 1 and source_roll[0].value is not None:
                yield from source_roll
            else:
                yield RollOutcome(sum(source_roll.outcomes()), sources=source_roll)

    res = self.op(self, _sum_roll_outcomes_by_rolls())

    if isinstance(res, RollOutcome):
        roll_outcomes = (res,)
    else:
        roll_outcomes = res  # type: ignore [assignment]  # TODO(posita): WTF?

    return Roll(
        self,
        roll_outcomes=roll_outcomes,
        source_rolls=source_rolls,
    )

PoolRoller (R)

A roller for rolling flattened “pools” from all sources.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
>>> PoolRoller((
...   PoolRoller((
...     ValueRoller(11),
...     ValueRoller(12),
...   )),
...   PoolRoller((
...     PoolRoller((
...       ValueRoller(211),
...       ValueRoller(212),
...     )),
...     PoolRoller((
...       ValueRoller(221),
...       ValueRoller(222),
...     )),
...   )),
...   ValueRoller(3),
... )).roll()
Roll(
  r=...,
  roll_outcomes=(
    RollOutcome(
      value=11,
      sources=...,
    ),
    RollOutcome(
      value=12,
      sources=...,
    ),
    RollOutcome(
      value=211,
      sources=...,
    ),
    RollOutcome(
      value=212,
      sources=...,
    ),
    RollOutcome(
      value=221,
      sources=...,
    ),
    RollOutcome(
      value=222,
      sources=...,
    ),
    RollOutcome(
      value=3,
      sources=...,
    ),
  ),
  source_rolls=...,
)

__slots__: Union[str, Iterable[str]] special

roll(self) -> Roll

Source code in dyce/r.py
@beartype
def roll(self) -> Roll:
    r""""""
    source_rolls = list(self.source_rolls())

    return Roll(
        self,
        roll_outcomes=(
            roll_outcome
            for roll_outcome in chain.from_iterable(source_rolls)
            if roll_outcome.value is not None
        ),
        source_rolls=source_rolls,
    )

R

Experimental

This class (and its descendants) should be considered experimental and may change or disappear in future versions.

Where H objects and P objects are used primarily for enumerating weighted outcomes, R objects represent rollers. More specifically, they are immutable nodes assembled in tree-like structures to represent calculations. Unlike H or P objects, rollers generate rolls that conform to weighted outcomes without engaging in computationally expensive enumeration. Roller trees are typically composed from various R class methods and operators as well as arithmetic operations.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>> from dyce import H, P, R
>>> d6 = H(6)
>>> r_d6 = R.from_value(d6) ; r_d6
ValueRoller(value=H(6), annotation='')
>>> ((4 * r_d6 + 3) ** 2 % 5).gt(2)
BinarySumOpRoller(
  bin_op=<function R.gt.<locals>._gt at ...>,
  left_source=BinarySumOpRoller(
      bin_op=<built-in function mod>,
      left_source=BinarySumOpRoller(
          bin_op=<built-in function pow>,
          left_source=BinarySumOpRoller(
              bin_op=<built-in function add>,
              left_source=BinarySumOpRoller(
                  bin_op=<built-in function mul>,
                  left_source=ValueRoller(value=4, annotation=''),
                  right_source=ValueRoller(value=H(6), annotation=''),
                  annotation='',
                ),
              right_source=ValueRoller(value=3, annotation=''),
              annotation='',
            ),
          right_source=ValueRoller(value=2, annotation=''),
          annotation='',
        ),
      right_source=ValueRoller(value=5, annotation=''),
      annotation='',
    ),
  right_source=ValueRoller(value=2, annotation=''),
  annotation='',
)

Info

No optimizations are made when initializing roller trees. They retain their exact structure, even where such structures could be trivially reduced.

1
2
3
4
5
6
7
>>> r_6 = R.from_value(6)
>>> r_6_abs_3 = 3@abs(r_6)
>>> r_6_abs_6_abs_6_abs = R.from_sources(abs(r_6), abs(r_6), abs(r_6))
>>> tuple(r_6_abs_3.roll().outcomes()), tuple(r_6_abs_6_abs_6_abs.roll().outcomes())  # they generate the same rolls
((6, 6, 6), (6, 6, 6))
>>> r_6_abs_3 == r_6_abs_6_abs_6_abs  # and yet, they're different animals
False

This is because the structure itself contains information that might be required by certain contexts. If such information loss is permissible and reduction is desirable, consider using histograms instead.

Roller trees can can be queried via the roll method, which produces Roll objects.

1
2
3
4
5
>>> roll = r_d6.roll()
>>> tuple(roll.outcomes())
(4,)
>>> roll.total()
4
1
2
3
4
>>> d6 + 3
H({4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1})
>>> (r_d6 + 3).roll().total() in (d6 + 3)
True

Roll objects are much richer than mere sequences of outcomes. They are also tree-like structures that mirror the roller trees used to produce them, capturing references to rollers and the outcomes generated at each node.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
>>> roll = (r_d6 + 3).roll()
>>> roll.total()
8
>>> roll
Roll(
  r=...,
  roll_outcomes=(
    RollOutcome(
      value=8,
      sources=(
        RollOutcome(
          value=5,
          sources=(),
        ),
        RollOutcome(
          value=3,
          sources=(),
        ),
      ),
    ),
  ),
  source_rolls=(
    Roll(
      r=ValueRoller(value=H(6), annotation=''),
      roll_outcomes=(
        RollOutcome(
          value=5,
          sources=(),
        ),
      ),
      source_rolls=(),
    ),
    Roll(
      r=ValueRoller(value=3, annotation=''),
      roll_outcomes=(
        RollOutcome(
          value=3,
          sources=(),
        ),
      ),
      source_rolls=(),
    ),
  ),
)

Rollers afford optional annotations as a convenience to callers. They are taken into account when comparing roller trees, but otherwise ignored, internally. One use is to capture references to nodes in an abstract syntax tree generated from parsing a proprietary grammar. Any provided annotation can be retrieved using the annotation property. The annotate method can be used to apply an annotation to existing roller.

The R class itself acts as a base from which several computation-specific implementations derive (such as expressing operands like outcomes or histograms, unary operations, binary operations, pools, etc.).

__slots__: Union[str, Iterable[str]] special

annotation: Any property readonly

Any provided annotation.

sources: Tuple[_SourceT, ...] property readonly

The roller’s direct sources (if any).

__abs__(self) -> UnarySumOpRoller special

Source code in dyce/r.py
@beartype
def __abs__(self) -> UnarySumOpRoller:
    return self.umap(__abs__)

__add__(self, other: _ROperandT) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __add__(self, other: _ROperandT) -> BinarySumOpRoller:
    try:
        return self.map(__add__, other)
    except NotImplementedError:
        return NotImplemented

__and__(self, other: Union[_SourceT, SupportsIntSCU]) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __and__(self, other: Union[_SourceT, SupportsIntSCU]) -> BinarySumOpRoller:
    try:
        if isinstance(other, R):
            return self.map(__and__, other)
        else:
            return self.map(__and__, as_int(other))
    except NotImplementedError:
        return NotImplemented

__eq__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __eq__(self, other) -> bool:
    if isinstance(other, R):
        return (
            (isinstance(self, type(other)) or isinstance(other, type(self)))
            and __eq__(self.sources, other.sources)  # order matters
            and __eq__(self.annotation, other.annotation)
        )
    else:
        return super().__eq__(other)

__floordiv__(self, other: _ROperandT) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __floordiv__(self, other: _ROperandT) -> BinarySumOpRoller:
    try:
        return self.map(__floordiv__, other)
    except NotImplementedError:
        return NotImplemented

__init__(self, sources: Iterable[_SourceT] = (), annotation: Any = '', **kw) special

Initializer.

Source code in dyce/r.py
@experimental
@beartype
def __init__(
    self,
    sources: Iterable[_SourceT] = (),
    annotation: Any = "",
    **kw,
):
    r"Initializer."
    super().__init__()
    self._sources = tuple(sources)
    self._annotation = annotation

__invert__(self) -> UnarySumOpRoller special

Source code in dyce/r.py
@beartype
def __invert__(self) -> UnarySumOpRoller:
    return self.umap(__invert__)

__matmul__(self, other: SupportsIntSCU) -> R special

Source code in dyce/r.py
@beartype
def __matmul__(self, other: SupportsIntSCU) -> R:
    try:
        other = as_int(other)
    except TypeError:
        return NotImplemented

    if other < 0:
        raise ValueError("argument cannot be negative")
    else:
        return RepeatRoller(other, self)

__mod__(self, other: _ROperandT) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __mod__(self, other: _ROperandT) -> BinarySumOpRoller:
    try:
        return self.map(__mod__, other)
    except NotImplementedError:
        return NotImplemented

__mul__(self, other: _ROperandT) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __mul__(self, other: _ROperandT) -> BinarySumOpRoller:
    try:
        return self.map(__mul__, other)
    except NotImplementedError:
        return NotImplemented

__ne__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __ne__(self, other) -> bool:
    if isinstance(other, R):
        return not __eq__(self, other)
    else:
        return super().__ne__(other)

__neg__(self) -> UnarySumOpRoller special

Source code in dyce/r.py
@beartype
def __neg__(self) -> UnarySumOpRoller:
    return self.umap(__neg__)

__or__(self, other: Union[_SourceT, SupportsIntSCU]) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __or__(self, other: Union[_SourceT, SupportsIntSCU]) -> BinarySumOpRoller:
    try:
        if isinstance(other, R):
            return self.map(__or__, other)
        else:
            return self.map(__or__, as_int(other))
    except NotImplementedError:
        return NotImplemented

__pos__(self) -> UnarySumOpRoller special

Source code in dyce/r.py
@beartype
def __pos__(self) -> UnarySumOpRoller:
    return self.umap(__pos__)

__pow__(self, other: _ROperandT) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __pow__(self, other: _ROperandT) -> BinarySumOpRoller:
    try:
        return self.map(__pow__, other)
    except NotImplementedError:
        return NotImplemented

__radd__(self, other: RealLikeSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __radd__(self, other: RealLikeSCU) -> BinarySumOpRoller:
    try:
        return self.rmap(other, __add__)
    except NotImplementedError:
        return NotImplemented

__rand__(self, other: SupportsIntSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __rand__(self, other: SupportsIntSCU) -> BinarySumOpRoller:
    try:
        return self.rmap(as_int(other), __and__)
    except NotImplementedError:
        return NotImplemented

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        return f"""{type(self).__name__}(
  sources=({_seq_repr(self.sources)}),
  annotation={self.annotation!r},
)"""

__rfloordiv__(self, other: RealLikeSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __rfloordiv__(self, other: RealLikeSCU) -> BinarySumOpRoller:  # type: ignore [misc]
    try:
        return self.rmap(other, __floordiv__)
    except NotImplementedError:
        return NotImplemented

__rmatmul__(self, other: SupportsIntSCU) -> R special

Source code in dyce/r.py
@beartype
def __rmatmul__(self, other: SupportsIntSCU) -> R:
    return self.__matmul__(other)

__rmod__(self, other: RealLikeSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __rmod__(self, other: RealLikeSCU) -> BinarySumOpRoller:
    try:
        return self.rmap(other, __mod__)
    except NotImplementedError:
        return NotImplemented

__rmul__(self, other: RealLikeSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __rmul__(self, other: RealLikeSCU) -> BinarySumOpRoller:
    try:
        return self.rmap(other, __mul__)
    except NotImplementedError:
        return NotImplemented

__ror__(self, other: SupportsIntSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __ror__(self, other: SupportsIntSCU) -> BinarySumOpRoller:
    try:
        return self.rmap(as_int(other), __or__)
    except NotImplementedError:
        return NotImplemented

__rpow__(self, other: RealLikeSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __rpow__(self, other: RealLikeSCU) -> BinarySumOpRoller:
    try:
        return self.rmap(other, __pow__)
    except NotImplementedError:
        return NotImplemented

__rsub__(self, other: RealLikeSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __rsub__(self, other: RealLikeSCU) -> BinarySumOpRoller:
    try:
        return self.rmap(other, __sub__)
    except NotImplementedError:
        return NotImplemented

__rtruediv__(self, other: RealLikeSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __rtruediv__(self, other: RealLikeSCU) -> BinarySumOpRoller:
    try:
        return self.rmap(other, __truediv__)
    except NotImplementedError:
        return NotImplemented

__rxor__(self, other: SupportsIntSCU) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __rxor__(self, other: SupportsIntSCU) -> BinarySumOpRoller:
    try:
        return self.rmap(as_int(other), __xor__)
    except NotImplementedError:
        return NotImplemented

__sub__(self, other: _ROperandT) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __sub__(self, other: _ROperandT) -> BinarySumOpRoller:
    try:
        return self.map(__sub__, other)
    except NotImplementedError:
        return NotImplemented

__truediv__(self, other: _ROperandT) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __truediv__(self, other: _ROperandT) -> BinarySumOpRoller:
    try:
        return self.map(__truediv__, other)
    except NotImplementedError:
        return NotImplemented

__xor__(self, other: Union[_SourceT, SupportsIntSCU]) -> BinarySumOpRoller special

Source code in dyce/r.py
@beartype
def __xor__(self, other: Union[_SourceT, SupportsIntSCU]) -> BinarySumOpRoller:
    try:
        if isinstance(other, R):
            return self.map(__xor__, other)
        else:
            return self.map(__xor__, as_int(other))
    except NotImplementedError:
        return NotImplemented

annotate(self, annotation: Any = '') -> R

Generates a copy of the roller with the desired annotation.

1
2
3
4
>>> r_just_the_n_of_us = R.from_value(5, annotation="But I'm 42!") ; r_just_the_n_of_us
ValueRoller(value=5, annotation="But I'm 42!")
>>> r_just_the_n_of_us.annotate("I'm a 42-year-old investment banker!")
ValueRoller(value=5, annotation="I'm a 42-year-old investment banker!")
Source code in dyce/r.py
@beartype
def annotate(self, annotation: Any = "") -> R:
    r"""
    Generates a copy of the roller with the desired annotation.

    ``` python
    >>> r_just_the_n_of_us = R.from_value(5, annotation="But I'm 42!") ; r_just_the_n_of_us
    ValueRoller(value=5, annotation="But I'm 42!")
    >>> r_just_the_n_of_us.annotate("I'm a 42-year-old investment banker!")
    ValueRoller(value=5, annotation="I'm a 42-year-old investment banker!")

    ```
    """
    r = copy(self)
    r._annotation = annotation

    return r

eq(self, other: _ROperandT) -> BinarySumOpRoller

Shorthand for self.map(lambda left, right: left.eq(right), other).

See the map method.

Source code in dyce/r.py
@beartype
def eq(self, other: _ROperandT) -> BinarySumOpRoller:
    r"""
    Shorthand for ``#!python self.map(lambda left, right: left.eq(right), other)``.

    See the [``map`` method][dyce.r.R.map].
    """

    def _eq(left_operand: RollOutcome, right_operand: RollOutcome) -> RollOutcome:
        return left_operand.eq(right_operand)

    return self.map(_eq, other)

filter(self, predicate: _PredicateT, annotation: Any = '') -> FilterRoller

Shorthand for type(self).filter_from_sources(predicate, self,annotation=annotation).

See the filter_from_sources method.

Source code in dyce/r.py
@beartype
def filter(
    self,
    predicate: _PredicateT,
    annotation: Any = "",
) -> FilterRoller:
    r"""
    Shorthand for ``#!python type(self).filter_from_sources(predicate, self,
    annotation=annotation)``.

    See the [``filter_from_sources`` method][dyce.r.R.filter_from_sources].
    """
    return type(self).filter_from_sources(predicate, self, annotation=annotation)

filter_from_sources(predicate: _PredicateT, *sources: _SourceT, *, annotation: Any = '') -> FilterRoller classmethod

Shorthand for cls.filter_from_sources_iterable(predicate, sources,annotation=annotation).

See the filter_from_sources_iterable method.

Source code in dyce/r.py
@classmethod
@beartype
def filter_from_sources(
    cls,
    predicate: _PredicateT,
    *sources: _SourceT,
    annotation: Any = "",
) -> FilterRoller:
    r"""
    Shorthand for ``#!python cls.filter_from_sources_iterable(predicate, sources,
    annotation=annotation)``.

    See the [``filter_from_sources_iterable``
    method][dyce.r.R.filter_from_sources_iterable].
    """
    return cls.filter_from_sources_iterable(
        predicate, sources, annotation=annotation
    )

filter_from_sources_iterable(predicate: _PredicateT, sources: Iterable[_SourceT], annotation: Any = '') -> FilterRoller classmethod

Creates and returns a FilterRoller for applying filterion predicate to sorted outcomes from sources.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
>>> r_filter = R.filter_from_sources_iterable(
...   lambda outcome: bool(outcome.is_even().value),
...   (R.from_value(i) for i in (5, 4, 6, 3, 7, 2, 8, 1, 9, 0)),
... ) ; r_filter
FilterRoller(
  predicate=<function <lambda> at ...>,
  sources=(
    ValueRoller(value=5, annotation=''),
    ValueRoller(value=4, annotation=''),
    ...,
    ValueRoller(value=9, annotation=''),
    ValueRoller(value=0, annotation=''),
  ),
  annotation='',
)
>>> tuple(r_filter.roll().outcomes())
(4, 6, 2, 8, 0)
Source code in dyce/r.py
@classmethod
@beartype
def filter_from_sources_iterable(
    cls,
    predicate: _PredicateT,
    sources: Iterable[_SourceT],
    annotation: Any = "",
) -> FilterRoller:
    r"""
    Creates and returns a [``FilterRoller``][dyce.r.FilterRoller] for applying filterion
    *predicate* to sorted outcomes from *sources*.

    ``` python
    >>> r_filter = R.filter_from_sources_iterable(
    ...   lambda outcome: bool(outcome.is_even().value),
    ...   (R.from_value(i) for i in (5, 4, 6, 3, 7, 2, 8, 1, 9, 0)),
    ... ) ; r_filter
    FilterRoller(
      predicate=<function <lambda> at ...>,
      sources=(
        ValueRoller(value=5, annotation=''),
        ValueRoller(value=4, annotation=''),
        ...,
        ValueRoller(value=9, annotation=''),
        ValueRoller(value=0, annotation=''),
      ),
      annotation='',
    )
    >>> tuple(r_filter.roll().outcomes())
    (4, 6, 2, 8, 0)

    ```
    """
    return FilterRoller(predicate, sources, annotation=annotation)

filter_from_values(predicate: _PredicateT, *values: _ValueT, *, annotation: Any = '') -> FilterRoller classmethod

Shorthand for cls.filter_from_values_iterable(predicate, values,annotation=annotation).

See the filter_from_values_iterable method.

Source code in dyce/r.py
@classmethod
@beartype
def filter_from_values(
    cls,
    predicate: _PredicateT,
    *values: _ValueT,
    annotation: Any = "",
) -> FilterRoller:
    r"""
    Shorthand for ``#!python cls.filter_from_values_iterable(predicate, values,
    annotation=annotation)``.

    See the
    [``filter_from_values_iterable`` method][dyce.r.R.filter_from_values_iterable].
    """
    return cls.filter_from_values_iterable(predicate, values, annotation=annotation)

filter_from_values_iterable(predicate: _PredicateT, values: Iterable[_ValueT], annotation: Any = '') -> FilterRoller classmethod

Shorthand for cls.filter_from_sources_iterable((cls.from_value(value) forvalue in values), annotation=annotation).

See the from_value and filter_from_sources_iterable methods.

Source code in dyce/r.py
@classmethod
@beartype
def filter_from_values_iterable(
    cls,
    predicate: _PredicateT,
    values: Iterable[_ValueT],
    annotation: Any = "",
) -> FilterRoller:
    r"""
    Shorthand for ``#!python cls.filter_from_sources_iterable((cls.from_value(value) for
    value in values), annotation=annotation)``.

    See the [``from_value``][dyce.r.R.from_value] and
    [``filter_from_sources_iterable``][dyce.r.R.filter_from_sources_iterable]
    methods.
    """
    return cls.filter_from_sources_iterable(
        predicate,
        (cls.from_value(value) for value in values),
        annotation=annotation,
    )

from_sources(*sources: _SourceT, *, annotation: Any = '') -> PoolRoller classmethod

Shorthand for cls.from_sources_iterable(rs, annotation=annotation).

See the from_sources_iterable method.

Source code in dyce/r.py
@classmethod
@beartype
def from_sources(
    cls,
    *sources: _SourceT,
    annotation: Any = "",
) -> PoolRoller:
    r"""
    Shorthand for ``#!python cls.from_sources_iterable(rs, annotation=annotation)``.

    See the [``from_sources_iterable`` method][dyce.r.R.from_sources_iterable].
    """
    return cls.from_sources_iterable(sources, annotation=annotation)

from_sources_iterable(sources: Iterable[_SourceT], annotation: Any = '') -> PoolRoller classmethod

Creates and returns a roller for “pooling” zero or more sources.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> r_pool = R.from_sources_iterable(R.from_value(h) for h in (H((1, 2)), H((3, 4)), H((5, 6))))
>>> roll = r_pool.roll()
>>> tuple(roll.outcomes())
(2, 4, 6)
>>> roll
Roll(
  r=...,
  roll_outcomes=(
    RollOutcome(
      value=2,
      sources=(),
    ),
    RollOutcome(
      value=4,
      sources=(),
    ),
    RollOutcome(
      value=6,
      sources=(),
    ),
  ),
  source_rolls=...,
)
Source code in dyce/r.py
@classmethod
@beartype
def from_sources_iterable(
    cls,
    sources: Iterable[_SourceT],
    annotation: Any = "",
) -> PoolRoller:
    r"""
    Creates and returns a roller for “pooling” zero or more *sources*.

    <!-- BEGIN MONKEY PATCH --
    For deterministic outcomes.

    >>> import random
    >>> from dyce import rng
    >>> rng.RNG = random.Random(1633056341)

      -- END MONKEY PATCH -->

    ``` python
    >>> r_pool = R.from_sources_iterable(R.from_value(h) for h in (H((1, 2)), H((3, 4)), H((5, 6))))
    >>> roll = r_pool.roll()
    >>> tuple(roll.outcomes())
    (2, 4, 6)
    >>> roll
    Roll(
      r=...,
      roll_outcomes=(
        RollOutcome(
          value=2,
          sources=(),
        ),
        RollOutcome(
          value=4,
          sources=(),
        ),
        RollOutcome(
          value=6,
          sources=(),
        ),
      ),
      source_rolls=...,
    )

    ```
    """
    return PoolRoller(sources, annotation=annotation)

from_value(value: _ValueT, annotation: Any = '') -> ValueRoller classmethod

Creates and returns a ValueRoller from value.

1
2
>>> R.from_value(6)
ValueRoller(value=6, annotation='')
1
2
>>> R.from_value(H(6))
ValueRoller(value=H(6), annotation='')
1
2
>>> R.from_value(P(6, 6))
ValueRoller(value=P(6, 6), annotation='')
Source code in dyce/r.py
@classmethod
@beartype
def from_value(
    cls,
    value: _ValueT,
    annotation: Any = "",
) -> ValueRoller:
    r"""
    Creates and returns a [``ValueRoller``][dyce.r.ValueRoller] from *value*.

    ``` python
    >>> R.from_value(6)
    ValueRoller(value=6, annotation='')

    ```

    ``` python
    >>> R.from_value(H(6))
    ValueRoller(value=H(6), annotation='')

    ```

    ``` python
    >>> R.from_value(P(6, 6))
    ValueRoller(value=P(6, 6), annotation='')

    ```
    """
    return ValueRoller(value, annotation=annotation)

from_values(*values: _ValueT, *, annotation: Any = '') -> PoolRoller classmethod

Shorthand for cls.from_values_iterable(values, annotation=annotation).

See the from_values_iterable method.

Source code in dyce/r.py
@classmethod
@beartype
def from_values(
    cls,
    *values: _ValueT,
    annotation: Any = "",
) -> PoolRoller:
    r"""
    Shorthand for ``#!python cls.from_values_iterable(values, annotation=annotation)``.

    See the [``from_values_iterable`` method][dyce.r.R.from_values_iterable].
    """
    return cls.from_values_iterable(values, annotation=annotation)

from_values_iterable(values: Iterable[_ValueT], annotation: Any = '') -> PoolRoller classmethod

Shorthand for cls.from_sources_iterable((cls.from_value(value) for valuein values), annotation=annotation).

See the from_value and from_sources_iterable methods.

Source code in dyce/r.py
@classmethod
@beartype
def from_values_iterable(
    cls,
    values: Iterable[_ValueT],
    annotation: Any = "",
) -> PoolRoller:
    r"""
    Shorthand for ``#!python cls.from_sources_iterable((cls.from_value(value) for value
    in values), annotation=annotation)``.

    See the [``from_value``][dyce.r.R.from_value] and
    [``from_sources_iterable``][dyce.r.R.from_sources_iterable] methods.
    """
    return cls.from_sources_iterable(
        (cls.from_value(value) for value in values),
        annotation=annotation,
    )

ge(self, other: _ROperandT) -> BinarySumOpRoller

Shorthand for self.map(lambda left, right: left.ge(right), other).

See the map method.

Source code in dyce/r.py
@beartype
def ge(self, other: _ROperandT) -> BinarySumOpRoller:
    r"""
    Shorthand for ``#!python self.map(lambda left, right: left.ge(right), other)``.

    See the [``map`` method][dyce.r.R.map].
    """

    def _ge(left_operand: RollOutcome, right_operand: RollOutcome) -> RollOutcome:
        return left_operand.ge(right_operand)

    return self.map(_ge, other)

gt(self, other: _ROperandT) -> BinarySumOpRoller

Shorthand for self.map(lambda left, right: left.gt(right), other).

See the map method.

Source code in dyce/r.py
@beartype
def gt(self, other: _ROperandT) -> BinarySumOpRoller:
    r"""
    Shorthand for ``#!python self.map(lambda left, right: left.gt(right), other)``.

    See the [``map`` method][dyce.r.R.map].
    """

    def _gt(left_operand: RollOutcome, right_operand: RollOutcome) -> RollOutcome:
        return left_operand.gt(right_operand)

    return self.map(_gt, other)

is_even(self) -> UnarySumOpRoller

Shorthand for: self.umap(lambda operand: operand.is_even()).

See the umap method.

Source code in dyce/r.py
@beartype
def is_even(self) -> UnarySumOpRoller:
    r"""
    Shorthand for: ``#!python self.umap(lambda operand: operand.is_even())``.

    See the [``umap`` method][dyce.r.R.umap].
    """

    def _is_even(operand: RollOutcome) -> RollOutcome:
        return operand.is_even()

    return self.umap(_is_even)

is_odd(self) -> UnarySumOpRoller

Shorthand for: self.umap(lambda operand: operand.is_odd()).

See the umap method.

Source code in dyce/r.py
@beartype
def is_odd(self) -> UnarySumOpRoller:
    r"""
    Shorthand for: ``#!python self.umap(lambda operand: operand.is_odd())``.

    See the [``umap`` method][dyce.r.R.umap].
    """

    def _is_odd(operand: RollOutcome) -> RollOutcome:
        return operand.is_odd()

    return self.umap(_is_odd)

le(self, other: _ROperandT) -> BinarySumOpRoller

Shorthand for self.map(lambda left, right: left.le(right), other).

See the map method.

Source code in dyce/r.py
@beartype
def le(self, other: _ROperandT) -> BinarySumOpRoller:
    r"""
    Shorthand for ``#!python self.map(lambda left, right: left.le(right), other)``.

    See the [``map`` method][dyce.r.R.map].
    """

    def _le(left_operand: RollOutcome, right_operand: RollOutcome) -> RollOutcome:
        return left_operand.le(right_operand)

    return self.map(_le, other)

lt(self, other: _ROperandT) -> BinarySumOpRoller

Shorthand for self.map(lambda left, right: left.lt(right), other).

See the map method.

Source code in dyce/r.py
@beartype
def lt(self, other: _ROperandT) -> BinarySumOpRoller:
    r"""
    Shorthand for ``#!python self.map(lambda left, right: left.lt(right), other)``.

    See the [``map`` method][dyce.r.R.map].
    """

    def _lt(left_operand: RollOutcome, right_operand: RollOutcome) -> RollOutcome:
        return left_operand.lt(right_operand)

    return self.map(_lt, other)

map(self, bin_op: _RollOutcomeBinaryOperatorT, right_operand: _ROperandT, annotation: Any = '') -> BinarySumOpRoller

Creates and returns a BinarySumOpRoller for applying bin_op to this roller and right_operand as its sources. Shorthands exist for many arithmetic operators and comparators.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> import operator
>>> r_bin_op = R.from_value(H(6)).map(operator.__pow__, 2) ; r_bin_op
BinarySumOpRoller(
  bin_op=<built-in function pow>,
  left_source=ValueRoller(value=H(6), annotation=''),
  right_source=ValueRoller(value=2, annotation=''),
  annotation='',
)
>>> r_bin_op == R.from_value(H(6)) ** 2
True
Source code in dyce/r.py
@beartype
def map(
    self,
    bin_op: _RollOutcomeBinaryOperatorT,
    right_operand: _ROperandT,
    annotation: Any = "",
) -> BinarySumOpRoller:
    r"""
    Creates and returns a [``BinarySumOpRoller``][dyce.r.BinarySumOpRoller] for applying
    *bin_op* to this roller and *right_operand* as its sources. Shorthands exist for
    many arithmetic operators and comparators.

    ``` python
    >>> import operator
    >>> r_bin_op = R.from_value(H(6)).map(operator.__pow__, 2) ; r_bin_op
    BinarySumOpRoller(
      bin_op=<built-in function pow>,
      left_source=ValueRoller(value=H(6), annotation=''),
      right_source=ValueRoller(value=2, annotation=''),
      annotation='',
    )
    >>> r_bin_op == R.from_value(H(6)) ** 2
    True

    ```
    """
    if isinstance(right_operand, RealLike):
        right_operand = ValueRoller(right_operand)

    if isinstance(right_operand, (R, RollOutcome)):
        return BinarySumOpRoller(bin_op, self, right_operand, annotation=annotation)
    else:
        raise NotImplementedError

ne(self, other: _ROperandT) -> BinarySumOpRoller

Shorthand for self.map(lambda left, right: left.ne(right), other).

See the map method.

Source code in dyce/r.py
@beartype
def ne(self, other: _ROperandT) -> BinarySumOpRoller:
    r"""
    Shorthand for ``#!python self.map(lambda left, right: left.ne(right), other)``.

    See the [``map`` method][dyce.r.R.map].
    """

    def _ne(left_operand: RollOutcome, right_operand: RollOutcome) -> RollOutcome:
        return left_operand.ne(right_operand)

    return self.map(_ne, other)

rmap(self, left_operand: Union[RealLikeSCU, 'RollOutcome'], bin_op: _RollOutcomeBinaryOperatorT, annotation: Any = '') -> BinarySumOpRoller

Analogous to the map method, but where the caller supplies left_operand.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> import operator
>>> r_bin_op = R.from_value(H(6)).rmap(2, operator.__pow__) ; r_bin_op
BinarySumOpRoller(
  bin_op=<built-in function pow>,
  left_source=ValueRoller(value=2, annotation=''),
  right_source=ValueRoller(value=H(6), annotation=''),
  annotation='',
)
>>> r_bin_op == 2 ** R.from_value(H(6))
True

Note

The positions of left_operand and bin_op are different from map method. This is intentional and serves as a reminder of operand ordering.

Source code in dyce/r.py
@beartype
def rmap(
    self,
    left_operand: Union[RealLikeSCU, "RollOutcome"],
    bin_op: _RollOutcomeBinaryOperatorT,
    annotation: Any = "",
) -> BinarySumOpRoller:
    r"""
    Analogous to the [``map`` method][dyce.r.R.map], but where the caller supplies
    *left_operand*.

    ``` python
    >>> import operator
    >>> r_bin_op = R.from_value(H(6)).rmap(2, operator.__pow__) ; r_bin_op
    BinarySumOpRoller(
      bin_op=<built-in function pow>,
      left_source=ValueRoller(value=2, annotation=''),
      right_source=ValueRoller(value=H(6), annotation=''),
      annotation='',
    )
    >>> r_bin_op == 2 ** R.from_value(H(6))
    True

    ```

    !!! note

        The positions of *left_operand* and *bin_op* are different from
        [``map`` method][dyce.r.R.map]. This is intentional and serves as a reminder
        of operand ordering.
    """
    if isinstance(left_operand, RealLike):
        return BinarySumOpRoller(
            bin_op, ValueRoller(left_operand), self, annotation=annotation
        )
    elif isinstance(left_operand, RollOutcome):
        return BinarySumOpRoller(bin_op, left_operand, self, annotation=annotation)
    else:
        raise NotImplementedError

roll(self) -> Roll

Sub-classes should implement this method to return a new Roll object taking into account any sources.

Note

Implementors guarantee that all RollOutcomes in the returned Roll must be associated with a roll, including all roll outcomes’ sources.

Tip

Show that roll outcomes from source rolls are excluded by creating a new roll outcome with a value of None with the excluded roll outcome as its source. The RollOutcome.euthanize convenience method is provided for this purpose.

See the section on “Dropping dice from prior rolls …” as well as the note in Roll.__init__ for additional color.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
>>> from itertools import chain
>>> class AntonChigurhRoller(R):
...   h_coin_toss = H((0, 1))
...   def roll(self) -> Roll:
...     source_rolls = list(self.source_rolls())
...     def _roll_outcomes_gen():
...       for roll_outcome in chain.from_iterable(source_rolls):
...         if roll_outcome.value is None:
...           # Omit those already deceased
...           continue
...         elif self.h_coin_toss.roll():
...           # This one lives. Wrap the old outcome in a new one with the same value.
...           yield roll_outcome
...         else:
...           # This one dies here. Wrap the old outcome in a new one with a value of None.
...           yield roll_outcome.euthanize()
...     return Roll(self, roll_outcomes=_roll_outcomes_gen(), source_rolls=source_rolls)
>>> ac_r = AntonChigurhRoller(sources=(R.from_value(1), R.from_value(2), R.from_value(3)))
>>> ac_r.roll()
Roll(
  r=AntonChigurhRoller(
    sources=(
      ValueRoller(value=1, annotation=''),
      ValueRoller(value=2, annotation=''),
      ValueRoller(value=3, annotation=''),
    ),
    annotation='',
  ),
  roll_outcomes=(
    RollOutcome(
      value=None,
      sources=(
        RollOutcome(
          value=1,
          sources=(),
        ),
      ),
    ),
    RollOutcome(
      value=2,
      sources=(),
    ),
    RollOutcome(
      value=3,
      sources=(),
    ),
  ),
  source_rolls=(
    Roll(
      r=ValueRoller(value=1, annotation=''),
      roll_outcomes=(
        RollOutcome(
          value=1,
          sources=(),
        ),
      ),
      source_rolls=(),
    ),
    Roll(
      r=ValueRoller(value=2, annotation=''),
      roll_outcomes=(
        RollOutcome(
          value=2,
          sources=(),
        ),
      ),
      source_rolls=(),
    ),
    Roll(
      r=ValueRoller(value=3, annotation=''),
      roll_outcomes=(
        RollOutcome(
          value=3,
          sources=(),
        ),
      ),
      source_rolls=(),
    ),
  ),
)
Source code in dyce/r.py
@abstractmethod
def roll(self) -> Roll:
    r"""
    Sub-classes should implement this method to return a new
    [``Roll`` object][dyce.r.Roll] taking into account any
    [sources][dyce.r.R.sources].

    !!! note

        Implementors guarantee that all [``RollOutcome``][dyce.r.RollOutcome]s in
        the returned [``Roll``][dyce.r.Roll] *must* be associated with a roll,
        *including all roll outcomes’ [``sources``][dyce.r.RollOutcome.sources]*.

    <!-- BEGIN MONKEY PATCH --
    For deterministic outcomes.

    >>> import random
    >>> from dyce import rng
    >>> rng.RNG = random.Random(1633403927)

      -- END MONKEY PATCH -->

    !!! tip

        Show that roll outcomes from source rolls are excluded by creating a new
        roll outcome with a value of ``#!python None`` with the excluded roll
        outcome as its source. The
        [``RollOutcome.euthanize``][dyce.r.RollOutcome.euthanize] convenience method
        is provided for this purpose.

        See the section on “[Dropping dice from prior rolls
        …](rollin.md#dropping-dice-from-prior-rolls-keeping-the-best-three-of-3d6-and-1d8)”
        as well as the note in [``Roll.__init__``][dyce.r.Roll.__init__] for
        additional color.

        ``` python
        >>> from itertools import chain
        >>> class AntonChigurhRoller(R):
        ...   h_coin_toss = H((0, 1))
        ...   def roll(self) -> Roll:
        ...     source_rolls = list(self.source_rolls())
        ...     def _roll_outcomes_gen():
        ...       for roll_outcome in chain.from_iterable(source_rolls):
        ...         if roll_outcome.value is None:
        ...           # Omit those already deceased
        ...           continue
        ...         elif self.h_coin_toss.roll():
        ...           # This one lives. Wrap the old outcome in a new one with the same value.
        ...           yield roll_outcome
        ...         else:
        ...           # This one dies here. Wrap the old outcome in a new one with a value of None.
        ...           yield roll_outcome.euthanize()
        ...     return Roll(self, roll_outcomes=_roll_outcomes_gen(), source_rolls=source_rolls)
        >>> ac_r = AntonChigurhRoller(sources=(R.from_value(1), R.from_value(2), R.from_value(3)))
        >>> ac_r.roll()
        Roll(
          r=AntonChigurhRoller(
            sources=(
              ValueRoller(value=1, annotation=''),
              ValueRoller(value=2, annotation=''),
              ValueRoller(value=3, annotation=''),
            ),
            annotation='',
          ),
          roll_outcomes=(
            RollOutcome(
              value=None,
              sources=(
                RollOutcome(
                  value=1,
                  sources=(),
                ),
              ),
            ),
            RollOutcome(
              value=2,
              sources=(),
            ),
            RollOutcome(
              value=3,
              sources=(),
            ),
          ),
          source_rolls=(
            Roll(
              r=ValueRoller(value=1, annotation=''),
              roll_outcomes=(
                RollOutcome(
                  value=1,
                  sources=(),
                ),
              ),
              source_rolls=(),
            ),
            Roll(
              r=ValueRoller(value=2, annotation=''),
              roll_outcomes=(
                RollOutcome(
                  value=2,
                  sources=(),
                ),
              ),
              source_rolls=(),
            ),
            Roll(
              r=ValueRoller(value=3, annotation=''),
              roll_outcomes=(
                RollOutcome(
                  value=3,
                  sources=(),
                ),
              ),
              source_rolls=(),
            ),
          ),
        )

        ```
    """
    ...

select(self, *which: _GetItemT, *, annotation: Any = '') -> SelectionRoller

Shorthand for self.select_iterable(which, annotation=annotation).

See the select_iterable method.

Source code in dyce/r.py
@beartype
def select(
    self,
    *which: _GetItemT,
    annotation: Any = "",
) -> SelectionRoller:
    r"""
    Shorthand for ``#!python self.select_iterable(which, annotation=annotation)``.

    See the [``select_iterable`` method][dyce.r.R.select_iterable].
    """
    return self.select_iterable(which, annotation=annotation)

select_from_sources(which: Iterable[_GetItemT], *sources: _SourceT, *, annotation: Any = '') -> SelectionRoller classmethod

Shorthand for cls.select_from_sources_iterable(which, sources,annotation=annotation).

See the select_from_sources_iterable method.

Source code in dyce/r.py
@classmethod
@beartype
def select_from_sources(
    cls,
    which: Iterable[_GetItemT],
    *sources: _SourceT,
    annotation: Any = "",
) -> SelectionRoller:
    r"""
    Shorthand for ``#!python cls.select_from_sources_iterable(which, sources,
    annotation=annotation)``.

    See the [``select_from_sources_iterable``
    method][dyce.r.R.select_from_sources_iterable].
    """
    return cls.select_from_sources_iterable(which, sources, annotation=annotation)

select_from_sources_iterable(which: Iterable[_GetItemT], sources: Iterable[_SourceT], annotation: Any = '') -> SelectionRoller classmethod

Creates and returns a SelectionRoller for applying selection which to sorted outcomes from sources.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
>>> r_select = R.select_from_sources_iterable(
...   (0, -1, slice(3, 6), slice(6, 3, -1), -1, 0),
...   (R.from_value(i) for i in (5, 4, 6, 3, 7, 2, 8, 1, 9, 0)),
... ) ; r_select
SelectionRoller(
  which=(0, -1, slice(3, 6, None), slice(6, 3, -1), -1, 0),
  sources=(
    ValueRoller(value=5, annotation=''),
    ValueRoller(value=4, annotation=''),
    ...,
    ValueRoller(value=9, annotation=''),
    ValueRoller(value=0, annotation=''),
  ),
  annotation='',
)
>>> tuple(r_select.roll().outcomes())
(0, 9, 3, 4, 5, 6, 5, 4, 9, 0)
Source code in dyce/r.py
@classmethod
@beartype
def select_from_sources_iterable(
    cls,
    which: Iterable[_GetItemT],
    sources: Iterable[_SourceT],
    annotation: Any = "",
) -> SelectionRoller:
    r"""
    Creates and returns a [``SelectionRoller``][dyce.r.SelectionRoller] for applying
    selection *which* to sorted outcomes from *sources*.

    ``` python
    >>> r_select = R.select_from_sources_iterable(
    ...   (0, -1, slice(3, 6), slice(6, 3, -1), -1, 0),
    ...   (R.from_value(i) for i in (5, 4, 6, 3, 7, 2, 8, 1, 9, 0)),
    ... ) ; r_select
    SelectionRoller(
      which=(0, -1, slice(3, 6, None), slice(6, 3, -1), -1, 0),
      sources=(
        ValueRoller(value=5, annotation=''),
        ValueRoller(value=4, annotation=''),
        ...,
        ValueRoller(value=9, annotation=''),
        ValueRoller(value=0, annotation=''),
      ),
      annotation='',
    )
    >>> tuple(r_select.roll().outcomes())
    (0, 9, 3, 4, 5, 6, 5, 4, 9, 0)

    ```
    """
    return SelectionRoller(which, sources, annotation=annotation)

select_from_values(which: Iterable[_GetItemT], *values: _ValueT, *, annotation: Any = '') -> SelectionRoller classmethod

Shorthand for cls.select_from_values_iterable(which, values,annotation=annotation).

See the select_from_values_iterable method.

Source code in dyce/r.py
@classmethod
@beartype
def select_from_values(
    cls,
    which: Iterable[_GetItemT],
    *values: _ValueT,
    annotation: Any = "",
) -> SelectionRoller:
    r"""
    Shorthand for ``#!python cls.select_from_values_iterable(which, values,
    annotation=annotation)``.

    See the
    [``select_from_values_iterable`` method][dyce.r.R.select_from_values_iterable].
    """
    return cls.select_from_values_iterable(which, values, annotation=annotation)

select_from_values_iterable(which: Iterable[_GetItemT], values: Iterable[_ValueT], annotation: Any = '') -> SelectionRoller classmethod

Shorthand for cls.select_from_sources_iterable((cls.from_value(value) forvalue in values), annotation=annotation).

See the from_value and select_from_sources_iterable methods.

Source code in dyce/r.py
@classmethod
@beartype
def select_from_values_iterable(
    cls,
    which: Iterable[_GetItemT],
    values: Iterable[_ValueT],
    annotation: Any = "",
) -> SelectionRoller:
    r"""
    Shorthand for ``#!python cls.select_from_sources_iterable((cls.from_value(value) for
    value in values), annotation=annotation)``.

    See the [``from_value``][dyce.r.R.from_value] and
    [``select_from_sources_iterable``][dyce.r.R.select_from_sources_iterable]
    methods.
    """
    return cls.select_from_sources_iterable(
        which,
        (cls.from_value(value) for value in values),
        annotation=annotation,
    )

select_iterable(self, which: Iterable[_GetItemT], annotation: Any = '') -> SelectionRoller

Shorthand for type(self).select_from_sources(which, self,annotation=annotation).

See the select_from_sources method.

Source code in dyce/r.py
@beartype
def select_iterable(
    self,
    which: Iterable[_GetItemT],
    annotation: Any = "",
) -> SelectionRoller:
    r"""
    Shorthand for ``#!python type(self).select_from_sources(which, self,
    annotation=annotation)``.

    See the [``select_from_sources`` method][dyce.r.R.select_from_sources].
    """
    return type(self).select_from_sources(which, self, annotation=annotation)

source_rolls(self) -> Iterator['Roll']

Generates new rolls from all sources.

Source code in dyce/r.py
@beartype
def source_rolls(self) -> Iterator["Roll"]:
    r"""
    Generates new rolls from all [``sources``][dyce.r.R.sources].
    """
    for source in self.sources:
        yield source.roll()

umap(self, un_op: _RollOutcomeUnaryOperatorT, annotation: Any = '') -> UnarySumOpRoller

Creates and returns a UnarySumOpRoller roller for applying un_op to this roller as its source.

1
2
3
4
5
6
7
8
9
>>> import operator
>>> r_un_op = R.from_value(H(6)).umap(operator.__neg__) ; r_un_op
UnarySumOpRoller(
  un_op=<built-in function neg>,
  source=ValueRoller(value=H(6), annotation=''),
  annotation='',
)
>>> r_un_op == -R.from_value(H(6))
True
Source code in dyce/r.py
@beartype
def umap(
    self,
    un_op: _RollOutcomeUnaryOperatorT,
    annotation: Any = "",
) -> UnarySumOpRoller:
    r"""
    Creates and returns a [``UnarySumOpRoller``][dyce.r.UnarySumOpRoller] roller for
    applying *un_op* to this roller as its source.

    ``` python
    >>> import operator
    >>> r_un_op = R.from_value(H(6)).umap(operator.__neg__) ; r_un_op
    UnarySumOpRoller(
      un_op=<built-in function neg>,
      source=ValueRoller(value=H(6), annotation=''),
      annotation='',
    )
    >>> r_un_op == -R.from_value(H(6))
    True

    ```
    """
    return UnarySumOpRoller(un_op, self, annotation=annotation)

RepeatRoller (R)

A roller to implement the __matmul__ operator. It is akin to a homogeneous PoolRoller containing n identical sources.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> d20 = H(20)
>>> r_d20 = R.from_value(d20)
>>> r_d20_100 = 100@r_d20 ; r_d20_100
RepeatRoller(
  n=100,
  source=ValueRoller(value=H(20), annotation=''),
  annotation='',
)
>>> all(outcome in d20 for outcome in r_d20_100.roll().outcomes())
True

__slots__: Union[str, Iterable[str]] special

n: int property readonly

The number of times to “repeat” the roller’s sole source.

__eq__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __eq__(self, other) -> bool:
    return super().__eq__(other) and self.n == other.n

__init__(self, n: SupportsIntSCU, source: _SourceT, annotation: Any = '', **kw) special

Source code in dyce/r.py
@beartype
def __init__(
    self,
    n: SupportsIntSCU,
    source: _SourceT,
    annotation: Any = "",
    **kw,
):
    r"Initializer."
    super().__init__(sources=(source,), annotation=annotation, **kw)
    self._n = as_int(n)

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        (source,) = self.sources

        return f"""{type(self).__name__}(
  n={self.n!r},
  source={indent(repr(source), "  ").strip()},
  annotation={self.annotation!r},
)"""

roll(self) -> Roll

Source code in dyce/r.py
@beartype
def roll(self) -> Roll:
    r""""""
    source_rolls: List[Roll] = []

    for _ in range(self.n):
        source_rolls.extend(self.source_rolls())

    return Roll(
        self,
        roll_outcomes=(
            roll_outcome
            for roll_outcome in chain.from_iterable(source_rolls)
            if roll_outcome.value is not None
        ),
        source_rolls=source_rolls,
    )

Roll (Sequence, Generic)

Experimental

This class should be considered experimental and may change or disappear in future versions.

An immutable roll result (or “roll” for short). More specifically, the result of calling the R.roll method. Rolls are sequences of RollOutcome objects that can be assembled into trees.

__slots__: Union[str, Iterable[str]] special

annotation: Any property readonly

Shorthand for self.r.annotation.

See the R.annotation property.

r: R property readonly

The roller that generated the roll.

source_rolls: Tuple[Roll, ...] property readonly

The source rolls from which this roll was generated.

__getitem__(self, key: _GetItemT) -> Union[RollOutcome, Tuple[RollOutcome, ...]] special

Source code in dyce/r.py
@beartype
# TODO(posita): See <https://github.com/python/mypy/issues/8393>
# TODO(posita): See <https://github.com/beartype/beartype/issues/39#issuecomment-871914114> et seq.
def __getitem__(  # type: ignore [override]
    self,
    key: _GetItemT,
) -> Union[RollOutcome, Tuple[RollOutcome, ...]]:
    if isinstance(key, slice):
        return self._roll_outcomes[key]
    else:
        return self._roll_outcomes[__index__(key)]

__init__(self, r: R, roll_outcomes: Iterable[RollOutcome], source_rolls: Iterable['Roll'] = ()) special

Initializer.

This initializer will associate each of roll_outcomes with the newly constructed roll if they do not already have a source_roll.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
>>> r_4 = ValueRoller(4)
>>> roll = r_4.roll()
>>> new_roll = Roll(r_4, roll) ; new_roll
Roll(
  r=ValueRoller(value=4, annotation=''),
  roll_outcomes=(
    RollOutcome(
      value=4,
      sources=(),
    ),
  ),
  source_rolls=(),
)
>>> roll[0].source_roll == roll
True
>>> roll[0].r == r_4
True

Note

Technically, this violates the immutability of roll outcomes.

dyce does not generally contemplate creation of rolls or roll outcomes outside the womb of R.roll implementations. Roll and RollOutcome objects generally mate for life, being created exclusively for (and in close proximity to) one another. A roll manipulating a roll outcome’s internal state post initialization may seem unseemly, but that intimacy is a fundamental part of their primordial ritual.

That being said, you’re an adult. Do what you want. Just know that if you’re going to create your own roll outcomes and pimp them out all over town, they might pick something up along the way.

See also the RollOutcome.source_roll property.

Source code in dyce/r.py
@experimental
@beartype
def __init__(
    self,
    r: R,
    roll_outcomes: Iterable[RollOutcome],
    source_rolls: Iterable["Roll"] = (),
):
    r"""
    Initializer.

    This initializer will associate each of *roll_outcomes* with the newly
    constructed roll if they do not already have a
    [``source_roll``][dyce.r.RollOutcome.source_roll].

    ``` python
    >>> r_4 = ValueRoller(4)
    >>> roll = r_4.roll()
    >>> new_roll = Roll(r_4, roll) ; new_roll
    Roll(
      r=ValueRoller(value=4, annotation=''),
      roll_outcomes=(
        RollOutcome(
          value=4,
          sources=(),
        ),
      ),
      source_rolls=(),
    )
    >>> roll[0].source_roll == roll
    True
    >>> roll[0].r == r_4
    True

    ```

    !!! note

        Technically, this violates the immutability of roll outcomes.

        ``dyce`` does not generally contemplate creation of rolls or roll outcomes
        outside the womb of [``R.roll``][dyce.r.R.roll] implementations.
        [``Roll``][dyce.r.Roll] and [``RollOutcome``][dyce.r.RollOutcome] objects
        generally mate for life, being created exclusively for (and in close
        proximity to) one another. A roll manipulating a roll outcome’s internal
        state post initialization may seem unseemly, but that intimacy is a
        fundamental part of their primordial ritual.

        That being said, you’re an adult. Do what you want. Just know that if you’re
        going to create your own roll outcomes and pimp them out all over town, they
        might pick something up along the way.

        See also the
        [``RollOutcome.source_roll`` property][dyce.r.RollOutcome.source_roll].
    """
    super().__init__()
    self._r = r
    self._roll_outcomes = tuple(roll_outcomes)
    self._source_rolls = tuple(source_rolls)

    for roll_outcome in self._roll_outcomes:
        if roll_outcome._roll is None:
            roll_outcome._roll = self

__iter__(self) -> Iterator[RollOutcome] special

Source code in dyce/r.py
@beartype
def __iter__(self) -> Iterator[RollOutcome]:
    return iter(self._roll_outcomes)

__len__(self) -> int special

Source code in dyce/r.py
@beartype
def __len__(self) -> int:
    return len(self._roll_outcomes)

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        return f"""{type(self).__name__}(
  r={indent(repr(self.r), "  ").strip()},
  roll_outcomes=({_seq_repr(self)}),
  source_rolls=({_seq_repr(self.source_rolls)}),
)"""

adopt(self, sources: Iterable['RollOutcome'] = (), coalesce_mode: CoalesceMode = <CoalesceMode.REPLACE: 1>) -> Roll

Shorthand for Roll(self.r, (roll_outcome.adopt(sources,coalesce_mode) for roll_outcome in self), self.source_rolls).

Source code in dyce/r.py
@beartype
def adopt(
    self,
    sources: Iterable["RollOutcome"] = (),
    coalesce_mode: CoalesceMode = CoalesceMode.REPLACE,
) -> Roll:
    r"""
    Shorthand for ``#!python Roll(self.r, (roll_outcome.adopt(sources,
    coalesce_mode) for roll_outcome in self), self.source_rolls)``.
    """
    return type(self)(
        self.r,
        (roll_outcome.adopt(sources, coalesce_mode) for roll_outcome in self),
        self.source_rolls,
    )

outcomes(self) -> Iterator[RealLikeSCU]

Shorthand for (roll_outcome.value for roll_outcome in self ifroll_outcome.value is not None).

Info

Unlike H.roll and P.roll, these outcomes are not sorted. Instead, they retain the ordering as passed to __init__.

1
2
3
4
5
6
7
>>> r_3d6 = 3@R.from_value(H(6))
>>> r_3d6_neg = 3@-R.from_value(H(6))
>>> roll = R.from_sources(r_3d6, r_3d6_neg).roll()
>>> tuple(roll.outcomes())
(1, 3, 1, -4, -6, -1)
>>> len(roll)
6
Source code in dyce/r.py
@beartype
def outcomes(self) -> Iterator[RealLikeSCU]:
    r"""
    Shorthand for ``#!python (roll_outcome.value for roll_outcome in self if
    roll_outcome.value is not None)``.

    <!-- BEGIN MONKEY PATCH --
    For deterministic outcomes.

    >>> import random
    >>> from dyce import rng
    >>> rng.RNG = random.Random(1633056410)

      -- END MONKEY PATCH -->

    !!! info

        Unlike [``H.roll``][dyce.h.H.roll] and [``P.roll``][dyce.p.P.roll], these
        outcomes are *not* sorted. Instead, they retain the ordering as passed to
        [``__init__``][dyce.r.Roll.__init__].

        ``` python
        >>> r_3d6 = 3@R.from_value(H(6))
        >>> r_3d6_neg = 3@-R.from_value(H(6))
        >>> roll = R.from_sources(r_3d6, r_3d6_neg).roll()
        >>> tuple(roll.outcomes())
        (1, 3, 1, -4, -6, -1)
        >>> len(roll)
        6

        ```
    """
    return (
        roll_outcome.value
        for roll_outcome in self
        if roll_outcome.value is not None
    )

total(self) -> RealLikeSCU

Shorthand for sum(self.outcomes()).

Source code in dyce/r.py
@beartype
def total(self) -> RealLikeSCU:
    r"""
    Shorthand for ``#!python sum(self.outcomes())``.
    """
    return sum(self.outcomes())

RollOutcome

Experimental

This class should be considered experimental and may change or disappear in future versions.

A single, (mostly) immutable outcome generated by a roll.

__slots__: Union[str, Iterable[str]] special

annotation: Any property readonly

Shorthand for self.source_roll.annotation.

See the source_roll and Roll.annotation properties.

r: R property readonly

Shorthand for self.source_roll.r.

See the source_roll and Roll.r properties.

source_roll: Roll property readonly

Returns the roll if one has been associated with this roll outcome. Usually that happens by submitting the roll outcome to the Roll.__init__ method inside a R.roll method implementation. Accessing this property before the roll outcome has been associated with a roll is considered a programming error.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
>>> ro = RollOutcome(4)
>>> ro.source_roll
Traceback (most recent call last):
  ...
AssertionError: RollOutcome.source_roll accessed before associating the roll outcome with a roll (usually via Roll.__init__)
assert None is not None
>>> roll = Roll(R.from_value(4), roll_outcomes=(ro,))
>>> ro.source_roll
Roll(
  r=ValueRoller(value=4, annotation=''),
  roll_outcomes=(
    RollOutcome(
      value=4,
      sources=(),
    ),
  ),
  source_rolls=(),
)

sources: Tuple[RollOutcome, ...] property readonly

The source roll outcomes from which this roll outcome was generated.

value: Optional[RealLikeSCU] property readonly

The outcome value. A value of None is used to signal that a source’s roll outcome was excluded by the roller.

__abs__(self) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __abs__(self) -> RollOutcome:
    return self.umap(__abs__)

__add__(self, other: _RollOutcomeOperandT) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __add__(self, other: _RollOutcomeOperandT) -> RollOutcome:
    try:
        return self.map(__add__, other)
    except NotImplementedError:
        return NotImplemented

__and__(self, other: Union['RollOutcome', SupportsIntSCU]) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __and__(self, other: Union["RollOutcome", SupportsIntSCU]) -> RollOutcome:
    try:
        if isinstance(other, SupportsInt):
            other = as_int(other)

        return self.map(__and__, other)
    except (NotImplementedError, TypeError):
        return NotImplemented

__eq__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __eq__(self, other) -> bool:
    if isinstance(other, RollOutcome):
        return bool(__eq__(self.value, other.value))
    else:
        return super().__eq__(other)

__floordiv__(self, other: _RollOutcomeOperandT) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __floordiv__(self, other: _RollOutcomeOperandT) -> RollOutcome:
    try:
        return self.map(__floordiv__, other)
    except NotImplementedError:
        return NotImplemented

__ge__(self, other: _RollOutcomeOperandT) -> bool special

Source code in dyce/r.py
@beartype
def __ge__(self, other: _RollOutcomeOperandT) -> bool:
    if isinstance(other, RollOutcome):
        return bool(__ge__(self.value, other.value))
    else:
        return NotImplemented

__gt__(self, other: _RollOutcomeOperandT) -> bool special

Source code in dyce/r.py
@beartype
def __gt__(self, other: _RollOutcomeOperandT) -> bool:
    if isinstance(other, RollOutcome):
        return bool(__gt__(self.value, other.value))
    else:
        return NotImplemented

__init__(self, value: Optional[RealLikeSCU], sources: Iterable['RollOutcome'] = ()) special

Initializer.

Source code in dyce/r.py
@experimental
@beartype
def __init__(
    self,
    value: Optional[RealLikeSCU],
    sources: Iterable["RollOutcome"] = (),
):
    r"Initializer."
    super().__init__()
    self._value = value
    self._sources = tuple(sources)
    self._roll: Optional[Roll] = None

    if self._value is None and not self._sources:
        raise ValueError("value can only be None if sources is non-empty")

__invert__(self) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __invert__(self) -> RollOutcome:
    return self.umap(__invert__)

__le__(self, other: _RollOutcomeOperandT) -> bool special

Source code in dyce/r.py
@beartype
def __le__(self, other: _RollOutcomeOperandT) -> bool:
    if isinstance(other, RollOutcome):
        return bool(__le__(self.value, other.value))
    else:
        return NotImplemented

__lt__(self, other: _RollOutcomeOperandT) -> bool special

Source code in dyce/r.py
@beartype
def __lt__(self, other: _RollOutcomeOperandT) -> bool:
    if isinstance(other, RollOutcome):
        return bool(__lt__(self.value, other.value))
    else:
        return NotImplemented

__mod__(self, other: _RollOutcomeOperandT) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __mod__(self, other: _RollOutcomeOperandT) -> RollOutcome:
    try:
        return self.map(__mod__, other)
    except NotImplementedError:
        return NotImplemented

__mul__(self, other: _RollOutcomeOperandT) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __mul__(self, other: _RollOutcomeOperandT) -> RollOutcome:
    try:
        return self.map(__mul__, other)
    except NotImplementedError:
        return NotImplemented

__ne__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __ne__(self, other) -> bool:
    if isinstance(other, RollOutcome):
        return bool(__ne__(self.value, other.value))
    else:
        return super().__ne__(other)

__neg__(self) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __neg__(self) -> RollOutcome:
    return self.umap(__neg__)

__or__(self, other: Union['RollOutcome', SupportsIntSCU]) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __or__(self, other: Union["RollOutcome", SupportsIntSCU]) -> RollOutcome:
    try:
        if isinstance(other, SupportsInt):
            other = as_int(other)

        return self.map(__or__, other)
    except (NotImplementedError, TypeError):
        return NotImplemented

__pos__(self) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __pos__(self) -> RollOutcome:
    return self.umap(__pos__)

__pow__(self, other: _RollOutcomeOperandT) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __pow__(self, other: _RollOutcomeOperandT) -> RollOutcome:
    try:
        return self.map(__pow__, other)
    except NotImplementedError:
        return NotImplemented

__radd__(self, other: RealLikeSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __radd__(self, other: RealLikeSCU) -> RollOutcome:
    try:
        return self.rmap(other, __add__)
    except NotImplementedError:
        return NotImplemented

__rand__(self, other: SupportsIntSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __rand__(self, other: SupportsIntSCU) -> RollOutcome:
    try:
        return self.rmap(as_int(other), __and__)
    except (NotImplementedError, TypeError):
        return NotImplemented

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        return f"""{type(self).__name__}(
  value={repr(self.value)},
  sources=({_seq_repr(self.sources)}),
)"""

__rfloordiv__(self, other: RealLikeSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __rfloordiv__(self, other: RealLikeSCU) -> RollOutcome:  # type: ignore [misc]
    try:
        return self.rmap(other, __floordiv__)
    except NotImplementedError:
        return NotImplemented

__rmod__(self, other: RealLikeSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __rmod__(self, other: RealLikeSCU) -> RollOutcome:
    try:
        return self.rmap(other, __mod__)
    except NotImplementedError:
        return NotImplemented

__rmul__(self, other: RealLikeSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __rmul__(self, other: RealLikeSCU) -> RollOutcome:
    try:
        return self.rmap(other, __mul__)
    except NotImplementedError:
        return NotImplemented

__ror__(self, other: SupportsIntSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __ror__(self, other: SupportsIntSCU) -> RollOutcome:
    try:
        return self.rmap(as_int(other), __or__)
    except (NotImplementedError, TypeError):
        return NotImplemented

__rpow__(self, other: RealLikeSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __rpow__(self, other: RealLikeSCU) -> RollOutcome:
    try:
        return self.rmap(other, __pow__)
    except NotImplementedError:
        return NotImplemented

__rsub__(self, other: RealLikeSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __rsub__(self, other: RealLikeSCU) -> RollOutcome:
    try:
        return self.rmap(other, __sub__)
    except NotImplementedError:
        return NotImplemented

__rtruediv__(self, other: RealLikeSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __rtruediv__(self, other: RealLikeSCU) -> RollOutcome:
    try:
        return self.rmap(other, __truediv__)
    except NotImplementedError:
        return NotImplemented

__rxor__(self, other: SupportsIntSCU) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __rxor__(self, other: SupportsIntSCU) -> RollOutcome:
    try:
        return self.rmap(as_int(other), __xor__)
    except (NotImplementedError, TypeError):
        return NotImplemented

__sub__(self, other: _RollOutcomeOperandT) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __sub__(self, other: _RollOutcomeOperandT) -> RollOutcome:
    try:
        return self.map(__sub__, other)
    except NotImplementedError:
        return NotImplemented

__truediv__(self, other: _RollOutcomeOperandT) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __truediv__(self, other: _RollOutcomeOperandT) -> RollOutcome:
    try:
        return self.map(__truediv__, other)
    except NotImplementedError:
        return NotImplemented

__xor__(self, other: Union['RollOutcome', SupportsIntSCU]) -> RollOutcome special

Source code in dyce/r.py
@beartype
def __xor__(self, other: Union["RollOutcome", SupportsIntSCU]) -> RollOutcome:
    try:
        if isinstance(other, SupportsInt):
            other = as_int(other)

        return self.map(__xor__, other)
    except (NotImplementedError, TypeError):
        return NotImplemented

adopt(self, sources: Iterable['RollOutcome'] = (), coalesce_mode: CoalesceMode = <CoalesceMode.REPLACE: 1>) -> RollOutcome

Creates and returns a new roll outcome identical to this roll outcome, but with sources replacing or appended to this roll outcome’s sources in accordance with coalesce_mode.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
>>> from dyce.r import CoalesceMode
>>> orig = RollOutcome(1, sources=(RollOutcome(2),)) ; orig
RollOutcome(
  value=1,
  sources=(
    RollOutcome(
      value=2,
      sources=(),
    ),
  ),
)
>>> orig.adopt((RollOutcome(3),), coalesce_mode=CoalesceMode.REPLACE)
RollOutcome(
  value=1,
  sources=(
    RollOutcome(
      value=3,
      sources=(),
    ),
  ),
)
>>> orig.adopt((RollOutcome(3),), coalesce_mode=CoalesceMode.APPEND)
RollOutcome(
  value=1,
  sources=(
    RollOutcome(
      value=2,
      sources=(),
    ),
    RollOutcome(
      value=3,
      sources=(),
    ),
  ),
)
Source code in dyce/r.py
@beartype
def adopt(
    self,
    sources: Iterable["RollOutcome"] = (),
    coalesce_mode: CoalesceMode = CoalesceMode.REPLACE,
) -> RollOutcome:
    r"""
    Creates and returns a new roll outcome identical to this roll outcome, but with
    *sources* replacing or appended to this roll outcome’s sources in accordance
    with *coalesce_mode*.

    ``` python
    >>> from dyce.r import CoalesceMode
    >>> orig = RollOutcome(1, sources=(RollOutcome(2),)) ; orig
    RollOutcome(
      value=1,
      sources=(
        RollOutcome(
          value=2,
          sources=(),
        ),
      ),
    )
    >>> orig.adopt((RollOutcome(3),), coalesce_mode=CoalesceMode.REPLACE)
    RollOutcome(
      value=1,
      sources=(
        RollOutcome(
          value=3,
          sources=(),
        ),
      ),
    )
    >>> orig.adopt((RollOutcome(3),), coalesce_mode=CoalesceMode.APPEND)
    RollOutcome(
      value=1,
      sources=(
        RollOutcome(
          value=2,
          sources=(),
        ),
        RollOutcome(
          value=3,
          sources=(),
        ),
      ),
    )

    ```
    """
    if coalesce_mode is CoalesceMode.REPLACE:
        adopted_sources = sources
    elif coalesce_mode is CoalesceMode.APPEND:
        adopted_sources = chain(self.sources, sources)
    else:
        assert False, f"unrecognized substitution mode {self.coalesce_mode!r}"

    adopted_roll_outcome = type(self)(self.value, adopted_sources)
    adopted_roll_outcome._roll = self._roll

    return adopted_roll_outcome

eq(self, other: _RollOutcomeOperandT) -> RollOutcome

Source code in dyce/r.py
@beartype
def eq(self, other: _RollOutcomeOperandT) -> RollOutcome:
    if isinstance(other, RollOutcome):
        return type(self)(
            bool(__eq__(self.value, other.value)), sources=(self, other)
        )
    else:
        return type(self)(bool(__eq__(self.value, other)), sources=(self,))

euthanize(self) -> RollOutcome

Shorthand for self.umap(lambda operand: None).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> two = RollOutcome(2)
>>> two.euthanize()
RollOutcome(
  value=None,
  sources=(
    RollOutcome(
      value=2,
      sources=(),
    ),
  ),
)

See the umap method.

Source code in dyce/r.py
@beartype
def euthanize(self) -> RollOutcome:
    r"""
    Shorthand for ``#!python self.umap(lambda operand: None)``.

    ``` python
    >>> two = RollOutcome(2)
    >>> two.euthanize()
    RollOutcome(
      value=None,
      sources=(
        RollOutcome(
          value=2,
          sources=(),
        ),
      ),
    )

    ```

    See the [``umap`` method][dyce.r.RollOutcome.umap].
    """

    def _euthanize(operand: Optional[RealLikeSCU]) -> Optional[RealLikeSCU]:
        return None

    return self.umap(_euthanize)

ge(self, other: _RollOutcomeOperandT) -> RollOutcome

Source code in dyce/r.py
@beartype
def ge(self, other: _RollOutcomeOperandT) -> RollOutcome:
    if isinstance(other, RollOutcome):
        return type(self)(
            bool(__ge__(self.value, other.value)), sources=(self, other)
        )
    else:
        return type(self)(bool(__ge__(self.value, other)), sources=(self,))

gt(self, other: _RollOutcomeOperandT) -> RollOutcome

Source code in dyce/r.py
@beartype
def gt(self, other: _RollOutcomeOperandT) -> RollOutcome:
    if isinstance(other, RollOutcome):
        return type(self)(
            bool(__gt__(self.value, other.value)), sources=(self, other)
        )
    else:
        return type(self)(bool(__gt__(self.value, other)), sources=(self,))

is_even(self) -> RollOutcome

Shorthand for: self.umap(dyce.types.is_even).

See the umap method.

Source code in dyce/r.py
@beartype
def is_even(self) -> RollOutcome:
    r"""
    Shorthand for: ``#!python self.umap(dyce.types.is_even)``.

    See the [``umap`` method][dyce.r.RollOutcome.umap].
    """
    return self.umap(is_even)

is_odd(self) -> RollOutcome

Shorthand for: self.umap(dyce.types.is_even).

See the umap method.

Source code in dyce/r.py
@beartype
def is_odd(self) -> RollOutcome:
    r"""
    Shorthand for: ``#!python self.umap(dyce.types.is_even)``.

    See the [``umap`` method][dyce.r.RollOutcome.umap].
    """
    return self.umap(is_odd)

le(self, other: _RollOutcomeOperandT) -> RollOutcome

Source code in dyce/r.py
@beartype
def le(self, other: _RollOutcomeOperandT) -> RollOutcome:
    if isinstance(other, RollOutcome):
        return type(self)(
            bool(__le__(self.value, other.value)), sources=(self, other)
        )
    else:
        return type(self)(bool(__le__(self.value, other)), sources=(self,))

lt(self, other: _RollOutcomeOperandT) -> RollOutcome

Source code in dyce/r.py
@beartype
def lt(self, other: _RollOutcomeOperandT) -> RollOutcome:
    if isinstance(other, RollOutcome):
        return type(self)(
            bool(__lt__(self.value, other.value)), sources=(self, other)
        )
    else:
        return type(self)(bool(__lt__(self.value, other)), sources=(self,))

map(self, bin_op: _BinaryOperatorT, right_operand: _RollOutcomeOperandT) -> RollOutcome

Applies bin_op to the value of this roll outcome as the left operand and right_operand as the right. Shorthands exist for many arithmetic operators and comparators.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
>>> import operator
>>> two = RollOutcome(2)
>>> two.map(operator.__pow__, 10)
RollOutcome(
  value=1024,
  sources=(
    RollOutcome(
      value=2,
      sources=(),
    ),
  ),
)
>>> two.map(operator.__pow__, 10) == two ** 10
True
Source code in dyce/r.py
@beartype
def map(
    self,
    bin_op: _BinaryOperatorT,
    right_operand: _RollOutcomeOperandT,
) -> RollOutcome:
    r"""
    Applies *bin_op* to the value of this roll outcome as the left operand and
    *right_operand* as the right. Shorthands exist for many arithmetic operators and
    comparators.

    ``` python
    >>> import operator
    >>> two = RollOutcome(2)
    >>> two.map(operator.__pow__, 10)
    RollOutcome(
      value=1024,
      sources=(
        RollOutcome(
          value=2,
          sources=(),
        ),
      ),
    )
    >>> two.map(operator.__pow__, 10) == two ** 10
    True

    ```
    """
    if isinstance(right_operand, RollOutcome):
        sources: Tuple[RollOutcome, ...] = (self, right_operand)
        right_operand_value: Optional[RealLikeSCU] = right_operand.value
    else:
        sources = (self,)
        right_operand_value = right_operand

    if isinstance(right_operand_value, RealLike):
        return type(self)(bin_op(self.value, right_operand_value), sources)
    else:
        raise NotImplementedError

ne(self, other: _RollOutcomeOperandT) -> RollOutcome

Source code in dyce/r.py
@beartype
def ne(self, other: _RollOutcomeOperandT) -> RollOutcome:
    if isinstance(other, RollOutcome):
        return type(self)(
            bool(__ne__(self.value, other.value)), sources=(self, other)
        )
    else:
        return type(self)(bool(__ne__(self.value, other)), sources=(self,))

rmap(self, left_operand: RealLikeSCU, bin_op: _BinaryOperatorT) -> RollOutcome

Analogous to the map method, but where the caller supplies left_operand.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
>>> import operator
>>> two = RollOutcome(2)
>>> two.rmap(10, operator.__pow__)
RollOutcome(
  value=100,
  sources=(
    RollOutcome(
      value=2,
      sources=(),
    ),
  ),
)
>>> two.rmap(10, operator.__pow__) == 10 ** two
True

Note

The positions of left_operand and bin_op are different from map method. This is intentional and serves as a reminder of operand ordering.

Source code in dyce/r.py
@beartype
def rmap(
    self,
    left_operand: RealLikeSCU,
    bin_op: _BinaryOperatorT,
) -> RollOutcome:
    r"""
    Analogous to the [``map`` method][dyce.r.RollOutcome.map], but where the caller
    supplies *left_operand*.

    ``` python
    >>> import operator
    >>> two = RollOutcome(2)
    >>> two.rmap(10, operator.__pow__)
    RollOutcome(
      value=100,
      sources=(
        RollOutcome(
          value=2,
          sources=(),
        ),
      ),
    )
    >>> two.rmap(10, operator.__pow__) == 10 ** two
    True

    ```

    !!! note

        The positions of *left_operand* and *bin_op* are different from
        [``map`` method][dyce.r.RollOutcome.map]. This is intentional and serves as
        a reminder of operand ordering.
    """
    if isinstance(left_operand, RealLike):
        return type(self)(bin_op(left_operand, self.value), sources=(self,))
    else:
        raise NotImplementedError

umap(self, un_op: _UnaryOperatorT) -> RollOutcome

Applies un_op to the value of this roll outcome. Shorthands exist for many arithmetic operators and comparators.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
>>> import operator
>>> two_neg = RollOutcome(-2)
>>> two_neg.umap(operator.__neg__)
RollOutcome(
  value=2,
  sources=(
    RollOutcome(
      value=-2,
      sources=(),
    ),
  ),
)
>>> two_neg.umap(operator.__neg__) == -two_neg
True
Source code in dyce/r.py
@beartype
def umap(
    self,
    un_op: _UnaryOperatorT,
) -> RollOutcome:
    r"""
    Applies *un_op* to the value of this roll outcome. Shorthands exist for many
    arithmetic operators and comparators.

    ``` python
    >>> import operator
    >>> two_neg = RollOutcome(-2)
    >>> two_neg.umap(operator.__neg__)
    RollOutcome(
      value=2,
      sources=(
        RollOutcome(
          value=-2,
          sources=(),
        ),
      ),
    )
    >>> two_neg.umap(operator.__neg__) == -two_neg
    True

    ```
    """
    return type(self)(un_op(self.value), sources=(self,))

RollWalkerVisitor

Experimental

This class (and its descendants) should be considered experimental and may change or disappear in future versions.

Abstract visitor interface for use with walk called for each Roll object found.

__slots__: Union[str, Iterable[str]] special

on_roll(self, roll: Roll, parents: Iterator[Roll]) -> None

Source code in dyce/r.py
@abstractmethod
def on_roll(self, roll: Roll, parents: Iterator[Roll]) -> None:
    ...

RollerWalkerVisitor

Experimental

This class (and its descendants) should be considered experimental and may change or disappear in future versions.

Abstract visitor interface for use with walk called for each R object found.

__slots__: Union[str, Iterable[str]] special

on_roller(self, r: R, parents: Iterator[R]) -> None

Source code in dyce/r.py
@abstractmethod
def on_roller(self, r: R, parents: Iterator[R]) -> None:
    ...

SelectionRoller (R)

A roller for sorting outcomes from its sources and applying a selector which.

Roll outcomes in created rolls are ordered according to the selections which. However, those selections are interpreted as indexes in a sorted view of the source’s roll outcomes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
>>> r_values = R.from_values(10000, 1, 1000, 10, 100)
>>> outcomes = tuple(r_values.roll().outcomes()) ; outcomes
(10000, 1, 1000, 10, 100)
>>> sorted_outcomes = tuple(sorted(outcomes)) ; sorted_outcomes
(1, 10, 100, 1000, 10000)
>>> which = (3, 1, 3, 2)
>>> tuple(sorted_outcomes[i] for i in which)
(1000, 10, 1000, 100)
>>> r_select = r_values.select_iterable(which) ; r_select
SelectionRoller(
  which=(3, 1, 3, 2),
  sources=(
    PoolRoller(
      sources=(
        ValueRoller(value=10000, annotation=''),
        ValueRoller(value=1, annotation=''),
        ValueRoller(value=1000, annotation=''),
        ValueRoller(value=10, annotation=''),
        ValueRoller(value=100, annotation=''),
      ),
      annotation='',
    ),
  ),
  annotation='',
)
>>> roll = r_select.roll()
>>> tuple(roll.outcomes())
(1000, 10, 1000, 100)
>>> roll
Roll(
  r=...,
  roll_outcomes=(
    RollOutcome(
      value=1000,
      sources=(),
    ),
    RollOutcome(
      value=10,
      sources=(),
    ),
    RollOutcome(
      value=1000,
      sources=(),
    ),
    RollOutcome(
      value=100,
      sources=(),
    ),
    RollOutcome(
      value=None,
      sources=(
        RollOutcome(
          value=1,
          sources=(),
        ),
      ),
    ),
    RollOutcome(
      value=None,
      sources=(
        RollOutcome(
          value=10000,
          sources=(),
        ),
      ),
    ),
  ),
  source_rolls=...,
)

__slots__: Union[str, Iterable[str]] special

which: Tuple[_GetItemT, ...] property readonly

The selector this roller applies to the sorted outcomes of its sole source.

__eq__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __eq__(self, other) -> bool:
    return super().__eq__(other) and self.which == other.which

__init__(self, which: Iterable[_GetItemT], sources: Iterable[_SourceT], annotation: Any = '', **kw) special

Source code in dyce/r.py
@beartype
def __init__(
    self,
    which: Iterable[_GetItemT],
    sources: Iterable[_SourceT],
    annotation: Any = "",
    **kw,
):
    r"Initializer."
    super().__init__(sources=sources, annotation=annotation, **kw)
    self._which = tuple(which)

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        return f"""{type(self).__name__}(
  which={self.which!r},
  sources=({_seq_repr(self.sources)}),
  annotation={self.annotation!r},
)"""

roll(self) -> Roll

Source code in dyce/r.py
@beartype
def roll(self) -> Roll:
    r""""""
    source_rolls = list(self.source_rolls())
    roll_outcomes = list(
        roll_outcome
        for roll_outcome in chain.from_iterable(source_rolls)
        if roll_outcome.value is not None
    )
    roll_outcomes.sort(key=attrgetter("value"))
    all_indexes = tuple(range(len(roll_outcomes)))
    selected_indexes = tuple(getitems(all_indexes, self.which))

    def _selected_roll_outcomes():
        for selected_index in selected_indexes:
            selected_roll_outcome = roll_outcomes[selected_index]
            assert selected_roll_outcome.value is not None
            yield selected_roll_outcome

        for excluded_index in set(all_indexes) - set(selected_indexes):
            yield roll_outcomes[excluded_index].euthanize()

    return Roll(
        self,
        roll_outcomes=_selected_roll_outcomes(),
        source_rolls=source_rolls,
    )

SubstitutionRoller (R)

A roller for applying expansion_op to determine when to roll new values up to max_depth times for incorporation via coalesce_mode.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
>>> from dyce.r import SubstitutionRoller
>>> r_d6 = R.from_value(H(6))
>>> r_replace = SubstitutionRoller(
...   lambda outcome: RollOutcome(0) if outcome.value is not None and outcome.value <= 3 else outcome,
...   r_d6,
... )
>>> (2@r_replace).roll()
Roll(
  r=RepeatRoller(
    n=2,
    source=SubstitutionRoller(
      expansion_op=<function <lambda> at ...>,
      source=ValueRoller(value=H(6), annotation=''),
      coalesce_mode=<CoalesceMode.REPLACE: 1>,
      max_depth=1,
      annotation='',
    ),
    annotation='',
  ),
  roll_outcomes=(
    RollOutcome(
      value=0,
      sources=(
        RollOutcome(
          value=2,
          sources=(),
        ),
      ),
    ),
    RollOutcome(
      value=5,
      sources=(),
    ),
  ),
  source_rolls=(...),
)

See the section on “Filtering and substitution” more examples.

__slots__: Tuple[str, ...] special

coalesce_mode: CoalesceMode property readonly

The coalesce mode this roller uses to incorporate substituted outcomes.

expansion_op: _ExpansionOperatorT property readonly

The expansion operator this roller applies to decide whether to substitute outcomes.

max_depth: int property readonly

The max number of times this roller will attempt to substitute an outcome satisfying its expansion_op.

__eq__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __eq__(self, other) -> bool:
    return (
        super().__eq__(other)
        and _callable_cmp(self.expansion_op, other.expansion_op)
        and self.coalesce_mode == other.coalesce_mode
        and self.max_depth == other.max_depth
    )

__init__(self, expansion_op: _ExpansionOperatorT, source: _SourceT, coalesce_mode: CoalesceMode = <CoalesceMode.REPLACE: 1>, max_depth: SupportsIntSCU = 1, annotation: Any = '', **kw) special

Source code in dyce/r.py
@beartype
def __init__(
    self,
    expansion_op: _ExpansionOperatorT,
    source: _SourceT,
    coalesce_mode: CoalesceMode = CoalesceMode.REPLACE,
    max_depth: SupportsIntSCU = 1,
    annotation: Any = "",
    **kw,
):
    r"Initializer."
    super().__init__(sources=(source,), annotation=annotation, **kw)
    self._expansion_op = expansion_op
    self._coalesce_mode = coalesce_mode
    self._max_depth = as_int(max_depth)

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        (source,) = self.sources

        return f"""{type(self).__name__}(
  expansion_op={self.expansion_op!r},
  source={indent(repr(source), "  ").strip()},
  coalesce_mode={self.coalesce_mode!r},
  max_depth={self.max_depth!r},
  annotation={self.annotation!r},
)"""

roll(self) -> Roll

Source code in dyce/r.py
@beartype
def roll(self) -> Roll:
    r""""""
    (source_roll,) = self.source_rolls()
    source_rolls: List[Roll] = []

    def _expanded_roll_outcomes(
        roll: Roll,
        depth: int = 0,
    ) -> Iterator[RollOutcome]:
        source_rolls.append(roll)
        roll_outcomes = (
            roll_outcome for roll_outcome in roll if roll_outcome.value is not None
        )

        if depth >= self.max_depth:
            yield from roll_outcomes

            return

        for roll_outcome in roll_outcomes:
            expanded = self.expansion_op(roll_outcome)

            if isinstance(expanded, RollOutcome):
                if expanded is not roll_outcome:
                    expanded = expanded.adopt((roll_outcome,), CoalesceMode.APPEND)

                yield expanded
            elif isinstance(expanded, Roll):
                if self.coalesce_mode == CoalesceMode.REPLACE:
                    yield roll_outcome.euthanize()
                elif self.coalesce_mode == CoalesceMode.APPEND:
                    yield roll_outcome
                else:
                    assert (
                        False
                    ), f"unrecognized substitution mode {self.coalesce_mode!r}"

                expanded_roll = expanded.adopt((roll_outcome,), CoalesceMode.APPEND)
                yield from _expanded_roll_outcomes(expanded_roll, depth + 1)
            else:
                assert False, f"unrecognized type for expanded value {expanded!r}"

    return Roll(
        self,
        roll_outcomes=_expanded_roll_outcomes(source_roll),
        source_rolls=source_rolls,
    )

UnarySumOpRoller (NarySumOpRoller)

An NarySumOpRoller for applying a unary operator un_op to the sum of all outcomes from its sole source.

__slots__: Union[str, Iterable[str]] special

un_op: _RollOutcomeUnaryOperatorT property readonly

The operator this roller applies to its sources.

__eq__(self, other) -> bool special

Source code in dyce/r.py
@beartype
def __eq__(self, other) -> bool:
    return super().__eq__(other) and (_callable_cmp(self.un_op, other.un_op))

__init__(self, un_op: _RollOutcomeUnaryOperatorT, source: _SourceT, annotation: Any = '', **kw) special

Source code in dyce/r.py
@beartype
def __init__(
    self,
    un_op: _RollOutcomeUnaryOperatorT,
    source: _SourceT,
    annotation: Any = "",
    **kw,
):
    r"Initializer."

    def _op(
        r: R,
        roll_outcomes: Iterable[RollOutcome],
    ) -> Union[RollOutcome, Iterable[RollOutcome]]:
        (operand,) = roll_outcomes

        return un_op(operand)

    super().__init__(op=_op, sources=(source,), annotation=annotation, **kw)
    self._un_op = un_op

__repr__(self) -> str special

Source code in dyce/r.py
    @beartype
    def __repr__(self) -> str:
        (source,) = self.sources

        return f"""{type(self).__name__}(
  un_op={self.un_op!r},
  source={indent(repr(source), "  ").strip()},
  annotation={self.annotation!r},
)"""

ValueRoller (R)

A roller whose roll outcomes are derived from scalars, H objects, P objects, RollOutcome objects, or even Roll objects, instead of other source rollers.

__slots__: Union[str, Iterable[str]] special

value: _ValueT property readonly

The value to be emitted by this roller via its ValueRoller.roll method.

__init__(self, value: _ValueT, annotation: Any = '', **kw) special

Source code in dyce/r.py
@beartype
def __init__(
    self,
    value: _ValueT,
    annotation: Any = "",
    **kw,
):
    r"Initializer."
    super().__init__(sources=(), annotation=annotation, **kw)

    if isinstance(value, P) and not value.is_homogeneous:
        warnings.warn(
            f"using a heterogeneous pool ({value}) is not recommended where traceability is important",
            stacklevel=2,
        )

    self._value = value

__repr__(self) -> str special

Source code in dyce/r.py
@beartype
def __repr__(self) -> str:
    return f"{type(self).__name__}(value={self.value!r}, annotation={self.annotation!r})"

roll(self) -> Roll

Source code in dyce/r.py
@beartype
def roll(self) -> Roll:
    r""""""
    if isinstance(self.value, P):
        return Roll(
            self,
            roll_outcomes=(RollOutcome(outcome) for outcome in self.value.roll()),
        )
    elif isinstance(self.value, H):
        return Roll(self, roll_outcomes=(RollOutcome(self.value.roll()),))
    elif isinstance(self.value, RealLike):
        return Roll(self, roll_outcomes=(RollOutcome(self.value),))
    else:
        assert False, f"unrecognized value type {self.value!r}"

walk(root: Union[Roll, R, RollOutcome], visitor: Union[RollWalkerVisitor, RollerWalkerVisitor, RollOutcomeWalkerVisitor]) -> None

Experimental

This function should be considered experimental and may change or disappear in future versions.

Walks through root, calling visitor for each matching object. No ordering guarantees are made.

On the current implementation

walk performs a breadth-first traversal of root, assembling a secondary index of referencing objects (parents). Visitors are called back grouped first by type, then by order encountered.

Source code in dyce/r.py
@experimental
@beartype
def walk(
    root: Union[Roll, R, RollOutcome],
    visitor: Union[RollWalkerVisitor, RollerWalkerVisitor, RollOutcomeWalkerVisitor],
) -> None:
    r"""
    !!! warning "Experimental"

        This function should be considered experimental and may change or disappear in
        future versions.

    Walks through *root*, calling *visitor* for each matching object. No ordering
    guarantees are made.

    !!! info "On the current implementation"

        ``#!python walk`` performs a breadth-first traversal of *root*, assembling a
        secondary index of referencing objects (parents). Visitors are called back
        grouped first by type, then by order encountered.
    """
    rolls: Dict[int, Roll] = {}
    rollers: Dict[int, R] = {}
    roll_outcomes: Dict[int, RollOutcome] = {}
    roll_parent_ids: DefaultDict[int, Set[int]] = defaultdict(set)
    roller_parent_ids: DefaultDict[int, Set[int]] = defaultdict(set)
    roll_outcome_parent_ids: DefaultDict[int, Set[int]] = defaultdict(set)
    queue = deque((root,))
    roll: Roll
    r: R
    roll_outcome: RollOutcome

    while queue:
        obj = queue.popleft()

        if isinstance(obj, Roll):
            roll = obj

            if id(roll) not in rolls:
                rolls[id(roll)] = roll

                queue.append(roll.r)

                for i, roll_outcome in enumerate(roll):
                    queue.append(roll_outcome)

                for source_roll in roll.source_rolls:
                    roll_parent_ids[id(source_roll)].add(id(roll))
                    queue.append(source_roll)
        elif isinstance(obj, R):
            r = obj

            if id(r) not in rollers:
                rollers[id(r)] = r

                for source_r in r.sources:
                    roller_parent_ids[id(source_r)].add(id(r))
                    queue.append(source_r)
        elif isinstance(obj, RollOutcome):
            roll_outcome = obj

            if id(roll_outcome) not in roll_outcomes:
                roll_outcomes[id(roll_outcome)] = roll_outcome

                for source_roll_outcome in roll_outcome.sources:
                    roll_outcome_parent_ids[id(source_roll_outcome)].add(
                        id(roll_outcome)
                    )
                    queue.append(source_roll_outcome)

    if rolls and isinstance(visitor, RollWalkerVisitor):
        for roll_id, roll in rolls.items():
            visitor.on_roll(roll, (rolls[i] for i in roll_parent_ids[roll_id]))

    if rollers and isinstance(visitor, RollerWalkerVisitor):
        for r_id, r in rollers.items():
            visitor.on_roller(r, (rollers[i] for i in roller_parent_ids[r_id]))

    if roll_outcomes and isinstance(visitor, RollOutcomeWalkerVisitor):
        for roll_outcome_id, roll_outcome in roll_outcomes.items():
            visitor.on_roll_outcome(
                roll_outcome,
                (roll_outcomes[i] for i in roll_outcome_parent_ids[roll_outcome_id]),
            )