I have posted before, and the code was very bulky and ugly. Here is the new and improved version.

#Dennis Gordick
#Brandon McCurry

#file system fully works, if you don't understand how to use it ask Brandon
Task list:
allow player to leave game
create different types of monsters
improve shop inventory
gain skill points every level to improve yourself
add multiple save files
monster creator file-(Goal is to make a program that creates new monsters via user input, and is added to Enemies.py
import random
import time
import pickle, shelve
from Enemies import Player, Monster, Shop_Keeper, Monster_Boss

class main():
    def __init__(self):
        response=self.valid_input("New game or Load game?",["load","new"])
        if response == "load":
                f = shelve.open("save.dat")
                attribute = f["attributes"]
                person = attribute[0]
                print("Save file is corrupt or doesn't exist")
        if response=="new":
            Name=input("What is your name?")
            Race=self.valid_input("What is your race? (Human, Elf, Dwarf) ", ["human","elf","dwarf"])
            #Race=input("What is your race? (Your choices are Human, Elf, and Dwarf.)")
            Class=self.valid_input("What is your class? (Warrior, Archer, Mage) ", ["warrior","archer","mage"])
            #Class=input("What is your class? (Your choices are Warrior, Archer, and Mage.")
            person = Player(Name, Race, Class)

        while person.health > 0:
            explore=self.valid_input("Do you want to explore, go to town, look at some info, or save? ", ["explore","town","save","info"])
            #explore=input("Do you want to explore or go to town or look at some stats/info or even save? (only say explore or town or info or save)")
            if explore=="explore":
                lvl=self.valid_int("What level monsters?")
                print("You explore")
                while turns < 100 and int(person.health) > 0:

                    #normal fight
                    if int(encounter) >=70:
                        enemy = Monster(lvl,person)
                        print("You encounterd a LVL: "+str(enemy.lvl)+" Monster!")
                        self.fight(person, enemy)
                        person.kills += 1

                    elif int(encounter)<70:
                        loot = random.randint(1,100)
                        trap = random.randint(1,100)

                        if int(loot)>=60:
                            print("You found "+str(lvl)+" gold")
                            print("You have a total of "+str(person.gold)+" gold")
                        elif int(loot)<=10:
                            if int(trap)>=50:
                                print("You step on a trap")
                                print("You lost ten health")
                                print("Your total health is "+str(person.health))

                    if int(turns)== 100:
                        #Boss fight
                        if int(boss)>5:
                            print("Boss Fight!")
                            run=self.valid_input("Do you fight or run?", ["fight","run"])
                            if run.lower() == "fight":
                                satan = Monster_Boss(lvl,person)
                                self.fight(person, satan)
                                person.boss_kills += 1

                    print("End of turn "+ str(turns)+"\n")

            elif explore=="info":
                 print("Total kills",str(person.kills))
                 print("Total boss kills",str(person.boss_kills))
                 print("Total gold",str(person.gold))
                 print("Total potions",str(person.potions))
                 print("Total xp", str(person.xp))

            elif explore=="save":
                f = shelve.open("save.dat")
                attributes = [person]
                f["attributes"] = attributes

            #Going to town (giggity)
            while explore=="town":
                town=self.valid_input("Where do you want to go? (shop, inspector, blacksmith, tavern, leave)", ["shop","inspector","blacksmith","tavern","leave"])

                if town=="shop":
                    print("Your gold "+str(person.gold))
                    print("The shopkeep says 'We only have potions of health! They are 20 gold each!'")
                    bought=input("How many do you want?")
                    if bought.isdigit():
                        if int(person.gold)>=int(cost):
                            print("Gold left "+str(person.gold))
                            print("Total potions "+str(person.potions))
                            print("'Your to poor! Come back with some gold fool!'\nThe shopkeeper kicks you out.")
                    elif bought.strip("-").isdigit():
                        print("Aye. Trying to pull a fast one on me are ya?")
                        keep = Shop_Keeper(person, bought)
                        self.fight(person, keep)
                        if person.health > 0:
                            person.potions += int(bought)*-1
                elif town=="inspector":
                    print("Coming soon")

                elif town=="blacksmith":
                    print("Coming soon")

                elif town=="leave":
                    explore = "leave"

                while town=="tavern":
                    print("Hello traveler, what can I do for you? A drink? Or the latest rumore?")
                    bar_keep=self.valid_input("Whats your choice? (drink, rumore, or leave)", ["drink","rumore","leave"])

                    if bar_keep=="drink":
                        print("Drinks cost one gold.")
                        drink=self.valid_input("Do you want a drink? ",["yes","no"])

                        while drink=="yes" and person.gold > 0:
                            print("Your gold: "+str(person.gold))
                            print("You get drunk out of your mind.")
                            drink = input("Another? ")

                    if bar_keep=="rumore":
                        if int(person.lvl) < 20:
                            print("I don't know anything.")
                            print("No, rumors at the moment.")

                    if bar_keep=="leave":
                        town = ""
    def fight(self, person, enemy):
        while int(enemy.health) > 0 and person.health > 0:
            print("Your Health: "+str(person.health))
            print(enemy.name + " Health: "+str(enemy.health))
            attack=input("Do you attack or use a potion? ")
            #your turn
            if attack == "attack":
                if int(hit)<=75:
                    print("\nYou did "+ str(dmg)+" damage")
                    print("You missed!")
            elif attack=="potion":
                if person.potions > 0:
                    print("Potions left... "+str(person.potions))
                    print("You have no potions... You just waisted your turn!")
                print("You sit there and take it")
            #enemies turn
            if int(enemy.health) > 0:
                if enemy.potions > 0 and enemy.health < person.dmg_min:
                    enemy.health += enemy.max_health//4
                    enemy.potions -= 1
                    print("The " + str(enemy.name) + " drank a potion")
                    if int(monster_hit_chance)<=60:
                        print("The " + str(enemy.name) + " did "+ str(enemy.dmg)+" damage")
                        if person.health <= 0:
                            print("You died")
                        print("The " + str(enemy.name) + " missed!")

            #loot and xp for normal monster
                print("\nThe " + str(enemy.name) + " died\n")
                print("XP gained: "+ str(enemy.xp))
                print("Your XP: "+ str(person.xp))
                if int(loot_chance) <10:
                    print("No loot :(")
                    print("Your gold "+str(person.gold))
                elif int(loot_chance) <90:
                    print("Your gold sir. It this many..." +str(enemy.gold))
                    if enemy.loot != "":
                        print("There was also a " + str(enemy.loot) + " on him.")
                        line = "Would you like to replace it with your " + str(person.weapon) + "? "
                        opt = self.valid_input(line, ["yes","no"])
                        if opt.lower() == "yes":
                            person.weapon = enemy.loot
                            print("Congradulations! Your new weapon is a " + str(person.weapon))
                    print("Your gold "+str(person.gold))
                    print("Rare loot! 1 potion!")
                    print("\nYour total potions "+str(person.potions))

    def valid_input(self, question, valid):
        response = input(question)
        while response.lower() not in valid:
            print("Valid responses: ")
            for i in valid:
            response = input(question)
        return response

    def valid_int(self, question):
        response = input(question)
        while not response.isdigit():
            print("That is not a number")
            response = input(question)
        return int(response)


There are some new files as well for the enemies.

import random
class Player():
    def __init__(self, name, race, Class):
        self.name = name
        self.race = race
        self.Class = Class
        self.potions = 0
        self.gold = 0
        self.lvl = 1
        self.xp = 0
        self.extra_health = self.lvl*10
        self.health = 90+self.extra_health
        self.max_health = self.health
        self.dmg_min = self.lvl
        self.dmg_max = self.lvl*6
        self.kills = 0
        self.boss_kills = 0
        if self.Class=="Warrior":
            self.weapon = "Sword"
        elif self.Class=="Archer":
        print("A " + self.weapon + " is your weapon.")
        print("The "+str(self.weapon)+" weilding "+ str(self.Class)+" of the "+ str(self.race)+" clan, went out on an adventure. There name was "+str(self.name)) 

    def dmg(self):
        return random.randint(self.dmg_min, self.dmg_max)

    def update(self):
        lvl_xp = 90 + int(self.extra_health)
        while int(self.xp) >= int(lvl_xp):
            print("Level Up! " + str(self.lvl))
            self.dmg_min = self.lvl
            self.dmg_max = self.lvl*6
            self.extra_health = self.lvl*10
            self.health = 90+self.extra_health
            self.max_health = self.health
            lvl_xp = 90 + int(self.extra_health)

class Monster():
    def __init__(self, lvl, player):
        self.lvl = int(lvl)
        self.dmg = int(lvl)
        self.xp = int(lvl)//int(player.lvl)*2
        self.gold = random.randint(int(lvl)//2,int(lvl)*2)
        self.name = "Monster"
        self.health = lvl*2
        self.max_health = self.health
        self.heal = lvl*10
        self.potions = 0
        lootable = random.randint(1,100)
        if lootable > 90:
            self.loot = random.choice(["Sword","Dagger","Staff"])
            self.loot = ""
class Shop_Keeper():
    def __init__(self, player, potions):
        potions = int(potions)*-1
        self.lvl = potions*10
        self.dmg = potions*10
        self.xp = self.lvl//int(player.lvl)*2
        self.gold = potions*10+int(player.lvl)*2
        self.name = "Shop Keeper"
        self.health = self.lvl*2
        self.max_health = self.health
        self.loot = "Dagger"
        self.potions = potions

class Monster_Boss():
    def __init__(self, lvl, player):
        self.lvl = lvl
        self.dmg = lvl*3
        self.xp = lvl//player.lvl*6
        self.gold = lvl*100
        self.name = "Boss"
        self.health = player.health
        self.max_health = self.health
        self.loot = random.choice(["Battle Axe", "Club"])
        self.potions = 0

And the save file did not really change as well:

import shelve

#opens file
f = shelve.open("save.dat")
gold = 2
potions = "3"
#sets all variables as a dictionary
f["attributes"] = {"gold": gold, "potions": potions}
#f.sync() adds all any f["whatever"] to the file
#always close after use!!!

#reopen file later to read the contents
f = shelve.open("save.dat")
#save the variables still in dictionary form to a new variable
attributes = f["attributes"]
#always close after use!!!
#access each variable individually, and save them to a new variable to match the rest of your code.
gold = attributes["gold"]
  • \$\begingroup\$ @Josay Hey... Could you review the new code? It is based off your recomendations \$\endgroup\$ Dec 3, 2014 at 13:51
  • 1
    \$\begingroup\$ Your code doesn't work, what are the random words before class main supposed to do? \$\endgroup\$
    – jonrsharpe
    Dec 3, 2014 at 13:52
  • \$\begingroup\$ @jonrsharpe I'm sorry... They are in the works... They can be deleted... They where not supposed to be posted here.. :) I'll fix it in the post. They were the only thing added \$\endgroup\$ Dec 3, 2014 at 13:56

1 Answer 1


Basic style/layout stuff:

  • Python has a style guide - e.g. Shop_Keeper should be ShopKeeper, Player.Class should be Player.class_, whitespace (blank lines, etc.) should be reviewed.
  • It would be good to see some docstrings for your classes and methods. I like the Google style, but others are available.

You have an opportunity to do some real OOP and reduce duplication, here; all of your characters have e.g. lvl, xp, gold, so why not abstract out to:

class Character(object):

    def __init__(self, lvl, xp, gold, ...):
        self.xp = xp
        self.lvl = lvl
        self.gold = gold

Then your shopkeeper is:

class ShopKeeper(Character):

    def __init__(self, player, potions):
        potions = int(potions) * -1
        super(ShopKeeper, self).__init__(potions*10, ...)

Also, some of the calculated attributes would be easier as properties:

class Player(Character):

    def extra_health(self): 
        return self.lvl * 10

Now the value changes automatically if the Player's lvl increases.

It is not clear why main is a class. It seems to be used mainly as a collection of helper methods (e.g. valid_input, valid_int) which would be better packaged as simple functions - note that very few of the methods include references to self attributes or other methods. Also, using __init__ to run things is unconventional - if you did have some kind of main class, I would expect it to be called like:

if __name__ == "__main__":

i.e. first instantiate the class, then call some method to run it. Note the guard to prevent this running if you from wherever import Main later on.

Your locations could be classes, too. For example:

class Tavern(Location):

if town == "tavern":

This allows you to make the town neater:

TOWN = {'tavern': Tavern, ...}


Some of the functionality could be moved to the Character classes, e.g.

class Player(Character):

    CLASSES = ["warrior", "archer", "mage"]
    RACES = ["human", "elf", "dwarf"]

    def __init__(self, name, race, class, ...):
        super(Player, self).__init__(lvl, xp, gold, ...)

    def fight(self, enemy):

    def from_input(cls):
        name = input("What is your name?")
        race = valid_input("What is your race?", cls.RACES)
        class_ = valid_input("What is your class?", cls.CLASSES)
        return cls(name, race, class_, ...)

Note that this is neater if valid_input doesn't included the valid options in the prompt - this leads to duplication of the input data (e.g. valid_input("What is your race? (Human, Elf, Dwarf) ", ["human","elf","dwarf"]) includes the same data twice.)

  • \$\begingroup\$ Thank you for your feed back :) I will have you notified when i update the game again. I will definitely take what you said into consideration... Give me a few days to update :) \$\endgroup\$ Dec 3, 2014 at 14:11
  • \$\begingroup\$ You should read through what Josay told you again too - there are some things (e.g. dictionaries) that you have not yet implemented. \$\endgroup\$
    – jonrsharpe
    Dec 3, 2014 at 14:19
  • 1
    \$\begingroup\$ ya... I did not get all the things updated... And i plan on adding more dictionaries... But I'm still learning how to use them properly \$\endgroup\$ Dec 3, 2014 at 14:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.