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

My beginner attempt at a text adventure

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

Problem

I'm a beginner working on a text adventure. It's quite bare-bones at this point but I would appreciate feedback before I continue and the flaws in my code become further ingrained.

I plan to add an item pickup and inventory system which can store items to heal and deal damage to enemies, some problem-solving and expand the number of locations. I'd like to hear some things I should implement which could help me learn though.

```
# A Countryside Adventure

#from questionmodule import * # moved module to this file for posting on forum
from sys import exit
import random

def ask_yes_no(question):
"""Ask a yes or no question"""

allowed_answers = ("y", "yes", "n", "no")
response = None
while response not in allowed_answers :
response = input(question).lower()
if response not in allowed_answers:
print("That is not a valid response.")
return response

def ask_yes_no_boolean(question):
"""As a yes or no question and return response as boolean"""

allowed_answers = ("y", "yes", "n", "no")
response = None
while response not in allowed_answers:
response = input(question).lower()
if response not in allowed_answers:
print("That is not a valid response.")
if response in ("y", "yes"):
return True
return False

def two_option_q(question, option_one, option_two):
"""As a two option question and return response as boolean"""

allowed_answers = (option_one, option_two)
response = None
while response not in allowed_answers:
response = input(question).lower()
if response not in allowed_answers:
print("That is not a valid response.")
if response == (option_one):
return option_one
else:
return option_two

def ask_left_right(question):
"""Ask a left or right question"""

allowed_answers = ("left", "l", "right", "r")
response = None
while response not in allowed_answers:
response = input(ques

Solution

This is a lot of code to run through, but I'll pick apart some sections of the code that stood out to me as needing improvement. Mind you, I'm privy to Python 2.7. Hopefully my code won't be foreign to you.

First, starting with the question functions -- you could cut down quite a few things. For example, how you take user input, and go about using the data. A simpler way of writing the function ask_yes_no would be like this:

def ask_question(question, boolean):
    while True:
        inp = raw_input(question + ': ').lower()
        lst = [x for x in ['yes', 'no'] if x.startswith(inp)]
        if len(lst) == 0:
            print 'Not a valid input.'

        elif len(lst) == 1:
            response = lst[0]
            break

    if boolean is True:
        if response == 'yes':
            return True

        else:
            return False

    else:
        return response

ask_question( ''' Question, Return as boolean or not? ''' )


As you can see, you can simply combine your first two question functions into one, by adding an argument to tell the code whether or not to return the response as a boolean. Also, by using the list [x for x in ['yes', 'no'] if x.startswith(inp)], users are now able to type just the first letter of a response, e.g. y for yes. If you sincerely want to keep specific lists on what response to allow, I would recommend adding another argument titled responses, and calling the function ask_question with a list of responses as an argument -- if you ever want to expand outside of just yes / no questions. Honestly, you could probably squeeze most of your questions into just a couple of functions, instead of the four you currently have.

Secondly, I would like to note the Player class for a moment. To start, you might want to scrap the class Death and add the concept as a variable or method of the Player class. Also, don't reference the actual class name inside of its own methods, i.e. with the line if strike_type in Player.STRIKES: in the method attack, the Player.STRIKES part could just be self.STRIKES. Though maybe you should just include it as an attribute of the __init__ statement? You may also want to add a take_damage method to the player class instead of changing it through a method of the Enemy class?

Speaking of the Enemy class, perhaps you should simply write a function named 'generate' that creates enemies? For instance, when a player walks into a new location, you can have an argument for the location that takes an enemy object, and then create one through the class method.

class Enemy(Character):
    enemy_list = ['boar', 'troll', 'wild cat']

    def __init__(self, name, health, damage):
        self.damage = damage
        super(Enemy, self).__init__(name, health)

    @classmethod
    def generate(cls, player):
        name = choice(cls.enemy_list)
        damage = randint(1, player.health / 2)
        health = player.health - (damage)
        return cls(name, health, damage)

enemy = Enemy.generate(player)
print enemy.name, enemy.damage, enemy.health


If you ran Enemy.generate('''player'''), it would return an object with the values printed above: name: troll, dmg: 3, health: 7, or something of the like. Doing things like this would allow you to run this function to generate a new enemy every time the player enters a new location, or a combat situation. Maybe you should create an __init__ function for class Location, and include an argument for enemies. And for the Map class, perhaps you should create an actual map, through something like a 2-D array, and create a custom world!

In any case, your game seems to be coming along. Try implementing more efficient functions, improve and expand on your Player and Enemy classes, a 2-D array to hold a world map, and possibly a visual map as well.

Inventory systems are fairly easy to put together. I would recommend using @classmethods to generate weapons, armor, and other in-game items just like you would generate enemies.

Hope my answer was informative!

Code Snippets

def ask_question(question, boolean):
    while True:
        inp = raw_input(question + ': ').lower()
        lst = [x for x in ['yes', 'no'] if x.startswith(inp)]
        if len(lst) == 0:
            print 'Not a valid input.'

        elif len(lst) == 1:
            response = lst[0]
            break

    if boolean is True:
        if response == 'yes':
            return True

        else:
            return False

    else:
        return response

ask_question( ''' Question, Return as boolean or not? ''' )
class Enemy(Character):
    enemy_list = ['boar', 'troll', 'wild cat']

    def __init__(self, name, health, damage):
        self.damage = damage
        super(Enemy, self).__init__(name, health)

    @classmethod
    def generate(cls, player):
        name = choice(cls.enemy_list)
        damage = randint(1, player.health / 2)
        health = player.health - (damage)
        return cls(name, health, damage)

enemy = Enemy.generate(player)
print enemy.name, enemy.damage, enemy.health

Context

StackExchange Code Review Q#138607, answer score: 3

Revisions (0)

No revisions yet.