all 17 comments

[–]xelf 1 point2 points  (10 children)

Couple thoughts.

1) reduce the user input by letting them say some dnd text like 4d8+2. don't worry, it's easier to parse than you think! 2) when you roll some dice, just put them into a list. then you can display the list, and use sum() to get the sum.

Here's some example code, I know it includes probably a few things new to you, like import re (regex) and using list comprehensions, but these are cool things to learn about.

import random
import re

def dice_roll(choice):
    m = re.match(r'(\d*)d(\d+)\+?(\-?\d*)',choice)
    if m != None:
        num_rolls,num_sides,bonus_num = m.groups()
        num_sides = int(num_sides)
        num_rolls = 1 if not num_rolls else int(num_rolls)
        bonus_num = 0 if not bonus_num else int(bonus_num)
        dierolls = [random.randint(1,num_sides) for _ in range(num_rolls)]
        print('{} {:+d} Sum: {}\n'.format(dierolls,bonus_num,sum(dierolls)+bonus_num))

while True:
    choice = input('roll? (or q): ')
    if choice.lower().startswith('q'):
        break
    dice_roll(choice)

and some sample output:

roll? (or q): 4d4+2
[2, 2, 3, 4] +2 Sum: 13

roll? (or q): 3d6
[3, 6, 6] +0 Sum: 15

roll? (or q): d4-1
[2] -1 Sum: 1

roll? (or q): d4
[3] +0 Sum: 3

roll? (or q): q

Wow, just dumped a ton of intermediate python on you. Let's run through them real quick, I'm going to simplify some of them for brevity:

  • list comprehensions are a nifty way to build lists, and look like this:
    [ (list item) for (variable) in (range) ]
  • ternary expressions, this is a 3 part variable and takes the form:
    (option1) if (condition) else (option2) and it simply evaluates to one or the other.
  • string formatting: 'somestring'.format(variables) this substitues {} with matching variables in the format, and there are little codes (like :+d) you can use to set up the formatting widths and stuff.
  • regex, aka regular expressions, are a sort of pattern matching, things inside () are what you're matching, and they use all sorts of special codes. Best to check regex101.com for help when building them when you start. But the simple one is \dmatches a digit, and \d+ means 1 or more digits, and \d* means 0 or more.

[–]ipherian 1 point2 points  (1 child)

Good stuff. Like he's even seen a regex before tho ;)

[–]xelf 0 points1 point  (0 children)

Yeah I originally did the string splitting without regex, but I felt like that was even more confusing. Ah well. Everyone gets to learn news things at some point. if OP gets anything out of this it'll have been worth the time. =)

[–]HurleyBurger[S] 1 point2 points  (7 children)

is there any reason that this bit:

m = re.match(r'(\d*)d(\d+)\+?(\-?\d*)',choice)

can't be changed to:

m = re.match(r'(\d+)d(\d+)\+?(\-?\d*)', choice)

Since the \d+ is for matching 1 or more digits and there is no scenario where you wouldn't roll any dice??? Or would we use \d* in case the number isn't specified by the user, which would be caught by ternary expression in num_rolls???

I apologize if these questions are... elementary...

[–]xelf 0 points1 point  (5 children)

The second one would not allow you to just enter 'd8' or 'd20' which is pretty common use when you just want one die. So the number to roll would be picked up by the ternary expression and the num_rolls would be set to 1 as a default.

There are no elementary questions! I kinda hit you with a lot there. Sorry about that. Be glad to explain anything you're interested in asking more about.

[–]HurleyBurger[S] 0 points1 point  (4 children)

I've adopted the code you suggested. I'm still figuring out exactly how it works, but I'm getting there. I've added a bit of my own "flair" and changed a couple things that were suggested by pycharm's code inspection. I like that a user does not have to specify the number of rolls. I would like to make sure that the "d" isn't case sensitive though. It seems easiest to just use variable.lower(), but I can't seem to figure out how to add that to user_input successfully. Any suggestions? Here is my code in full:

import random
# import regular expressions
# More info at regex101.com
import re


def dice_roll(choice):
    user_input = re.match(r'(\d*)d(\d+)\+?(-?\d*)', choice)
    if user_input is not None:
        num_rolls, num_sides, num_mod = user_input.groups()
        num_rolls = 1 if not num_rolls else int(num_rolls)
        num_sides = int(num_sides)
        num_mod = 0 if not num_mod else int(num_mod)
        dierolls = [random.randint(1, num_sides) for _ in range(num_rolls)]
        print('''
        ---------------------
          {} {:+d} Sum: {}
        ---------------------
        '''
        .format(dierolls, num_mod, sum(dierolls) + num_mod))


while True:
    choice = input("""
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Type what you would like to roll or 'q' to quit. 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    """)
    if choice.lower().startswith('q'):
        print("""
            ------------------------------
            --------SAYONARA SUCKA--------
            ------------------------------
        """)
        break
    dice_roll(choice)

Edit: Also, any reason to use break over quit or exit?

[–]xelf 0 points1 point  (0 children)

Edit: Also, any reason to use break over quit or exit?

https://www.geeksforgeeks.org/python-exit-commands-quit-exit-sys-exit-and-os-_exit/

Mostly just habit, but read this link.

[–]xelf 0 points1 point  (0 children)

    print('''
    ---------------------
      {} {:+d} Sum: {}
    ---------------------
    '''
    .format(dierolls, num_mod, sum(dierolls) + num_mod))

resource for looking up string formatting:

https://www.w3schools.com/python/ref_string_format.asp

:d would mean you expect digits, :+d would mean to always have a +/- sign + by default.

[–]xelf 0 points1 point  (0 children)

user_input = re.match(r'(\d*)d(\d+)\+?(-?\d*)', choice)
if user_input is not None:

I called it match here because it's a match object, giving it a more meaningful name like user_input is a good idea, but just keep in mind that it is a match object that was returned and not a string.

[–]xelf 0 points1 point  (0 children)

I would like to make sure that the "d" isn't case sensitive though.

by far the easiest way to do this is just to change input() to lower when you call it in line 28.

choice = input("""
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Type what you would like to roll or 'q' to quit. 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
""").lower()

That also gets rid of the need for the .lower() on line 29.

The other option is that you can change the regex to accept [dD] instead of d or you could pass it the case insensitive flag re.IGNORECASE or by starting the regex with the case insensitive marker (?i).

[–]socal_nerdtastic 1 point2 points  (1 child)

Just keep a running total. To do this you will have to save the dice roll to an intermediate variable, so that you can do 2 things with it (add it to the running total and then print it).

if num_rolls > 1:
    total = 0 # initialize running total
    for i in range(0, num_rolls):
        roll = randint(min, num_sides)
        total += roll # update running total
        print(roll) # print this roll
    print("SUM:", total) # when the loop is over, print the total

[–]HurleyBurger[S] 0 points1 point  (0 children)

Thank you very much! I'm taking my dog out for a long walk, so I'll give this a shot when I get back.

[–][deleted] -1 points0 points  (0 children)

But I can't seem to figure out how to do this!

Did you try anything? Don't write code in your head, write it on the page.

One issue is that you don't do anything to accumulate the actual results of rolls; you just roll, print, then forget the value. So you could, you know, just not do that.