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
BasicOpRoller (R)
A roller for applying op to some variation of outcomes from sources.
Any RollOutcome
s returned by op are used directly in the
creation of a new Roll
.
Source code in dyce/r.py
class BasicOpRoller(R):
r"""
A [roller][dyce.r.R] for applying *op* to some variation of outcomes from *sources*.
Any [``RollOutcome``][dyce.r.RollOutcome]s returned by *op* are used directly in the
creation of a new [``Roll``][dyce.r.Roll].
"""
__slots__: Union[str, Iterable[str]] = ("_op",)
# ---- Initializer -----------------------------------------------------------------
@beartype
def __init__(
self,
op: BasicOperatorT,
sources: Iterable[_SourceT],
annotation: Any = "",
**kw,
):
r"Initializer."
super().__init__(sources=sources, annotation=annotation, **kw)
self._op = op
# ---- Overrides -------------------------------------------------------------------
@beartype
def __repr__(self) -> str:
return f"""{type(self).__name__}(
op={self.op!r},
sources=({_seq_repr(self.sources)}),
annotation={self.annotation!r},
)"""
@beartype
def __eq__(self, other) -> bool:
return super().__eq__(other) and (_callable_cmp(self.op, other.op))
@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,
)
# ---- Properties ------------------------------------------------------------------
@property
def op(self) -> BasicOperatorT:
r"""
The operator this roller applies to its sources.
"""
return self._op
__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.
Source code in dyce/r.py
class BinarySumOpRoller(NarySumOpRoller):
r"""
An [``NarySumOpRoller``][dyce.r.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]] = ("_bin_op",)
# ---- Initializer -----------------------------------------------------------------
@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
# ---- Properties ------------------------------------------------------------------
@property
def bin_op(self) -> _RollOutcomeBinaryOperatorT:
r"""
The operator this roller applies to its sources.
"""
return self._bin_op
# ---- Overrides -------------------------------------------------------------------
@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},
)"""
@beartype
def __eq__(self, other) -> bool:
return super().__eq__(other) and (_callable_cmp(self.bin_op, other.bin_op))
__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 |
|
See the section on “Filtering and substitution” more examples.
Source code in dyce/r.py
class FilterRoller(R):
r"""
A [roller][dyce.r.R] for applying *predicate* to filter outcomes its *sources*.
<!-- BEGIN MONKEY PATCH --
For deterministic outcomes.
>>> import random
>>> from dyce import rng
>>> rng.RNG = random.Random(1639580307)
-- END MONKEY PATCH -->
``` python
>>> 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 [operator]
... )
>>> (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](rollin.md#filtering-and-substitution)” more examples.
"""
__slots__: Tuple[str, ...] = ("_predicate",)
# ---- Initializer -----------------------------------------------------------------
@beartype
def __init__(
self,
predicate: _PredicateT,
sources: Iterable[R],
annotation: Any = "",
**kw,
):
r"Initializer."
super().__init__(sources=sources, annotation=annotation, **kw)
self._predicate = predicate
# ---- Overrides -------------------------------------------------------------------
@beartype
def __repr__(self) -> str:
return f"""{type(self).__name__}(
predicate={self.predicate!r},
sources=({_seq_repr(self.sources)}),
annotation={self.annotation!r},
)"""
@beartype
def __eq__(self, other) -> bool:
return super().__eq__(other) and _callable_cmp(self.predicate, other.predicate)
@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,
)
# ---- Properties ------------------------------------------------------------------
@property
def predicate(self) -> _PredicateT:
r"""
The predicate this roller applies to filter its sources.
"""
return self._predicate
__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.
Source code in dyce/r.py
class NarySumOpRoller(BasicOpRoller):
r"""
A [``BasicOpRoller``][dyce.r.BasicOpRoller] for applying *op* to the sum of outcomes
grouped by each of *sources*.
"""
__slots__: Union[str, Iterable[str]] = ()
# ---- Overrides -------------------------------------------------------------------
@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,
)
__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 |
|
Source code in dyce/r.py
class PoolRoller(R):
r"""
A [roller][dyce.r.R] for rolling flattened “pools” from all *sources*.
``` python
>>> 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]] = ()
# ---- Overrides -------------------------------------------------------------------
@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,
)
__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 |
|
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 |
|
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 |
|
1 2 3 4 |
|
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 |
|
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.).
Source code in dyce/r.py
class R:
r"""
!!! warning "Experimental"
This class (and its descendants) should be considered experimental and may
change or disappear in future versions.
Where [``H`` objects][dyce.h.H] and [``P`` objects][dyce.p.P] are used primarily for
enumerating weighted outcomes, ``#!python R`` objects represent rollers. More
specifically, they are immutable nodes assembled in tree-like structures to
represent calculations. Unlike [``H``][dyce.h.H] or [``P``][dyce.p.P] objects,
rollers generate rolls that conform to weighted outcomes without engaging in
computationally expensive enumeration. Roller trees are typically composed from
various ``#!python R`` class methods and operators as well as arithmetic operations.
``` python
>>> 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.
``` python
>>> 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][dyce.h.H] instead.
Roller trees can can be queried via the [``roll`` method][dyce.r.R.roll], which
produces [``Roll`` objects][dyce.r.Roll].
<!-- BEGIN MONKEY PATCH --
For deterministic outcomes.
>>> import random
>>> from dyce import rng
>>> rng.RNG = random.Random(1633056380)
-- END MONKEY PATCH -->
``` python
>>> roll = r_d6.roll()
>>> tuple(roll.outcomes())
(4,)
>>> roll.total()
4
```
``` python
>>> 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][dyce.r.Roll] 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.
``` python
>>> 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][dyce.r.R.annotation]. The
[``annotate`` method][dyce.r.R.annotate] can be used to apply an annotation to
existing roller.
The ``#!python 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.).
<!-- BEGIN MONKEY PATCH --
For type-checking docstrings
>>> from typing import Tuple, Union
>>> from dyce.r import PoolRoller, Roll, RollOutcome, ValueRoller
>>> which: Tuple[Union[int, slice], ...]
-- END MONKEY PATCH -->
"""
__slots__: Union[str, Iterable[str]] = ("_annotation", "_sources")
# ---- Initializer -----------------------------------------------------------------
@experimental
@beartype
def __init__(
self,
sources: Iterable[_SourceT] = (),
annotation: Any = "",
**kw,
):
r"Initializer."
super().__init__()
self._sources = tuple(sources)
self._annotation = annotation
# ---- Overrides -------------------------------------------------------------------
@beartype
def __repr__(self) -> str:
return f"""{type(self).__name__}(
sources=({_seq_repr(self.sources)}),
annotation={self.annotation!r},
)"""
@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)
@beartype
def __ne__(self, other) -> bool:
if isinstance(other, R):
return not __eq__(self, other)
else:
return super().__ne__(other)
@beartype
def __add__(self, other: _ROperandT) -> BinarySumOpRoller:
try:
return self.map(__add__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __radd__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __add__)
except NotImplementedError:
return NotImplemented
@beartype
def __sub__(self, other: _ROperandT) -> BinarySumOpRoller:
try:
return self.map(__sub__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rsub__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __sub__)
except NotImplementedError:
return NotImplemented
@beartype
def __mul__(self, other: _ROperandT) -> BinarySumOpRoller:
try:
return self.map(__mul__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rmul__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __mul__)
except NotImplementedError:
return NotImplemented
@beartype
def __matmul__(self, other: SupportsInt) -> R:
try:
other = as_int(other)
except TypeError:
return NotImplemented
if other < 0:
raise ValueError("argument cannot be negative")
else:
return RepeatRoller(other, self)
@beartype
def __rmatmul__(self, other: SupportsInt) -> R:
return self.__matmul__(other)
@beartype
def __truediv__(self, other: _ROperandT) -> BinarySumOpRoller:
try:
return self.map(__truediv__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rtruediv__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __truediv__)
except NotImplementedError:
return NotImplemented
@beartype
def __floordiv__(self, other: _ROperandT) -> BinarySumOpRoller:
try:
return self.map(__floordiv__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rfloordiv__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __floordiv__)
except NotImplementedError:
return NotImplemented
@beartype
def __mod__(self, other: _ROperandT) -> BinarySumOpRoller:
try:
return self.map(__mod__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rmod__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __mod__)
except NotImplementedError:
return NotImplemented
@beartype
def __pow__(self, other: _ROperandT) -> BinarySumOpRoller:
try:
return self.map(__pow__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rpow__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __pow__)
except NotImplementedError:
return NotImplemented
@beartype
def __and__(self, other: Union[_SourceT, SupportsInt]) -> BinarySumOpRoller:
try:
if isinstance(other, R):
return self.map(__and__, other)
else:
return self.map(__and__, as_int(other))
except NotImplementedError:
return NotImplemented
@beartype
def __rand__(self, other: SupportsInt) -> BinarySumOpRoller:
try:
return self.rmap(as_int(other), __and__)
except NotImplementedError:
return NotImplemented
@beartype
def __xor__(self, other: Union[_SourceT, SupportsInt]) -> BinarySumOpRoller:
try:
if isinstance(other, R):
return self.map(__xor__, other)
else:
return self.map(__xor__, as_int(other))
except NotImplementedError:
return NotImplemented
@beartype
def __rxor__(self, other: SupportsInt) -> BinarySumOpRoller:
try:
return self.rmap(as_int(other), __xor__)
except NotImplementedError:
return NotImplemented
@beartype
def __or__(self, other: Union[_SourceT, SupportsInt]) -> BinarySumOpRoller:
try:
if isinstance(other, R):
return self.map(__or__, other)
else:
return self.map(__or__, as_int(other))
except NotImplementedError:
return NotImplemented
@beartype
def __ror__(self, other: SupportsInt) -> BinarySumOpRoller:
try:
return self.rmap(as_int(other), __or__)
except NotImplementedError:
return NotImplemented
@beartype
def __neg__(self) -> UnarySumOpRoller:
return self.umap(__neg__)
@beartype
def __pos__(self) -> UnarySumOpRoller:
return self.umap(__pos__)
@beartype
def __abs__(self) -> UnarySumOpRoller:
return self.umap(__abs__)
@beartype
def __invert__(self) -> UnarySumOpRoller:
return self.umap(__invert__)
@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=(),
),
),
)
```
"""
...
# ---- Properties ------------------------------------------------------------------
@property
def annotation(self) -> Any:
r"""
Any provided annotation.
"""
return self._annotation
@property
def sources(self) -> Tuple[_SourceT, ...]:
r"""
The roller’s direct sources (if any).
"""
return self._sources
# ---- Methods ---------------------------------------------------------------------
@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)
@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)
@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)
@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)
@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,
)
@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
)
@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)
@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)
@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,
)
@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)
@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)
@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)
@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,
)
@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()
@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
@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
@beartype
def rmap(
self,
left_operand: Union[RealLike, "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
@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)
@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)
@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)
@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)
@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)
@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)
@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)
@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)
@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)
@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)
@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)
@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)
__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, SupportsInt]) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __and__(self, other: Union[_SourceT, SupportsInt]) -> 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: SupportsInt) -> R
special
Source code in dyce/r.py
@beartype
def __matmul__(self, other: SupportsInt) -> 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, SupportsInt]) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __or__(self, other: Union[_SourceT, SupportsInt]) -> 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: RealLike) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __radd__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __add__)
except NotImplementedError:
return NotImplemented
__rand__(self, other: SupportsInt) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __rand__(self, other: SupportsInt) -> 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: RealLike) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __rfloordiv__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __floordiv__)
except NotImplementedError:
return NotImplemented
__rmatmul__(self, other: SupportsInt) -> R
special
Source code in dyce/r.py
@beartype
def __rmatmul__(self, other: SupportsInt) -> R:
return self.__matmul__(other)
__rmod__(self, other: RealLike) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __rmod__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __mod__)
except NotImplementedError:
return NotImplemented
__rmul__(self, other: RealLike) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __rmul__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __mul__)
except NotImplementedError:
return NotImplemented
__ror__(self, other: SupportsInt) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __ror__(self, other: SupportsInt) -> BinarySumOpRoller:
try:
return self.rmap(as_int(other), __or__)
except NotImplementedError:
return NotImplemented
__rpow__(self, other: RealLike) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __rpow__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __pow__)
except NotImplementedError:
return NotImplemented
__rsub__(self, other: RealLike) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __rsub__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __sub__)
except NotImplementedError:
return NotImplemented
__rtruediv__(self, other: RealLike) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __rtruediv__(self, other: RealLike) -> BinarySumOpRoller:
try:
return self.rmap(other, __truediv__)
except NotImplementedError:
return NotImplemented
__rxor__(self, other: SupportsInt) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __rxor__(self, other: SupportsInt) -> 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, SupportsInt]) -> BinarySumOpRoller
special
Source code in dyce/r.py
@beartype
def __xor__(self, other: Union[_SourceT, SupportsInt]) -> 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 |
|
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 |
|
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 |
|
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 |
|
1 2 |
|
1 2 |
|
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 |
|
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[RealLike, '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 |
|
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[RealLike, "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 RollOutcome
s 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 |
|
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 |
|
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 |
|
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 |
|
Source code in dyce/r.py
class RepeatRoller(R):
r"""
A [roller][dyce.r.R] to implement the ``#!python __matmul__`` operator. It is akin
to a homogeneous [``PoolRoller``][dyce.r.PoolRoller] containing *n* identical
*source*s.
``` python
>>> 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]] = ("_n",)
# ---- Initializer -----------------------------------------------------------------
@beartype
def __init__(
self,
n: SupportsInt,
source: _SourceT,
annotation: Any = "",
**kw,
):
r"Initializer."
super().__init__(sources=(source,), annotation=annotation, **kw)
self._n = as_int(n)
# ---- Overrides -------------------------------------------------------------------
@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},
)"""
@beartype
def __eq__(self, other) -> bool:
return super().__eq__(other) and self.n == other.n
@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,
)
# ---- Properties ------------------------------------------------------------------
@property
def n(self) -> int:
r"""
The number of times to “repeat” the roller’s sole source.
"""
return self._n
__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: SupportsInt, source: _SourceT, annotation: Any = '', **kw)
special
Source code in dyce/r.py
@beartype
def __init__(
self,
n: SupportsInt,
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.
Source code in dyce/r.py
class Roll(Sequence[RollOutcome]):
r"""
!!! warning "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][dyce.r.R.roll]. Rolls are sequences of
[``RollOutcome`` objects][dyce.r.RollOutcome] that can be assembled into trees.
"""
__slots__: Union[str, Iterable[str]] = ("_r", "_roll_outcomes", "_source_rolls")
# ---- Initializer -----------------------------------------------------------------
@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
# ---- Overrides -------------------------------------------------------------------
@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)}),
)"""
@beartype
def __len__(self) -> int:
return len(self._roll_outcomes)
@overload
def __getitem__(self, key: SupportsIndex) -> RollOutcome:
...
@overload
def __getitem__(self, key: slice) -> Tuple[RollOutcome, ...]:
...
@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)]
@beartype
def __iter__(self) -> Iterator[RollOutcome]:
return iter(self._roll_outcomes)
# ---- Properties ------------------------------------------------------------------
@property
def annotation(self) -> Any:
r"""
Shorthand for ``#!python self.r.annotation``.
See the [``R.annotation`` property][dyce.r.R.annotation].
"""
return self.r.annotation
@property
def r(self) -> R:
r"""
The roller that generated the roll.
"""
return self._r
@property
def source_rolls(self) -> Tuple[Roll, ...]:
r"""
The source rolls from which this roll was generated.
"""
return self._source_rolls
# ---- Methods ---------------------------------------------------------------------
@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,
)
@beartype
def outcomes(self) -> Iterator[RealLike]:
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
)
@beartype
def total(self) -> RealLike:
r"""
Shorthand for ``#!python sum(self.outcomes())``.
"""
return sum(self.outcomes())
__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 |
|
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[RealLike]
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 |
|
Source code in dyce/r.py
@beartype
def outcomes(self) -> Iterator[RealLike]:
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) -> RealLike
Shorthand for sum(self.outcomes())
.
Source code in dyce/r.py
@beartype
def total(self) -> RealLike:
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.
Source code in dyce/r.py
class RollOutcome:
r"""
!!! warning "Experimental"
This class should be considered experimental and may change or disappear in
future versions.
A single, ([mostly][dyce.r.Roll.__init__]) immutable outcome generated by a roll.
"""
__slots__: Union[str, Iterable[str]] = ("_roll", "_sources", "_value")
# ---- Initializer -----------------------------------------------------------------
@experimental
@beartype
def __init__(
self,
value: Optional[RealLike],
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")
# ---- Overrides -------------------------------------------------------------------
@beartype
def __repr__(self) -> str:
return f"""{type(self).__name__}(
value={repr(self.value)},
sources=({_seq_repr(self.sources)}),
)"""
@beartype
# TODO(posita): See <https://github.com/python/mypy/issues/10943>
def __lt__(self, other: _RollOutcomeOperandT) -> bool: # type: ignore [has-type]
if isinstance(other, RollOutcome):
return bool(__lt__(self.value, other.value))
else:
return NotImplemented
@beartype
# TODO(posita): See <https://github.com/python/mypy/issues/10943>
def __le__(self, other: _RollOutcomeOperandT) -> bool: # type: ignore [has-type]
if isinstance(other, RollOutcome):
return bool(__le__(self.value, other.value))
else:
return NotImplemented
@beartype
def __eq__(self, other) -> bool:
if isinstance(other, RollOutcome):
return bool(__eq__(self.value, other.value))
else:
return super().__eq__(other)
@beartype
def __ne__(self, other) -> bool:
if isinstance(other, RollOutcome):
return bool(__ne__(self.value, other.value))
else:
return super().__ne__(other)
@beartype
def __gt__(self, other: _RollOutcomeOperandT) -> bool:
if isinstance(other, RollOutcome):
return bool(__gt__(self.value, other.value))
else:
return NotImplemented
@beartype
def __ge__(self, other: _RollOutcomeOperandT) -> bool:
if isinstance(other, RollOutcome):
return bool(__ge__(self.value, other.value))
else:
return NotImplemented
@beartype
def __add__(self, other: _RollOutcomeOperandT) -> RollOutcome:
try:
return self.map(__add__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __radd__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __add__)
except NotImplementedError:
return NotImplemented
@beartype
def __sub__(self, other: _RollOutcomeOperandT) -> RollOutcome:
try:
return self.map(__sub__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rsub__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __sub__)
except NotImplementedError:
return NotImplemented
@beartype
def __mul__(self, other: _RollOutcomeOperandT) -> RollOutcome:
try:
return self.map(__mul__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rmul__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __mul__)
except NotImplementedError:
return NotImplemented
@beartype
def __truediv__(self, other: _RollOutcomeOperandT) -> RollOutcome:
try:
return self.map(__truediv__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rtruediv__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __truediv__)
except NotImplementedError:
return NotImplemented
@beartype
def __floordiv__(self, other: _RollOutcomeOperandT) -> RollOutcome:
try:
return self.map(__floordiv__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rfloordiv__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __floordiv__)
except NotImplementedError:
return NotImplemented
@beartype
def __mod__(self, other: _RollOutcomeOperandT) -> RollOutcome:
try:
return self.map(__mod__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rmod__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __mod__)
except NotImplementedError:
return NotImplemented
@beartype
def __pow__(self, other: _RollOutcomeOperandT) -> RollOutcome:
try:
return self.map(__pow__, other)
except NotImplementedError:
return NotImplemented
@beartype
def __rpow__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __pow__)
except NotImplementedError:
return NotImplemented
@beartype
def __and__(self, other: Union["RollOutcome", SupportsInt]) -> RollOutcome:
try:
if isinstance(other, SupportsInt):
other = as_int(other)
return self.map(__and__, other)
except (NotImplementedError, TypeError):
return NotImplemented
@beartype
def __rand__(self, other: SupportsInt) -> RollOutcome:
try:
return self.rmap(as_int(other), __and__)
except (NotImplementedError, TypeError):
return NotImplemented
@beartype
def __xor__(self, other: Union["RollOutcome", SupportsInt]) -> RollOutcome:
try:
if isinstance(other, SupportsInt):
other = as_int(other)
return self.map(__xor__, other)
except (NotImplementedError, TypeError):
return NotImplemented
@beartype
def __rxor__(self, other: SupportsInt) -> RollOutcome:
try:
return self.rmap(as_int(other), __xor__)
except (NotImplementedError, TypeError):
return NotImplemented
@beartype
def __or__(self, other: Union["RollOutcome", SupportsInt]) -> RollOutcome:
try:
if isinstance(other, SupportsInt):
other = as_int(other)
return self.map(__or__, other)
except (NotImplementedError, TypeError):
return NotImplemented
@beartype
def __ror__(self, other: SupportsInt) -> RollOutcome:
try:
return self.rmap(as_int(other), __or__)
except (NotImplementedError, TypeError):
return NotImplemented
@beartype
def __neg__(self) -> RollOutcome:
return self.umap(__neg__)
@beartype
def __pos__(self) -> RollOutcome:
return self.umap(__pos__)
@beartype
def __abs__(self) -> RollOutcome:
return self.umap(__abs__)
@beartype
def __invert__(self) -> RollOutcome:
return self.umap(__invert__)
# ---- Properties ------------------------------------------------------------------
@property
def annotation(self) -> Any:
r"""
Shorthand for ``#!python self.source_roll.annotation``.
See the [``source_roll``][dyce.r.RollOutcome.source_roll] and
[``Roll.annotation``][dyce.r.Roll.annotation] properties.
"""
return self.source_roll.annotation
@property
def r(self) -> R:
r"""
Shorthand for ``#!python self.source_roll.r``.
See the [``source_roll``][dyce.r.RollOutcome.source_roll] and
[``Roll.r``][dyce.r.Roll.r] properties.
"""
return self.source_roll.r
@property
def source_roll(self) -> Roll:
r"""
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][dyce.r.Roll.__init__] inside a
[``R.roll`` method][dyce.r.R.roll] implementation. Accessing this property
before the roll outcome has been associated with a roll is considered a
programming error.
``` python
>>> 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=(),
)
```
"""
assert (
self._roll is not None
), "RollOutcome.source_roll accessed before associating the roll outcome with a roll (usually via Roll.__init__)"
return self._roll
@property
def sources(self) -> Tuple[RollOutcome, ...]:
r"""
The source roll outcomes from which this roll outcome was generated.
"""
return self._sources
@property
def value(self) -> Optional[RealLike]:
r"""
The outcome value. A value of ``#!python None`` is used to signal that a source’s
roll outcome was excluded by the roller.
"""
return self._value
# ---- Methods ---------------------------------------------------------------------
@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[RealLike] = 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
@beartype
def rmap(self, left_operand: RealLike, 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
@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,))
@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,))
@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,))
@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,))
@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,))
@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,))
@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,))
@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)
@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)
@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
@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[RealLike]) -> Optional[RealLike]:
return None
return self.umap(_euthanize)
__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 |
|
sources: Tuple[RollOutcome, ...]
property
readonly
The source roll outcomes from which this roll outcome was generated.
value: Optional[RealLike]
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', SupportsInt]) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __and__(self, other: Union["RollOutcome", SupportsInt]) -> 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[RealLike], sources: Iterable['RollOutcome'] = ())
special
Initializer.
Source code in dyce/r.py
@experimental
@beartype
def __init__(
self,
value: Optional[RealLike],
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
# TODO(posita): See <https://github.com/python/mypy/issues/10943>
def __le__(self, other: _RollOutcomeOperandT) -> bool: # type: ignore [has-type]
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
# TODO(posita): See <https://github.com/python/mypy/issues/10943>
def __lt__(self, other: _RollOutcomeOperandT) -> bool: # type: ignore [has-type]
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', SupportsInt]) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __or__(self, other: Union["RollOutcome", SupportsInt]) -> 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: RealLike) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __radd__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __add__)
except NotImplementedError:
return NotImplemented
__rand__(self, other: SupportsInt) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __rand__(self, other: SupportsInt) -> 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: RealLike) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __rfloordiv__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __floordiv__)
except NotImplementedError:
return NotImplemented
__rmod__(self, other: RealLike) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __rmod__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __mod__)
except NotImplementedError:
return NotImplemented
__rmul__(self, other: RealLike) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __rmul__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __mul__)
except NotImplementedError:
return NotImplemented
__ror__(self, other: SupportsInt) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __ror__(self, other: SupportsInt) -> RollOutcome:
try:
return self.rmap(as_int(other), __or__)
except (NotImplementedError, TypeError):
return NotImplemented
__rpow__(self, other: RealLike) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __rpow__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __pow__)
except NotImplementedError:
return NotImplemented
__rsub__(self, other: RealLike) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __rsub__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __sub__)
except NotImplementedError:
return NotImplemented
__rtruediv__(self, other: RealLike) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __rtruediv__(self, other: RealLike) -> RollOutcome:
try:
return self.rmap(other, __truediv__)
except NotImplementedError:
return NotImplemented
__rxor__(self, other: SupportsInt) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __rxor__(self, other: SupportsInt) -> 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', SupportsInt]) -> RollOutcome
special
Source code in dyce/r.py
@beartype
def __xor__(self, other: Union["RollOutcome", SupportsInt]) -> 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 |
|
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 |
|
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[RealLike]) -> Optional[RealLike]:
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 |
|
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[RealLike] = 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: RealLike, 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 |
|
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: RealLike, 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 |
|
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.
Source code in dyce/r.py
class RollWalkerVisitor:
r"""
!!! warning "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``][dyce.r.walk] called for each
[``Roll`` object][dyce.r.Roll] found.
"""
__slots__: Union[str, Iterable[str]] = ()
# ---- Overrides -------------------------------------------------------------------
@abstractmethod
def on_roll(self, roll: Roll, parents: Iterator[Roll]) -> None:
...
__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.
Source code in dyce/r.py
class RollerWalkerVisitor:
r"""
!!! warning "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``][dyce.r.walk] called for each
[``R`` object][dyce.r.R] found.
"""
__slots__: Union[str, Iterable[str]] = ()
# ---- Overrides -------------------------------------------------------------------
@abstractmethod
def on_roller(self, r: R, parents: Iterator[R]) -> None:
...
__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 |
|
Source code in dyce/r.py
class SelectionRoller(R):
r"""
A [roller][dyce.r.R] 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.
``` python
>>> 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]] = ("_which",)
# ---- Initializer -----------------------------------------------------------------
@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)
# ---- Overrides -------------------------------------------------------------------
@beartype
def __repr__(self) -> str:
return f"""{type(self).__name__}(
which={self.which!r},
sources=({_seq_repr(self.sources)}),
annotation={self.annotation!r},
)"""
@beartype
def __eq__(self, other) -> bool:
return super().__eq__(other) and self.which == other.which
@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,
)
# ---- Properties ------------------------------------------------------------------
@property
def which(self) -> Tuple[_GetItemT, ...]:
r"""
The selector this roller applies to the sorted outcomes of its sole source.
"""
return self._which
__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 |
|
See the section on “Filtering and substitution” more examples.
Source code in dyce/r.py
class SubstitutionRoller(R):
r"""
A [roller][dyce.r.R] for applying *expansion_op* to determine when to roll new
values up to *max_depth* times for incorporation via *coalesce_mode*.
<!-- BEGIN MONKEY PATCH --
For deterministic outcomes.
>>> import random
>>> from dyce import rng
>>> rng.RNG = random.Random(1639580307)
-- END MONKEY PATCH -->
``` python
>>> 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](rollin.md#filtering-and-substitution)” more examples.
"""
__slots__: Tuple[str, ...] = ("_coalesce_mode", "_expansion_op", "_max_depth")
# ---- Initializer -----------------------------------------------------------------
@beartype
def __init__(
self,
expansion_op: _ExpansionOperatorT,
source: _SourceT,
coalesce_mode: CoalesceMode = CoalesceMode.REPLACE,
max_depth: SupportsInt = 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)
# ---- Overrides -------------------------------------------------------------------
@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},
)"""
@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
)
@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,
)
# ---- Properties ------------------------------------------------------------------
@property
def max_depth(self) -> int:
r"""
The max number of times this roller will attempt to substitute an outcome satisfying
its [``expansion_op``][dyce.r.SubstitutionRoller.expansion_op].
"""
return self._max_depth
@property
def expansion_op(self) -> _ExpansionOperatorT:
r"""
The expansion operator this roller applies to decide whether to substitute outcomes.
"""
return self._expansion_op
@property
def coalesce_mode(self) -> CoalesceMode:
r"""
The coalesce mode this roller uses to incorporate substituted outcomes.
"""
return self._coalesce_mode
__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: SupportsInt = 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: SupportsInt = 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.
Source code in dyce/r.py
class UnarySumOpRoller(NarySumOpRoller):
r"""
An [``NarySumOpRoller``][dyce.r.NarySumOpRoller] for applying a unary operator
*un_op* to the sum of all outcomes from its sole *source*.
"""
__slots__: Union[str, Iterable[str]] = ("_un_op",)
# ---- Initializer -----------------------------------------------------------------
@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
# ---- Overrides -------------------------------------------------------------------
@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},
)"""
@beartype
def __eq__(self, other) -> bool:
return super().__eq__(other) and (_callable_cmp(self.un_op, other.un_op))
# ---- Properties ------------------------------------------------------------------
@property
def un_op(self) -> _RollOutcomeUnaryOperatorT:
r"""
The operator this roller applies to its sources.
"""
return self._un_op
__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.
Source code in dyce/r.py
class ValueRoller(R):
r"""
A [roller][dyce.r.R] whose roll outcomes are derived from scalars,
[``H`` objects][dyce.h.H], [``P`` objects][dyce.p.P],
[``RollOutcome`` objects][dyce.r.RollOutcome], or even
[``Roll`` objects][dyce.r.Roll], instead of other source rollers.
"""
__slots__: Union[str, Iterable[str]] = ("_value",)
# ---- Initializer -----------------------------------------------------------------
@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
# ---- Overrides -------------------------------------------------------------------
@beartype
def __repr__(self) -> str:
return f"{type(self).__name__}(value={self.value!r}, annotation={self.annotation!r})"
@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}"
# ---- Properties ------------------------------------------------------------------
@property
def value(self) -> _ValueT:
r"""
The value to be emitted by this roller via its
[``ValueRoller.roll`` method][dyce.r.ValueRoller.roll].
"""
return self._value
__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]),
)