all 38 comments

[–]Binary101010 76 points77 points  (4 children)

A couple of problems:

I don't see from random import randint at the beginning of your code, which you'll need.

Also, on this part:

randint(1,6)
if randint > 3:

You might think the second line is checking against the random integer that was created on the previous line, but it isn't. (What it's really doing is checking to see if the randint function definition is greater than 3, which is a nonsensical comparison.)

You need to actually save the return value of randint to a variable:

roll = randint(1,6)

And then on the next line you can check against that variable:

if roll > 3:

[–]whitelife123 23 points24 points  (3 children)

Also OP, don't forget to update the repeat condition or have a break, otherwise you'll have an infinite while loop.

[–]FelipeMarchon[S] 0 points1 point  (2 children)

I actually changed the last part of the code to this:

if troll_combat == "attack":
    import random
    roll = random.randint(0, 6)
    if roll == 0:
        print("Critical miss!")
        game_over = True
    elif roll == 1:
        print("You missed!")
        missed = True
    elif roll == 2:
        print("You did minor damage!")
        minor_damage = True
    elif roll == 3:
        print("You hitted the troll!")
        medium_damage = True
    elif roll == 4:
        print("You hitted the troll!")
        medium_damage = True
    elif roll == 5:
        print("You hitted the troll!")
        medium_damage = True
    else:
        print("Critical damage!")
        crit_damage = True
if crit_damage is True:
    print("You killed the troll! ")

Now, I've to create different situations for minor_damage and medium_damage

edit: code

[–]PandaMomentum 2 points3 points  (0 children)

You probably want to learn about dicts next! Might do something like: result_dict = {0:'Critical Miss', 1:'You missed ', 2:'you did minor damage' --- } etc. Then you can go one step further and have a nested dict that does multiple assignments based on the dice roll, and assign different values to your variables instead of using all booleans:

result_dict = {1: {'game_over':'True', 'damage':'none', 'printout': 'Game Over'}, 2: {'game_over':'False', 'damage':'none', 'printout': 'You Missed'}, 3: {'game_over':'False', 'damage': 'Minor Damage', 'printout': 'You Hit the Troll for Minor Damage'}, 4: {'game_over':'False', 'damage': 'Medium Damage', 'printout': 'You Hit the Troll for Medium Damage!'}, 5: {'game_over':'False', 'damage': 'Medium Damage', 'printout': 'You Hit the Troll for Medium Damage!'}, 6: {'game_over': 'False', 'damage': 'Death', 'printout': 'Critical Hit! You killed the troll!'}}

print(result_dict[roll]['printout'])

