1

I am building an RPG style text based game in Python and I am stuck on a part. I am trying to ask for user input and use that in the code to select the players character and race. I have all the races as a class and the classes as a class.

Here is an example:

class Human:
    hp = 50
    dg = 10
    ar = .1
    cs = .05
    ak = 5
    ev = 0.1

class Elf:
    hp = 20
    dg = 20
    ar = .1
    cs = .2
    ak = 15
    ev = 0.15

def race_sel():
    y = eval(input("Select Race: Human, Elf").capitalize())
    return y

x = race_sel()

class Mage(x):
    hp = 80 + x.hp
    dg = 20 + x.dg
    ar = .15 + x.ar
    cs = .1 + x.cs
    ak = 40 + x.ak
    ev = .15 + x.ev

class Warrior(x):
    hp = 100 + x.hp
    dg = 20 + x.dg
    ar = .2 + x.ar
    cs = .1 + x.cs
    ak = 30 + x.ak
    ev = .15 + x.ev

def char_sel():
    y = eval(input("Select Class: Mage, Warrior").capitalize())
    return y

q = char_sel()

This semi works. In the console it asks the "String" then if I type, human or elf it .capitalize()s the input and sets x to the class selected. Then x is plugged into one of the classes, either Mage(x) or Warrior(x). Then it adds x.whatever to the class that's selected.

Then the char_sel(): function selects what class the character is playing.

The issue I have is I wanted to use an if else statement in the functions: race_sel() and class_sel() where if the user input is not "human" or "elf" then it prints "Invalid selection, try again", but it seems with eval() it automatically checks your input with all the Python classes you have in your code. Because eval() changes it away from a str type.

Could I take a string input in then convert it to the eval() type? I really am not very familiar with the eval() method. It was just something I found so I could call my classes with user console input.

I am sure there is an easier way to do most of what I am writing.

Jongware
  • 22,200
  • 8
  • 54
  • 100
Eric Christensen
  • 177
  • 1
  • 16

2 Answers2

1

Using eval is generally bad practice. It's better to do this (it also solves your question):

def race_sel():
    while True:
        # the .lower() allows user to input hUmAn or something and it will still work
        type = input('Select Race: Human, Elf ').lower()
        if type == 'human':
            return Human
        elif type == 'elf':
            return Elf
        else:
            print('Invalid selection, try again')

Use a similar process for char_sel.

By the way, there is no such thing as an eval type. eval returns a typical Python object if successful (the type depends on the contents of the argument of eval).

iz_
  • 15,923
  • 3
  • 25
  • 40
1

I understand the appeal of using introspection to avoid repeating yourself. Don't use eval(), just check to see if the user's entry is in globals().

def race_sel():
    prompt = "Select race: Human, Elf "
    race = input(prompt).capitalize()
    while race not in globals():
        print(race, "is not a valid race. Try again.")
        race = input(prompt).capitalize()
    return globals()[race]

For further not-repeating-yourself, derive all your classes from a base class, then use isinstance() to make sure the user has actually entered a valid race. (Or you could put all your races inside a Race class, using the outer class simply as a namespace.) You can also dynamically generate your prompt using introspection, so defining a new race will automatically update the menus.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • First off thank you for your' answer! I really like the idea of being able to add more classes and the prompt dynamically updates with it. I also like the idea of using a class as a name space like: class Races: class Human: ev = 1 etc... I tried using isinstance() but I can't seem to figure it out fully. I am trying to check is "human" is in the class "races" then if true it returns it. if not prints "invaild" – Eric Christensen Dec 02 '18 at 16:56