8
\$\begingroup\$

I'm writing a little text based adventure game, and wanted to know if there's anything I can do better thus far. It is not completed to don't get mad when it ends. I'm just curious as to better ways to write the syntax, or different ways to do things.

from random import randint


BACKPACK = {}  # What you will bring with you on your journey
SURVIVAL_TOOLS = {}  # What you collect over the course of your journey


def create_player():
    """
    Create your character name
    """
    player_name = raw_input('Enter your characters name: ').capitalize()
    return player_name


def formatter(width=50):
    """
    Formatting for to keep everything all pretty
    :type width: Integer
    """
    return '*' * width


def die(why):
    """
    Kill off the program becasue you did something stupid
    :type why: String
    """
    return "{}. You slowly fade into darkness. Game over.".format(why)


def gather_gear(player):
    """
    Gather your gear from a set list of items you have available
    :type player: String
    """
    print formatter()
    print "{}! Shouts Jack as he runs towards you." \
          " Do you wanna go Hiking this weekend?" \
          " You ponder this for a second." \
          " What the hell, you think." \
          " Can't be any worse then last time." \
          " Sure, Jack! You say enthusiastically." \
          " Just let me get some things prepared.".format(player)

    options = {  # All the items that are available to you before you leave
        'fire starter': 1,
        'matches': randint(1, 5),  # Uses random integers as the value
        'flash light': 1,
        'sleeping bag': 1,
        'canteen cup': 1,
        'dried foods': randint(1, 6),
        'shovel': 1,
        'knife': 1
    }

    for key in options:
        print "You have {} {}".format(options[key], key)  # Print out all your items and make it look pretty
    print "\n"

    count = 3
    num_in_pack = 0

    while count != 0:
        item = raw_input("What would you like to take? Choose {} items one at a time: ".format(str(count))).lower()
        if item in options and item not in BACKPACK:  # As long as the item is available you can use it
            BACKPACK[item] = options[item]  # Add the item value to your backpack constant
            count -= 1
            print "You throw a {} in your backpack".format(item)
            num_in_pack += 1
            if num_in_pack == 3:  # If you have three items, lets begin!
                print "Your backpack is now full."
                start_adventure(player)
        else:
            print "Can't bring that item."

    return BACKPACK


def start_adventure(player):
    """Start your adventure, you start out hiking and then someone dies
       you freak out because your a wuss and run away like a little girl.
       :type player: String
    """

    print formatter()  # Pretty formatting
    print "You head out to your location." \
          " On your way there you and Jack" \
          " talk about your lives until you" \
          " finally pull in to the trail." \
          " You look at the map and notice there's" \
          " an emergency number. " \
          ' "Jack" you say "Should we take down this number?"' \
          ' "Don\'t be such a baby {}" says Jack laughing' \
          " You start walking the trail and come to a clearing." \
          " When suddenly there's a loud growl, you turn around" \
          " and see a mountain lion jump on Jack," \
          " Jack starts screaming as the lion rips his throat out." \
          " You begin to run, you hear Jack's screams fading away" \
          " in the distance. You run for what seems like an hour" \
          " and realize you have no idea where you are." \
          " You lean against a trunk and think about what just" \
          " happened. You have limited options.\n".format(player)  # Well that escalated quickly O.O

    options = ['Start a fire', 'Cry', 'Find food', 'Build a shelter']
    print "Options are:"

    for opt in options:
        print "{}".format(opt)  # Print out your possible options at this point
    print "\n"
    choice = raw_input('What do you want to do: ').capitalize()


def welcome_screen():
    """
    Main program method, where all the magic happens
    """
    player_name = create_player()
    print "\nWelcome to the survival game {}.\n".format(player_name)
    message = """The object of this game is fairly simple. Survive.
You will be given a set number of items to survive
with. Remember, small mistakes in the wilderness
can be the difference between life and death.
Are you ready to begin, {} [Y/N]?""".format(player_name)

    print formatter()
    print message
    begin = raw_input()

    if begin == 'y' or 'Y':
        gather_gear(player_name)
    elif begin == 'n' or 'N':
        print 'Exiting game..'
    else:
        print "{} is not an option. try again.."


