HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Text-based adventure game with combat and game-reset functionality

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
combatwithtextadventuregamebasedandfunctionalityreset

Problem

Please suggest improvements/refactoring to this game to make it more idiomatic Python.

```
import random
import time

class Hero:
def __init__(self):
self.level = 1
self.max_hp = 3
self.hp = self.max_hp
self.attack = 5 + self.level
self.defense = 5 + self.level
self.name = ''
self.xp = 0

def name_self(self):
self.name = raw_input("What do you call yourself, anyway? ")
if self.name == "":
self.name_self()

def heal_self(self):
amount = self.xp * .2
self.hp += amount
print "You attempt to heal yourself..."
time.sleep(1)
print "You healed yourself for %d HP, but used half your XP. Feels good, man." % amount
self.xp *= .5
self.hp_limit()

def hp_limit(self):
if self.hp > self.max_hp:
self.hp = self.max_hp

def death(self):
print "Sorry, %s, you is dead now." % (self.name)
time.sleep(1)
print "Well, aren't you lucky, there is an afterlife after all."

def xp_up(self, xp):
self.xp += xp
print "You gain: %s XP" % xp

def look_self(self):
print ("You are %s, not from around here.") % self.name
print ("You are level %s with %s attack and %s defense.") % (self.level, self.attack, self.defense)
print ("You need %s XP to level up.") % (self.level**2 * 10)

class Monster:
def __init__(self, name):
self.name = name
self.hp = random.randint(2,10)
self.attack = random.randint(2,5)
self.defense = random.randint(2,5)
self.xp = random.randint(2,8)

class Room:
def __init__(self, key):
self.room_data = {
"home":{"description":"You're at home. This is where you live, unfortunately.",
"exits":["forest"]},
"forest":{"description":"You're in a dark forest. It's fairly gloomy.",
"exits":["home", "lake"]},
"lake"

Solution

A few suggestions:

Simplify and reduce duplication using inheritance, for example:

class Character(object): # note new-style classes

    def __init__(self, name, hp, attack, defence, xp):
        self.name = name
        self.hp = hp
        self.attack = attack
        self.defence = defence
        self.xp = xp

class Monster(Character):

    def __init__(self, name):
        super(Monster, self).__init__(name, random.randint(2,10),
                                      random.randint(2,5), 
                                      random.randint(2,5),
                                      random.randint(2,8))

class Hero(Character):

    MAX_HP = 3

    def __init__(self, level=1):
        super(Hero, self).__init__('', self.MAX_HP, level+5, 
                                   level+5, 0)
        self.level = level


Move general class data out of the instance:

class Room(object):

    ROOM_DATA = {"home": {"description": "You're at home. This is where you live, unfortunately.",
                          "exits": ["forest"]},
                 "forest": {"description": "You're in a dark forest. It's fairly gloomy.",
                            "exits": ["home", "lake"]},
                 "lake": {"description": "You see a lake circled by rocks. It's too cold to swim.",
                          "exits": ["forest", "mountain"]},
                 "mountain": {"description":"You can see for miles around. Don't fall off.",
                              "exits": ["lake"]}}

    def __init__(self, key):
        self.description = self.ROOM_DATA[key]["description"]
        self.exits = self.ROOM_DATA[key]["exits"]
        self.name = str(key)
        self.monster_list = {}


Similarly e.g. Game.COMMAND_LIST isn't instance-specific.

Simplify the display using "magic methods" and str.format:

class Monster(Character):

    ...

    def __str__(self):
        template = "The {0} has {1.hp} HP, {1.attack} attack, ..." # etc.
        return template.format(self.name.capitalize(), self)

class Game(object):

    ...

    def look_monster(self, monster):
        print str(monster)


Use looping rather than recursion for input validation (see e.g. this SO community wiki):

def name_self(self):
    while True:
        self.name = raw_input("What do you call yourself, anyway? ")
        if self.name != "":
            break


Consider structuring your functions differently, such that they call each other, to cut down multiple lines of calls at the top level. For example: Game.__init__ could call populate and look; Hero.__init__ could call name_self and look_self(); and you could add Game.play_round to call handle_input(), game.update() and game.output().

Put the top-level code in an if __name__ == '__main__': block (see e.g. this SO question).

Code Snippets

class Character(object): # note new-style classes

    def __init__(self, name, hp, attack, defence, xp):
        self.name = name
        self.hp = hp
        self.attack = attack
        self.defence = defence
        self.xp = xp


class Monster(Character):

    def __init__(self, name):
        super(Monster, self).__init__(name, random.randint(2,10),
                                      random.randint(2,5), 
                                      random.randint(2,5),
                                      random.randint(2,8))


class Hero(Character):

    MAX_HP = 3

    def __init__(self, level=1):
        super(Hero, self).__init__('', self.MAX_HP, level+5, 
                                   level+5, 0)
        self.level = level
class Room(object):

    ROOM_DATA = {"home": {"description": "You're at home. This is where you live, unfortunately.",
                          "exits": ["forest"]},
                 "forest": {"description": "You're in a dark forest. It's fairly gloomy.",
                            "exits": ["home", "lake"]},
                 "lake": {"description": "You see a lake circled by rocks. It's too cold to swim.",
                          "exits": ["forest", "mountain"]},
                 "mountain": {"description":"You can see for miles around. Don't fall off.",
                              "exits": ["lake"]}}

    def __init__(self, key):
        self.description = self.ROOM_DATA[key]["description"]
        self.exits = self.ROOM_DATA[key]["exits"]
        self.name = str(key)
        self.monster_list = {}
class Monster(Character):

    ...

    def __str__(self):
        template = "The {0} has {1.hp} HP, {1.attack} attack, ..." # etc.
        return template.format(self.name.capitalize(), self)


class Game(object):

    ...

    def look_monster(self, monster):
        print str(monster)
def name_self(self):
    while True:
        self.name = raw_input("What do you call yourself, anyway? ")
        if self.name != "":
            break

Context

StackExchange Code Review Q#56873, answer score: 6

Revisions (0)

No revisions yet.