Ghostortoast
Ghostortoast

Reputation: 17

Adding or Subtracting a number from a randomly generated number

I'm trying to implement a feature in my program that the user would be able to add or subtract a randomly generated number using a specific sided die. The foundation of my code is posted here:

import discord
import random

DND_1d20 = range(1, 21)

# Roll d20
    if message.content == ";roll 1d20":
        response = random.choice(DND_1d20)
        response_str = "You rolled {0}".format(response)
        if response_str == "You rolled 20":
            await message.channel.send("**Critical Hit!**\n You rolled 20")
        if response_str == "You rolled 1":
            await message.channel.send("**Critical Fail!**\n You rolled 1")

I would like the user to be able to specify a dice roll ";1d20" BUT also have the ability to add ";1d20+(x)" or subtract ";1d20-(x)" any number (x) from the generated dice roll. The logic would look something like this

-user ";1d20+2" Lets say the random number generated would be 6. Since the user wants to add 2 to the random number we generated, the outcome would be 8.

-bot "You rolled 8"

# Roll d20
    if message.content == ";roll 1d20":
        response = random.choice(DND_1d20)
        response_str = "You rolled {0}".format(response)
        if response_str == "You rolled 20":
            await message.channel.send("**Critical Hit!**\n You rolled 20")
        if response_str == "You rolled 1":
            await message.channel.send("**Critical Fail!**\n You rolled 1")
        else:
            if message.content == "-":

How would I go about doing this? I am really confused on where to start. I dont think the code above is right, because the message would have to exactly be a "-". Also, how would I incorporate the value (x), since it could be a vast array of numbers, or the +/- signs from the user input?

Any help is appreciated!

Upvotes: 0

Views: 538

Answers (1)

Patrick Haugh
Patrick Haugh

Reputation: 61032

Here's a more advanced solution using a library called lark to define a grammar for these dice expressions (cribbed from this question), parse those expressions into syntax trees, then evaluate those trees. Make a file named dice_grammar.py with this string:

grammar="""
start: _expr

_expr: add
    | subtract
    | roll
    | NUMBER
add: _expr "+" _expr
subtract: _expr "-" _expr
roll: [NUMBER] ("d"|"D") (NUMBER|PERCENT)

NUMBER: ("0".."9")+
PERCENT: "%"

%ignore " "
"""

If you're not familiar with grammars like this, don't panic. All this is showing is that we can roll dice, add, and subtract. We can then have a dice_transformer.py to consume the trees the parser will produce:

from lark import Transformer, v_args
from random import randint

class DiceTransformer(Transformer):
    PERCENT = lambda self, percent: 100
    NUMBER = int
    def __init__(self):
            super().__init__(visit_tokens=True)
    @v_args(inline=True)
    def start(self, expr):
            return expr
    @v_args(inline=True)
    def add(self, left, right):
            return left + right
    @v_args(inline=True)
    def subtract(self, left, right):
            return left - right
    @v_args(inline=True)
    def roll(self, qty, size):
            qty = qty or 1
            return sum(randint(1, size) for _ in range(qty))

and a dice_bot.py that uses these to evaluate dice expressions from the user:

from discord.ext import commands
from lark import Lark
from lark.exceptions import LarkError
from dice_grammar import grammar
from dice_transformer import DiceTransformer

bot = commands.Bot(";")

parser = Lark(grammar, maybe_placeholders=True)
transformer = DiceTransformer()

@bot.command()
async def roll(ctx, *, expression):
    try:
        tree = parser.parse(expression)
    except LarkError:
        await ctx.send("Bad Expression")
        return
    print(tree.pretty()) # Log the roll
    result = transformer.transform(tree)
    await ctx.send(f"You rolled: {result}")

bot.run("token")

This allows us to ask for the computation of more complicated rolls like

;roll 2d6 +7 + d% - 3d4

On the advice of Erez in the comments, I changed my answer to use lark.Transformer. You should be able to see my original code in the edit history of this answer.

Upvotes: 1

Related Questions