if __name__ == '__main__':
    welcome_screen()
\$\endgroup\$
  • \$\begingroup\$ As it stands, you'll probably be able to get a review. That being said, could you be a little more specific in areas that you might want feedback? "Different ways to do things" is a little too vague. \$\endgroup\$ – Trojan404 Aug 3 '16 at 19:42
4
\$\begingroup\$

The biggest issue I see is that you're mixing your logic (i.e. how things happen) with the gameplay (why things happen). In general, regardless of what sort of project you're working on, you want to separate the business logic (how things happen) from the user interface (what the user is doing, what is happening, etc).

To facilitate this I'd advise using OOP. The most obvious place to do this is with a Player class. I've also added a Backpack class, and some custom exceptions.

class AdventureGameException(Exception): pass

class BackpackFullException(AdventureGameException): pass

class Backpack(object):

    def __init__(self, items=None, max_size=3):
        self.stuff = {}
        if items:
            self.stuff.update(items)
        self._max_size = 3

    @property
    def full(self):
        return len(self.stuff) == self._max_size

    def add_item(self, name, value):
        if self.full:
            self.stuff[name] = value
        else:
            raise BackpackFullException()

    def __str__(self):
        return "A backpack with {} items.".format(len(self.stuff))

    def __repr__(self):
        return "Backpack(items={items}, max_size={max_size})".format(items=self.stuff, max_size=self._max_size)


class Player(object):

    def __init__(self, name, backpack=Backpack()):
        self.name = name
        self.backpack = backpack

    def take_item(self, item_name, item_value):
        self.backpack.add_item(item_name, item_value)

    def __str__(self):
        return self.name

    def __repr__(self):
        return "Player({name}, backpack={backpack})".format(name-self.name, backpack=repr(self.backpack))

All I've done is make some custom exceptions that we can use in our application, and a Backpack and Player object.

The backpack just keeps track of its own stuff, and whether or not it is full. The player keeps track of themselves, and in this case doesn't worry about error handling.

I've also given them both repr implementations. While it isn't necessary, it is nice when a repr is roundtrippable, i.e. eval(repr(some_item)) == some_item. Speaking of == I didn't implement any of the other operators; I didn't see a need for them, but if you want it then they're straightforward to implement.

The next thing I'd do is make a game driver class (and as the app gets bigger, probably classes. You might even want to do something like MVC eventually). This class should be responsible for actually taking the user through the game.

I made something like this

