Module realm_api.rpc.roll

Dice roller RPC module

Sub-modules

realm_api.rpc.roll.parse

Parse segments from roll command arguments

Global variables

var explode_regex

Regex for exploding die extra

var keep_hilow_regex

Regex for keep highest/lowest extras

Functions

async def roll_handler(formula: str) ‑> realm_schema.dice_rolls.BatchResults
Expand source code
async def roll_handler(formula: str) -> BatchResults:
    """Roll them bones."""

    results = BatchResults(results=[])

    try:
        batches = parse_segments(formula)

        for segments in batches:
            results.results.append(
                RollResults(results=[roll_segment(s) for s in segments])
            )
    except Exception as ex:
        logger.warning(ex)
        return BatchResults(results=[])

    return results

Roll them bones.

def roll_segment(segment: realm_schema.dice_rolls.RollSegment) ‑> realm_schema.dice_rolls.SegmentResult
Expand source code
def roll_segment(segment: RollSegment) -> SegmentResult:
    """Calculate the results of an individual RollSegment instance."""

    if isinstance(segment, ConstantModifier):
        return SegmentResult(
            segment=segment,
            total=segment.number * (-1 if segment.negative else 1),
        )

    if isinstance(segment, DiceRoll):
        limit = 0
        exploding = False
        result = SegmentResult(segment=segment, rolls=[], work="")
        assert result.rolls is not None
        assert result.work is not None

        if segment.extra and segment.extra[0] == "!":
            exploding = True

            # explosion limit
            if len(segment.extra) > 1:
                explode_limit_match = explode_regex.match(segment.extra)
                assert explode_limit_match
                limit = int(explode_limit_match.groupdict()["num"])

        # roll dice
        for _ in range(segment.dice):
            roll = randint(1, segment.faces)
            exploded = 0

            # exploding die
            while (
                exploding
                and roll == segment.faces
                and (limit == 0 or exploded < limit)
            ):
                exploded += 1
                result.rolls.append(roll)
                result.total += roll
                roll = randint(1, segment.faces)

            result.rolls.append(roll)
            result.total += roll

        # keep methods
        if segment.extra and segment.extra.startswith("k"):
            ord_rolls = sorted(result.rolls)
            args_match = keep_hilow_regex.match(segment.extra)
            assert args_match
            args = args_match.groupdict()
            fun = args["fun"]
            keep = int(args["num"]) if args["num"] else 1

            assert keep < segment.dice and keep > 0
            drop = range(segment.dice - keep)

            # keep highest
            if fun == "kh":
                for i in drop:
                    result.total -= ord_rolls[i]

            # keep lowest
            elif fun == "kl":
                for i in drop:
                    result.total -= ord_rolls[-(i + 1)]

        rolls = [
            f"**{roll}**" if roll == segment.faces else f"{roll}"
            for roll in result.rolls
        ]
        result.work = "".join(
            [
                f"[{', '.join(rolls)}] = ",
                f"**{'-' if segment.negative else ''}{result.total}**",
            ]
        )

        if segment.negative:
            result.total *= -1

        return result

    raise NotImplementedError()

Calculate the results of an individual RollSegment instance.