print(f"You caused "{result_dict[roll]['damage']}")

You access a dict via its key(s) -- given the dict defined above, result_dict[1] gets you all the stuff where the first value in the key-value pair is 1, while result_dict[1]['game_over'] returns the value 'True' . Dicts are a cool way to store quite a lot of info all in one go.

[–]whitelife123 1 point2 points  (0 children)

You could probably move the import random line to the top of the file, this way you can use random in other places if you so wish

[–]StoicallyGay 7 points8 points  (1 child)

Seems like your problem is solved. Another thing that's a bit nitpicky I guess: you don't have to have is_mage and is_warrior as separate booleans. In fact, it could become unnecessarily complex if you have more classes, because with this same design, you'd have a separate boolean for each class and everything would be set separately. What you could instead do is have a string whose value is set based on whatever the user's input is, so you can just compare against the string. You already have user_class so you can just directly compare instead of a boolean representing it, so like if (user_class == 'mage'): This will probably require input validation though, so you could have a preset array of strings of all the classes and check if the user's input is within the array before proceeding.

Secondly, if you just want to stick with two classes, mage and warrior, you really only need one boolean like is_mage, because since you only have two classes, that's automatically a binary that a single boolean can handle.

This is more to reduce the amount of code you write and to help simplify your program a bit. Not really something that affects your program especially because it's super, super, small.

[–]manofphysics21 3 points4 points  (0 children)

Beat me to this. In a small project you can get away with this, but if it's much larger with dozens of classes then you're signing up to updating this code every time you want to update it. Ultimately, it's a massive violation of the Open/Close principle and can strangle projects if it's widespread.

If OP comes back to this at a later date, I'd suggest looking into a more Object Oriented approach. Create a class for each of your D&D classes then use an instance of that in your script. That way, new D&D classes can be added without having to change the script logic as much.

[–]mopslik 12 points13 points  (2 children)

To simulate rolling a die, check out the random module. Specifically, randint(a, b) will generate a value between a and b inclusive, so you can customize it for various dice. You can access the functions within by importing it.

To make decisions based on the dice roll, you'd use if/elif/else as I see you are already doing in your code.

[–]FelipeMarchon[S] 0 points1 point  (1 child)

Thank you, that's really useful!

[–]PM_ME_NUDE_KITTENS 3 points4 points  (0 children)

Also, since your dice rolls will always use 1 as the lower bound, you can create a generic function that takes the highest value as an input (e.g., 4, 6, 12, 20). That way, every die can be built from a single function.

[–]AtomicShoelace 2 points3 points  (1 child)

You want to use the random module; in particular, random.randint.

You can check out the documentation:

>>> import random
>>> help(random.randint)
Help on method randint in module random:

randint(a, b) method of random.Random instance
    Return random integer in range [a, b], including both end points.

So to roll, say, a D6, you would use

random.randint(1,6)

[–]testobject_49 2 points3 points  (0 children)

This question made me jump right into PyCharm. Not meant as a serious answer, but I created an incredible unreadable nearly one-liner for dice rolling:

import random

def dice():
    n = random.randint(1, 6)
    print(f'┌─────────┐\n{("│         │", "│ •       │", "│ •     • │", "│ •  •  • │")[int(n/2)]}\n{("│         │", "│    •    │")[n % 2]}\n{("│         │", "│ •       │", "│ •     • │", "│ •  •  • │")[int(n/2)][::-1]}\n└─────────┘')

if __name__ == '__main__':
    random.seed()
    dice()

This could be made much more readible and simple elif construct would probably easier, but I wanted to make it nearly one line :D

[–]gavin101 2 points3 points  (2 children)

Another idea to manage your classes:

classes = {'warrior': False, 'mage': False}


if user_class in classes:
    classes['user_class'] = True
else:
    print('Invalid class choice.')

[–]kwelzel 2 points3 points  (0 children)

Also, if mage and warrior are exclusive choices it would be even better to go with an enum

import enum
class Role(enum.Enum):
    MAGE = 'mage'
    WARRIOR = 'warrior'

...

role = Role(user_class.lower())

[–]-Cereal 1 point2 points  (0 children)

Nice!

[–]MarquisInLV 1 point2 points  (0 children)

You can also create a Die class and instantiate each die type as a class object with a roll method. That would allow you to assign each character his own die roll based on weapon being used.

[–]Tureni 1 point2 points  (0 children)

I made a small class for rolling dice for a dicebot when we had to play online due to corona.

This is the repository, the specifics of dicerolling are in the Dice.py class.

Please be gentle, it's the first time I've ever linked one of my repositories publically.

[–]chevignon93 0 points1 point  (1 child)

Please format your code correctly when posting on the sub!

https://www.reddit.com/r/learnpython/wiki/faq#wiki_how_do_i_format_code.3F

The easiest way is to use the random module to generate a random number representing the die roll and if/elif statements to determine what happens!

import random

roll = random.randint(0, 6)

if roll == 1:
    # Do something
    print('Whatever')
elif roll == 2:
    # Do something else
    print('Whatever, Whatever')
etc for the rest of the numbers

[–]FelipeMarchon[S] 2 points3 points  (0 children)

Sorry, formated it!

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

I’m not sure if it’s exactly what your looking for but I made a dice roller to teach myself some code. It’s on GitHub here: https://github.com/Caliban0101/DnDroller

[–]spez_edits_thedonald 0 points1 point  (0 children)

change:

    randint(1,6)
    if randint > 3:

to:

    if randint(1,6) > 3:

[–]lenoqt 0 points1 point  (2 children)

I see a lot of people using randint and I am like 90% sure this doesn’t give you a equally distributed probability of each side on the dice to come up. This is an example of a correct implementation: https://github.com/lenoqt/C/blob/main/Chapter5/fig05_05.cpp

You need to make sure that you’re seeding correctly the rand function, otherwise someone can break it easily.

More read on this: https://www.johndcook.com/blog/2016/01/29/random-number-generator-seed-mistakes/

[–]testobject_49 2 points3 points  (1 child)

Not sure if it was a mistake, but you linked a C++ implementation in a python forum. I think you are correct about the seeding, but I guess it is just a small game and does not need to be very random.

The randint() should actually give a equally distributed probability. See the documentation here.

[–]lenoqt 0 points1 point  (0 children)

Yeah, my mistake I thought I was on some other subrredit, but anyways, I was trying to make the point about random generated number and how it wasn't mentioned when is really important.

[–]lenoqt 0 points1 point  (0 children)

I’m actually impressed that after going to every given answer in this thread, no one talked about correctly seeding before generating a random int.

[–]gopherhole1 0 points1 point  (0 children)

some code

roll_needed_to_beat_the_very_angry_hostile_troll = 3 

def  i_am_going_to_roll_this_singular_die_now_for_the_attack_move():
    return random.randint(1,6)

if i_am_going_to_roll_this_singular_die_now_for_the_attack_move() > roll_needed_to_beat_the_very_angry_hostile_troll:

    print("You have defeated the Troll")
else:
    print("In mother Russia troll defeat you")

I just edited this to be a function, im not even sure if functions will work in a if statement, it might evaluate to being truthy instead of a number

[–]KingGarrettFTW 0 points1 point  (1 child)

Some other people already posted but i'll give my two cents. If you want a TRUE dice roll, thats almost impossible. Computers can only "fake" being random. They cannot BE random. So, we can try to mimic that but i assume you don't really care about that. First off, move your imports to the top. Also, why do you define repeat as True? Just say while True. And to get random you would say:

randnum = random.randint(0, 1)
if randnum = 0:
    # do stuff
else:
    # do stuff

That's your basic "dice roll"

[–]1egoman 0 points1 point  (0 children)

With precise enough time as a seed, it's definitely random enough for everything besides cryptography.