class GameDriver(object):

    def __init__(self):
        self.player = self.get_player()
        self.in_progress = False
        self.lost = False

    def start_game(self):
        print "\nWelcome to the survival game {}.\n".format(self.player)    
        print formatter()
        print """The object of this game is fairly simple. Survive.
You will be given a set number of items to survive
with. Remember, small mistakes in the wilderness
can be the difference between life and death.
Are you ready to begin, {} [Y/N]?""".format(self.player)

        begin = raw_input().lower()

        while begin not in ['y', 'n']
            if begin == 'y':
                self.start_game()
            elif begin == 'n':
                print 'Exiting game..'
                self.end_game('Quitter')
            else:
                print "{} is not an option. Please pick [Y/N]".format(begin)

    def get_player(self):
        return Player(raw_input('Enter your characters name: ').capitalize())

    def end_game(self, reason):
        self.in_progress = False
        self.lost = True

        print "{}. You slowly fade into darkness. Game over.".format(reason)

    def gather_gear(self):
        print self.formatter()
        print "'{}! Shouts Jack as he runs towards you." \
              " 'Do you wanna go Hiking this weekend?'" \
              " You ponder this for a second." \
              " What the hell, you think." \
              " Can't be any worse then last time." \
              " 'Sure, Jack!' You say enthusiastically." \
              " 'Just let me get some things prepared.'".format(self.player)

        options = {
            'fire starter': 1,
            'matches': randint(1, 5),
            'flash light': 1,
            'sleeping bag': 1,
            'canteen cup': 1,
            'dried foods': randint(1, 6),
            'shovel': 1,
            'knife': 1
        }

        self.take_items_until_full(options)

    def print_options(self, items):    
        for option_name, option_value in items.iteritems():
            print "\t{} {}.format(option_value, option_name)"
        print

    def take_items_until_full(self, items):
        num_left = self.player.backpack.space
        while num_left > 0:
            print "You can take {} more items from:".format(num_left)
            self.print_options(items)
            choice = raw_input("What would you like to take with you?").strip().lower()

            try:
                item = items.pop(choice)
            except KeyError:
                print "You can't take that item"
                continue

            try:
                num_left = self.player.take_item(choice, item)
            except BackpackFullException:
                print "You can't carry anything else!"
                break

            print "You throw a {} in your backpack".format(item)
            num_left -= 1

        print "Your backpack is now full"

    def formatter(self, width=50):
        """
        Formatting for to keep everything all pretty
        :type width: Integer
        """
        return '*' * width

    def start_game(self):
        self.gather_gear()
        self.start_adventure()

    def start_adventure(self):
        print self.formatter()
        print "You head out to your location." \
              " On your way there you and Jack" \
              " talk about your lives until you" \
              " finally pull in to the trail." \
              " You look at the map and notice there's" \
              " an emergency number. " \
              ' "Jack" you say "Should we take down this number?"' \
              ' "Don\'t be such a baby {}" says Jack laughing' \
              " You start walking the trail and come to a clearing." \
              " When suddenly there's a loud growl, you turn around" \
              " and see a mountain lion jump on Jack," \
              " Jack starts screaming as the lion rips his throat out." \
              " You begin to run, you hear Jack's screams fading away" \
              " in the distance. You run for what seems like an hour" \
              " and realize you have no idea where you are." \
              " You lean against a trunk and think about what just" \
              " happened. You have limited options.\n".format(self.player)  # Well that escalated quickly O.O

        options = ['Start a fire', 'Cry', 'Find food', 'Build a shelter']
        choice = raw_input("What do you want to do? \n{}".format(options.join('\n')))

I've made a few small adjustments to your original code for clarity, but I mostly just took your existing functions and put them into the class. They rely on the player and backpack objects to handle themselves, and just use them as necessary. I also broke things up into their own functions to help with readability.

The biggest issue now is that you have a ton of game text mixed in with your driver. This is annoying because all of that text makes it harder to actually read the logic, and the contents of those strings is (generally) not going to actually make a huge difference to your code. It would also make it hard for you to translate this if it makes it big and you wanted people from all over to play it. I like to use a class like this when I have a lot of text that I want to keep separate. By separating the text from the code you also make it easier if you ever want to change where the code comes from; maybe you have a server that gives the text, or a database, or just a text file. By keeping the text out of the game and making something responsible for retrieving it you gain that modularity. I've just left the strings in the code, but in their own class.

class GameText(object):

    def __init__(self, player):
        self.player_name = player.name

    @property
    def welcome_text(self):
        return "\nWelcome to the survival game {}.\n".format(self.player_name)

    @property
    def separator(self):
        return "*" * 50

    @property
    def objective(self):
        return """The object of this game is fairly simple. Survive.
You will be given a set number of items to survive
with. Remember, small mistakes in the wilderness
can be the difference between life and death.
Are you ready to begin, {} [Y/N]?""".format(self.player_name)

    @property
    def exit_message(self):
        return 'Exiting game..'

    def get_invalid_choice_message(self, options, selection):
        return "{} is not an option. Please pick [{}]".format(selection, options)

I didn't include all of them here because it should be self explanatory. One thing I'd like to point out is that I made a lot of them properties instead of attributes. This isn't a huge deal right now, but it will make it much easier if you ever switch to another system of getting text (i.e. a database).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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