Search code examples
pythonpython-3.x

Rolling Stats for a DND Character in Python


I've been trying to teach myself a little bit about coding and thought a cool project would be to make a DND 5E Character Generator in Python 3.5 for personal use. I've built a simple one so far that'll give you a Class, Gender, Race, and Background. But I really want to flesh it out a ton more over time. Eventually I'd like to have it do everything involved in character creation and type it into a PDF character sheet. But I'm not nearly that far yet. I've managed to hack together something resembling real code that works thus far, now I want to add in Stat Rolling and that's where I need help 'cause I'm flying by the seat of my pants and don't really know what I'm doing.

So the way I like to roll stats for characters is to roll 4d6 and drop the lowest die, then add the three remaining numbers together to get a stat number. Then I do that 7 times and drop the lowest number to have a total of 6 stat numbers available to assign to the different stats. I'm not sure how to drop the lowest number and add the remainders together, also not 100% sure how to handle assigning the stats.

For now I'm thinking they should probably be assigned automatically at first and then later on I could code in the option to change stuff at the end after presenting the user with a "finished" character. Likewise if I make an automatic version first I could go back later and add the option to do an automatic or guided character creation, and make a guided version that prompts the user to choose each setting as they go along. Anyways, for now I just really need help with how to add the stats up and assign them correctly so any advice you'd have to offer would be greatly appreciated! Here's my code so far:

import random

gender='female'
race='dragonborn'
klass='barbarian'
background='acolyte'

genders = [
  'Female',
  'God Damned',
  'Male'
]

races = [
  'Dragonborn',
  'Dwarf',
  'Elf',
  'Gnome',
  'Half-Elf',
  'Half-Orc',
  'Halfling',
  'Human',
  'Tiefling'
]

klasses = [
  'Barbarian',
  'Bard',
  'Cleric',
  'Druid',
  'Fighter',
  'Monk',
  'Paladin',
  'Ranger',
  'Rogue',
  'Sorcerer',
  'Warlock',
  'Wizard'
]

backgrounds = [
  'Acolyte',
  'Charlatan',
  'Criminal',
  'Entertainer',
  'Folk Hero',
  'Gladiator',
  'Guild Artisan',
  'Guild Merchant',
  'Hermit',
  'Knight',
  'Noble',
  'Outlander',
  'Pirate',
  'Sage',
  'Sailor',
  'Soldier',
  'Urchin'
]

# Here I want to roll for 6 stats, for each stat I want to roll 4d6, and     
# then add the three highest numbers together. Or, add all four together, 
# and then subtract the lowest number.
#
# Although I personally like to roll for 7 stats like this and then ignore 
# the lowest stat. I'd like to figure out how to do it both ways eventually.
#
# As I understand it individual numbers won't repeat in a set, so that may 
# be useful. However if I were to make a set of four random integers between 
# 1 and 6 and there were no repeats between them, adding them all together 
# would then give me too high of a number. So I still need to be able to 
# identify the lowest number afaik.
#
#
#    stats = [
#      'Strength',
#      'Dexterity',
#      'Constitution',
#      'Intelligence',
#      'Wisdom',
#      'Charisma'
#    ]
#    
#      strength = [
#      my_set = {random.randrange(0,6), random.randrange(0,6), random.randrange(0,6), random.randrange(0,6)}
#    ]
#
# ???
#

def displayDialog():
  cont = True
  while cont:
    userInput = input("Type the command you want or help").lower()
    if(userInput == 'help'):
      printCommands()
    else:
      roll(userInput)

def roll(stat):
  global gender
  global race
  global klass
  global background
  statLow = stat.lower()
  if(statLow== 'gender'):
    gender = genders[random.randint(0, len(genders)-1)]
  if(statLow== 'race'):
    race = races[random.randint(0, len(races)-1)]
  if(statLow== 'klass'):
    klass = klasses[random.randint(0, len(klasses)-1)]
  if(statLow== 'background'):
    background = backgrounds[random.randint(0, len(backgrounds)-1)]
  if(statLow== 'random'):
    gender = genders[random.randint(0, len(genders)-1)]
    race = races[random.randint(0, len(races)-1)]
    klass = klasses[random.randint(0, len(klasses)-1)]
    background = backgrounds[random.randint(0, len(backgrounds)-1)]

  print('You are a ' + gender + ' ' + 
                       race + ' ' +
                       klass + ' ' + 'with a background as a ' +
                       background + '!')

def printCommands():
  print('---Commands---\nGender \nRace  \nKlass \nBackground \nRandom')

displayDialog()

Solution

  • A set is not the right choice here; you do need all of the dice.

    A function like this should do:

    def roll_dice_discarding_lowest(n_dice, dice_rank):
        results = [  # Generate n_dice numbers between [1, dice_rank]
            random.randint(1, dice_rank)
            for n
            in range(n_dice)
        ]
        lowest = min(results)  # Find the lowest roll among the results
        results.remove(lowest)  # Remove the first instance of that lowest roll
        return sum(results)  # Return the sum of the remaining results.
    

    To roll 5d6 discarding the lowest result, call roll_dice_discarding_lowest(5, 6).