Search code examples
pythondice

Python dice throw


So for a private project I want to digitalize the throw of several different dice. So far my code asks for an input, how many and what kind of dice are thrown, and outputs the result. So far, I have the following:

import dice

die = input("Roll the dice: ")

if die == "d4":
    dice.d4()
elif die == "d6":
    dice.d6()

whereas the dice-module contains

import random

def d4():
    n = int(input())
    x = 0
    d4=[1, 2, 3, 4]
    while x < n: 
        print(random.choice(d4))
        x = x + 1

def d6():
    n = int(input())
    x = 0
    d6=[1, 2, 3, 4, 5, 6]
    while x < n: 
        print(random.choice(d6))
        x = x + 1

and so on. The if-elif check to determine the type of dice seems to be functional, but it feels somewhat clunky. Now to my question: Is there a more elegant way to check for the number and type of dice? For example that I can input 3d6 and the script calculates 3 times a d6-throw?


Solution

  • Is there a more elegant way to check for the number and type of dice?

    You could use a regex to parse (\d+)d(\d+) in order to extract X and Y from XdY. Or even just split on "d" since the input format is strictly <numbers>d<numbers> meaning the split is not ambiguous.

    From this, you can use getattr to get the corresponding method from the dice module, although in reality the dice module is not really useful: dX is just random.randint(1, X). So you can just have something along the lines of:

    def roll(spec):
        rolls, dice = map(int, spec.split('d'))
        return [
            random.randint(1, dice)
            for _ in range(rolls)
        ]
    

    And not have any limits as to the "shape" of the dice you want to support, this is fine with any faces count from 1 to essentially infinity.

    Incidentally this can be further modified through the use of random.choices and an explicit range. Whether you find it simpler or not is a matter of taste:

    def roll(spec):
        rolls, dice = map(int, spec.split('d'))
        return random.choices(range(1, dice+1), k=rolls)
    

    This has less broad compatibility (choices was added in python 3.6) but can fairly easily be extended to support e.g. weighted dice, or special dices where some faces are duplicated (though obviously the latter can be supported by just reassigning values from a regular dice).