janpreeece
Posts: 5
Joined: Fri Aug 31, 2012 3:58 am

simple adventure game.

Wed Sep 26, 2012 8:06 am

Howdy the Pi heads.
When I was learning basic on a bbc computer back in 1985. I managed to program a simple 8 room adventure game. It was a text desciption only. It was great fun.
anyone have any advice as to what code to use? or any helpful tips on writting a similar game.
Peace from Jan

User avatar
jackokring
Posts: 815
Joined: Tue Jul 31, 2012 8:27 am
Location: London, UK
Contact: ICQ

Re: simple adventure game.

Wed Sep 26, 2012 8:14 am

Python as all the peeps will and do use it, and documentation is easy to find. The TAB key is important to python, it's only minor irritation when speaking it. Maybe scratch, as it can do it, although slow to enter when you can type fast.
Pi[NFA]=B256R0USB CL4SD8GB Raspbian Stock.
Pi[Work]=A+256 CL4SD8GB Raspbian Stock.
My favourite constant 1.65056745028

Sleep Mode zZ
Posts: 319
Joined: Sun Aug 19, 2012 5:56 am
Location: Finland

Re: simple adventure game.

Wed Sep 26, 2012 8:44 am

I really liked the book Invent your own computer games with python. It's free and the example programs can be used as a starting point for your own programs. By the sixth chapter - "Dragon Realm" - you probably know all that you need for your program.

User avatar
MattHawkinsUK
Posts: 536
Joined: Tue Jan 10, 2012 8:48 pm
Location: UK
Contact: Website

Re: simple adventure game.

Wed Sep 26, 2012 9:06 am

Thanks Sleep Mode. Great tip. The free books are available from :
http://inventwithpython.com
My Raspberry Pi blog and home of the BerryClip Add-on board : http://www.raspberrypi-spy.co.uk/
Follow me on Google+, Facebook, Pinterest and Twitter (@RPiSpy)

janpreeece
Posts: 5
Joined: Fri Aug 31, 2012 3:58 am

Re: simple adventure game.

Wed Sep 26, 2012 12:57 pm

Thanks Pi people.
Your advice has been very helpful and I am on my way to invent my own game. I also have another project that I would eventualy like to solve using Pi. Which is to connect a radio reciever (the type that remote control cars use) to my pi via those pins that stick up. so I could remotely control my music system(ipod) that is aslo connected to the Pi. Play, pause, fwd and rwnd etc, also visable on a monitor.
It's got to be possible.
Thanks again from a very excited Pi owner.
P.S. Didn't really know what to do with the Pi but have realised it has a huge amount of protential. Is this the next shift in the modern world?
Regards from Jan

JonB
Posts: 220
Joined: Tue Nov 29, 2011 9:26 pm

Re: simple adventure game.

Wed Sep 26, 2012 7:03 pm

Try this:

http://forum.6502.org/viewtopic.php?t=1819

It's a text adventure game written in Microsoft 8K BASIC that I ported from Commodore PET BASIC to the UK101.

ACP_
Posts: 33
Joined: Sat Aug 18, 2012 10:40 pm

Re: simple adventure game.

Wed Sep 26, 2012 10:34 pm

You can easly use Python Cmd.cmd class from standard library. While official documentation might be hard to understand by beginner here is a better explenation with examples: http://www.doughellmann.com/PyMOTW/cmd/index.html

You can use it for input handling. It seems that Cmd.cmd has been designed with text based adventure games in mind.

User avatar
FeersumEndjinn
Posts: 148
Joined: Mon Nov 21, 2011 4:02 pm

Re: simple adventure game.

Fri Oct 05, 2012 2:36 pm

I'm writing a similar thing, although I think my sights are set a little higher than yours - I'm aiming for a 200+ room adventure game (possibly with some ASCII art if I can find an artist), written entirely in Python 3 and powered by a SQLite3 backend.

My advice if you're sticking purely to Python, is look at the various ways to accept input from the user. The more tolerant it is to people using different words (look, inspect etc) to do the same thing, the more patience people will have for it. The better you make this bit, the easier the rest becomes. Plan ahead, think of what keywords you are likely going to use and make sure you cover all of your bases. Then think of your story - you need to make people WANT to play your game, as you have no artwork to advertise it.

My advice would be to map out your world carefully. The hardest bit is tracking which location is in which direction from which location - this is where a database comes in handy! Keep things simple at first - create a 20 room world and make sure you can move about, pick up objects, store them in an inventory etc. and keep directions to the main 4 cardinal compass points - North, South, East and West.

Also, if you come across an obstacle, post it in this thread (I've subscribed to it) and myself and the others will do what we can to help you :)
Morrolan / FeersumEndjinn

"And the lord God said unto John, 'Come forth and receive eternal life', but John came fifth and won a toaster."

SiriusHardware
Posts: 480
Joined: Thu Aug 02, 2012 9:09 pm
Location: UK

Re: simple adventure game.

Sun Oct 07, 2012 12:00 am

By the time text adventures went out of fashion the most important and most worked on component (after the story, which had to be good) was the parser, the routine which took whatever the user typed in and tried to make sense of it - some of the first generation of those games were spectacularly bad at this, requiring one particular phrase to perform a specific action, and rejecting any other ways of saying the same thing.

At the other, most advanced end of the scale came the USA's Infocom and their heartfelt British tribute act Magnetic Scrolls, both of whom had very sophisticated parsers - but even they frustrated their users sometimes - Magnetic Scrolls in particular had some beautiful location art in key scenes on the Atari ST / Amiga versions of their games and from this there arose a demand for a picture for every scene - at the same time, developers realised that if the player's choice of things to say were limited to those which could be picked from a pre-ordained list - Get, Drop, Use (object) with, Examine, you could stop the player from wasting time typing in words which were never likely to be recognised. Then, all the player had to do was to form sentences by picking the right actions from the list in the right places -

And so the graphic adventure, as splendidly typified by Monkey Island, was born.

Text adventures could rise to the fore again, but only if the parsers became so freakishly clever that they could make an apparently intelligent response to almost anything the player typed in - that's getting into the realms of AI, but I would love to see it done. I don't know anyone who loved text adventures (or played more of them) than I did.

If you own any of Infocom's old games in a form which can be copied to the Pi there are several game engines such as FROTZ in the repository which can run the story.dat code of Infocom games.

pygmy_giant
Posts: 1562
Joined: Sun Mar 04, 2012 12:49 am

Re: simple adventure game.

Sun Oct 07, 2012 12:19 am

I have given this thought myself and agree that the text adventure is an under exploited genre.

It seems to me that the easiest way to create such a comprehensive parser is to have a HUGE vocabluary (possibly an entire English dictionary) and for all command words to be also recognised via their synonyms.

Add 2 level predictive text that works on the word level and then sentance level with unrecognised words taken out and jobs a good'un?
Ostendo ignarus addo scientia.

SiriusHardware
Posts: 480
Joined: Thu Aug 02, 2012 9:09 pm
Location: UK

Re: simple adventure game.

Sun Oct 07, 2012 8:44 am

pygmy_giant wrote:
Add 2 level predictive text that works on the word level and then sentence level with unrecognised words taken out and jobs a good'un?
The idea of the parser using predictive text, restricting the guesses it makes only to words which are within the vocabulary necessary to complete the game is a good one - however, it could be a bit of a spoiler if not handled correctly, for example, it might supply the player with (as a suggested word) the name of an object which you don't even know exists yet because you haven't found it yet.

I have to be honest, predictive text (in places where it is normally found, such as on mobile phones) drives me insane.

I always turn it off.

pygmy_giant
Posts: 1562
Joined: Sun Mar 04, 2012 12:49 am

Re: simple adventure game.

Mon Oct 08, 2012 9:30 pm

Me too - but lets be honest, most Ai isn't up to snuff.

I'd rather know what my options are than waste time playing 20 questions with the parser.

For this application the illusion of intelligence would come partly from a huge vocabluary and a huge table of synonyms. This would prevent a spoiler as the huge vocabluary could contain objects that are not even in the game.

The user would still be able to make daft requests - the challenge is to indulge the player in these and allow him/her to suffer the consequences.

User avatar
jburke71
Posts: 14
Joined: Tue Oct 23, 2012 2:16 am

Re: simple adventure game.

Tue Oct 23, 2012 2:28 am

After ordering my PI a couple months ago, I wrote an interactive fiction framework in Python 3. At the time, I wanted to write a tutorial for it, but I haven't gotten around to doing it yet (though I just received my PI last week, so I guess I should get going on that). In any event, here's the code that I wrote. I tried to keep it as simple as possible for beginners. Feel free to critique, and if you're embarking on writing your own adventure, I hope you find this useful.

Code: Select all

#------------------------------------------------------------------------------
#                         <<< Command functions >>>
#
# Most of these will be modified for your game. This is where all of the magic
# happens.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# cmd_move: Moves the player from the current room to the
#           destination room (assuming the rooms connect).
#------------------------------------------------------------------------------

def cmd_move(obj):

    # Variable to track which direction gets assigned.
    dir = ""

    # Check which direction was requested and assign it to the dir variable.
    # If we don't recognie the direction, print an error.
    if(obj == "n" or obj == "north"):
        dir = NORTH
    elif(obj == "s" or obj == "south"):
        dir = SOUTH
    elif(obj == "e" or obj == "east"):
        dir = EAST
    elif(obj == "w" or obj == "west"):
        dir = WEST
    elif(obj == "u" or obj == "up"):
        dir = UP
    elif(obj == "d" or obj == "down"):
        dir = DOWN
    else:
        print("You can't go that way.")
        return player_room

    # If we recognize the direction, check to see if the player can go that
    # way. If they can, move the play. Otherwise, print an error.
    if(rooms[player_room][dir] != 0):
        return rooms[player_room][dir]
    else:
        print("You can't go that way.")
        return player_room

#------------------------------------------------------------------------------
# cmd_look: This function displays the room information to the player.
#------------------------------------------------------------------------------

def cmd_look(obj):

    # No object specified, so show the room and it's contents.
    if(obj == ""):

        # Holding variables for look function.
        items_in_room = []

        # Print out the room name and description.
        print(rooms[player_room][NAME])
        print(rooms[player_room][DESC])

        # Check to see what items are in the room and gether their names
        # for display.
        for item in items:
            if(item[LOCATION] == player_room and item[SHOW] != 0):
                items_in_room.append(item[NAME])

        # Print out the item names.
        if(len(items_in_room) > 0):
           print("You see: ", end="")
           for item in items_in_room:
               print(item, end=" ")
           print()

    # An object was specified, so show the item description if it's in
    # the room or your inventory.
    else:
        # Check for it in the room.
        for item in items:
            if(item[NAME].lower() == obj and
               item[LOCATION] == player_room and item[VISIBLE] != 0):
                print(item[DESC])
        # Check for it in the players inventory.
        for item in player_inv:
            if(item[NAME].lower() == obj):
                print(item[DESC])

    #--------------------------------------------------------------------------
    # Add look customizations here.
    #--------------------------------------------------------------------------

#------------------------------------------------------------------------------
# cmd_get: Pickup an item in the room. 
#------------------------------------------------------------------------------

def cmd_get(obj):

    # Check to see if the item is in the room and put it in the inventory.
    for item in items:
        if(item[NAME].lower() == obj and item[LOCATION] == player_room):
            if(item[PICKUP] != 0):
                player_inv.append(item)
                item[LOCATION] = 0
            else:
                print("You can't pick that up.")

    #--------------------------------------------------------------------------
    # Add get customizations here.
    #--------------------------------------------------------------------------

#------------------------------------------------------------------------------
# cmd_drop: Drop something in your inventory.
#------------------------------------------------------------------------------

def cmd_drop(obj):

    # Holding variables for tracking the dropped item.
    item_index = ""
    item_dropped = ""
    
    if(obj != ""):

        # See if the item is in inventory and find its index.
        for item in player_inv:
            if(item[NAME].lower() == obj):
                item_index = player_inv.index(item)

        # Add the item back to the room, and remove it from player's inventory.
        if(item_index != ""):
            item_dropped = player_inv[item_index]
            item_dropped[LOCATION] = player_room            
            del player_inv[item_index]

        else:
            print("You aren't carrying that.")

    else:
        print("You can't drop nothing.")

    #--------------------------------------------------------------------------
    # Add drop customizations here.
    #--------------------------------------------------------------------------

#------------------------------------------------------------------------------
# cmd_inv: See what you're carrying.
#------------------------------------------------------------------------------

def cmd_inv():

    if(len(player_inv) > 0):
        print("You are carrying: ")
        for item in player_inv:
            print(item[NAME])
    else:
        print("You aren't carrying anything.")

#------------------------------------------------------------------------------
# cmd_quit: exit the game.
#------------------------------------------------------------------------------

def cmd_quit():
    exit()

#------------------------------------------------------------------------------
# cmd_help: Print a list of possible commands.
#------------------------------------------------------------------------------

def cmd_help():
    print("You may be able to do some of these: ")
    for cmd in cmd_list:
        print(cmd)

#------------------------------------------------------------------------------
#                           <<< Game Data >>>
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
#                         <<< Global Variables >>>
#
# Global variables should be used springly. However, with some games, global
# variables can make the code much simpler.
#------------------------------------------------------------------------------

cmd_list = ["go", "n", "s", "e", "w", "u", "d", "get", "look", "inv", "drop",
            "quit", "help"]
player_room = 1
player_inv = []
rooms = []
items = []

#------------------------------------------------------------------------------
#                         <<< Global Constants >>>
#
# Constants are variables that don't change and are useful for defining "magic"
# numbers in programs. Use them whenever you can.
#------------------------------------------------------------------------------

NAME = 0
DESC = 1
NORTH = 2
SOUTH = 3
EAST = 4
WEST = 5
UP = 6
DOWN = 7
LOCATION = 2
PICKUP = 3
SHOW = 4
VISIBLE = 5

#------------------------------------------------------------------------------
# Room Data: The room data holds information about each room. The data
#            structure that we're using is a list of lists. The first list is
#            the room, and the embeded list holds all the information about
#            that room.
#------------------------------------------------------------------------------

def load_rooms():
    rooms.append(["", "", 0, 0, 0, 0, 0, 0])
    #List structure for rooms: Name, Desc, N, S, E, W, U, D
    rooms.append(["Room 1", "In room #1", 2, 0, 0, 0, 0, 0])
    rooms.append(["Room 2", "In room #2", 0, 1, 3, 4, 0, 0])
    rooms.append(["Room 3", "In room #3", 0, 0, 0, 2, 0, 0])
    rooms.append(["Room 4", "In room #4", 0, 0, 2, 0, 0, 0])

#------------------------------------------------------------------------------
# Item Data: As with the room data, the item data is a list of lists, and
#            holds the information about items in the game.
#------------------------------------------------------------------------------

def load_items():
    items.append(["", "", 0, 0, 0])
    #List structure for items: Name, Desc, Location, Pickup, Show, Visible.
    items.append(["Hammer", "Just a standard hammer.", 2, 1, 1, 1])
    items.append(["Nail", "It'll do the job.", 2, 1, 1, 1])
    items.append(["Desk", "A good place to collect your thoughts", 1, 0, 1, 1])

#------------------------------------------------------------------------------
# Game Intro: Give the player some background on your game.
#------------------------------------------------------------------------------

def intro():
    print("Welcome to my adventure game. Type 'help' to get a")
    print("list of commands.")

#------------------------------------------------------------------------------
#                            <<< The Game >>>
#
# The game starts executing here. Initialization and the game loop make up
# the bulk of any game. Data and functions make up the rest.
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Game Initialization.
#------------------------------------------------------------------------------

load_rooms()
load_items()
intro()
cmd_look("")

#------------------------------------------------------------------------------
# The game loop starts here.
#------------------------------------------------------------------------------

while 1:
    
    #--------------------------------------------------------------------------
    # Get player input, and parse the command from the object of the command.
    #--------------------------------------------------------------------------

    action = input(">").lower().strip()
    
    try:
        cmd, obj = action.split(" ", 1)
    except:
        cmd = action
        obj = ""

    #--------------------------------------------------------------------------
    # Based on the players input. Call the corresponding function.
    #--------------------------------------------------------------------------

    if(cmd in cmd_list):

        if(cmd == "n" or cmd == "s" or cmd == "w" or
           cmd == "e" or cmd == "u" or cmd == "d"):
            player_room = cmd_move(cmd)
            cmd_look("")
            
        elif(cmd == "go"): 
            player_room = cmd_move(obj)
            cmd_look("")
            
        elif(cmd == "get"): 
            cmd_get(obj)
            
        elif(cmd == "look"):
            cmd_look(obj)

        elif(cmd == "inv"):
            cmd_inv()

        elif(cmd == "drop"):
            cmd_drop(obj)
            
        elif(cmd == "quit"):
            cmd_quit()

        elif(cmd == "help"):
            cmd_help()

        #----------------------------------------------------------------------
        # Add custom commands here.
        #----------------------------------------------------------------------
        
        #----------------------------------------------------------------------
        # End custom commands.
        #----------------------------------------------------------------------
        
    else:
        print("Huh?")
    


pygmy_giant
Posts: 1562
Joined: Sun Mar 04, 2012 12:49 am

Re: simple adventure game.

Tue Oct 23, 2012 11:19 am

I don't know python, but it seems an elegant simple working solution.

I'm looking at a more sophisticated multi-user PHP / C++ on-line solution. I'm toying with the idea of having a flexible object class were all objects can double as locations.

E.g. A test for the system would be for it to allow one to enter an upstairs room, see that there is a chest of drawers with a vase on it, open the top draw and take an object from it, push the chest under a high window, climb on the chest and accidentally knock off the vase which smashes, then climb out of an open window and down a drainpipe to a garden below.

I've got some of the way there on paper and am pleased with the object orientated approach as it allows for the user to themselves be a location containing a jacket with pockets and items in the pockets, or a tree with branches that can be climbed. It also allows recursive rendering so that a description of an item such as a chest of drawers would include the vase on it which inturn would include the flowers in the vase, and also the drawers in it, and the drawers' descriptions would include the items in them when opened.

This is off-topic as the title of this thread is about a 'simple' (not complex) adventure game and I think you've acheived that.

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am
Contact: Website

Re: simple adventure game.

Thu Oct 25, 2012 8:03 am

@jburke71: The program makes very little use of Python. It almost looks like it is written with another programming language in mind. No classes, not even dictionaries are used. But lists with magical numerical indices as constants to make it a little more readable instead. Truth values and `None` are also not used. Lists are made to look like index 1 based with dummy entries at index 0.

In addition to the clearly documented global variables, `cmd`, `obj`, and `action` are also global, because the main code is on module level and not in an own function. This functions is usually called `main` and is invoked with the following idiom:

Code: Select all

if __name__ == '__main__':
    main()
Now the module can be imported by other modules or in an interactive Python shell without the main code starting to run. This is helpful to test functions manually or with automated tests, or reuse them in other modules.

The comments above functions explaining what the functions do are better written as docstrings. Then they can be used by tools and IDEs to create documentation in various formats or to show tool tips in an editor.

When a name has to be bound to some value but there is no meaningful value at that time, don't bind it to the empty string unless that would be a valid value later on in the program. The value for ”nothing” in Python is `None`.

Instead of 0 and 1 for meaning true and false the decicated boolean type `bool` and its literals `False` and `True` should be used. The the reader does not have to wonder if there are other valid values besides 0 and 1 for a variable then.

A default argument for `cmd_look()` would make it unneccesary to call it explicitly with an empty string, which looks a bit odd to me.

Every value in Python has a truth value that is used in places like the result of the condition after ``if``. For sequence types it is usually `False` if the sequence is empty and `True` otherwise. So expressions like ``if len(sequence) > 0:`` can be written as ``if sequence:``.

``if`` is not a function so it should not be written as if it where. There is no need for parenthesis around the conditional expression and there should be a space after the keyword.

If there is no concrete exception(s) given after an ``except`` statement, it handles *all* possible exceptions. That's is usually a bad idea because it will also handle exceptions the programmer may not have thought of, just like the ones he expected. Code sensible for a `ValueError` might not be suited well for a `MemoryError` or a `NameError`. Bare excepts should be used seldom, and if they are they should re-raise the exception or log it somewhere so the programmer has a chance to know which exceptions are going through this handler. Otherwise hunting bugs could get *very* frustrating.

Here's a version with some changes applied:

Code: Select all

#!/usr/bin/env python
from __future__ import print_function
import sys

if sys.version_info[0] < 3:
    input = raw_input

#------------------------------------------------------------------------------
#                           <<< Game Data >>>
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
#                         <<< Global Constants >>>
#
# Constants are variables that don't change and are useful for defining "magic"
# numbers in programs. Use them whenever you can.
#------------------------------------------------------------------------------

NAME = 0
DESC = 1
NORTH = 2
SOUTH = 3
EAST = 4
WEST = 5
UP = 6
DOWN = 7
LOCATION = 2
PICKUP = 3
SHOW = 4
VISIBLE = 5

STRING_TO_DIRECTION = {
    'north': NORTH,
    'south': SOUTH,
    'east': EAST,
    'west': WEST,
    'up': UP,
    'down': DOWN,
}
# 
# Also map the first character of each direction to the direction constant.
# 
STRING_TO_DIRECTION.update((k[0], v) for k, v in STRING_TO_DIRECTION.items())

COMMAND_NAMES = [
    "go", "n", "s", "e", "w", "u", "d", "get", "look", "inv", "drop", "quit",
    "help",
]


def load_rooms():
    """The room data holds information about each room.
    
    The data structure that we're using is a list of lists.  The first list
    is the room, and the embeded list holds all the information about that
    room.
    """
    #List structure for rooms: Name, Desc, N, S, E, W, U, D
    return [
        ["Room 1", "In room #1", 2, None, None, None, None, None],
        ["Room 2", "In room #2", None, 1, 3, 4, None, None],
        ["Room 3", "In room #3", None, None, None, 2, None, None],
        ["Room 4", "In room #4", None, None, 2, None, None, None],
    ]


def load_items():
    """As with the room data, the item data is a list of lists, and  holds the
    information about items in the game.
    """
    #List structure for items: Name, Desc, Location, Pickup, Show, Visible.
    return [
        ["Hammer", "Just a standard hammer.", 2, True, True, True],
        ["Nail", "It'll do the job.", 2, True, True, True],
        ["Desk", "A good place to collect your thoughts", 1, False, True, True],
    ]


#------------------------------------------------------------------------------
#                         <<< Command functions >>>
#
# Most of these will be modified for your game. This is where all of the magic
# happens.
#------------------------------------------------------------------------------

def cmd_move(obj, rooms, player_room):
    """Moves the player from the current room to the destination room
    (assuming the rooms connect).
    """
    direction = STRING_TO_DIRECTION.get(obj)
    if direction is not None:
        new_room = rooms[player_room][direction]
        if new_room is not None:
            return new_room
    
    print("You can't go that way.")
    return player_room


def cmd_look(rooms, player_room, items, player_inv, obj=None):
    """This function displays the room information to the player."""
    # 
    # No object specified, so show the room and it's contents.
    # 
    if obj is None:

        print(rooms[player_room][NAME])
        print(rooms[player_room][DESC])

        # Check to see what items are in the room and gather their names
        # for display.
        items_in_room = [
            item[NAME]
            for item in items
            if item[LOCATION] == player_room and item[SHOW]
        ]
        if items_in_room:
            print('You see:', ' '.join(items_in_room))

    # An object was specified, so show the item description if it's in
    # the room or your inventory.
    else:
        # Check for it in the room.
        for item in items:
            if (
                item[NAME].lower() == obj
                and item[LOCATION] == player_room
                and item[VISIBLE]
            ):
                print(item[DESC])
        # Check for it in the players inventory.
        for item in player_inv:
            if item[NAME].lower() == obj:
                print(item[DESC])

    #--------------------------------------------------------------------------
    # Add look customizations here.
    #--------------------------------------------------------------------------


def cmd_get(obj, items, player_room, player_inv):
    """Pickup an item in the room."""
    # 
    # Check to see if the item is in the room and put it in the inventory.
    # 
    for item in items:
        if item[NAME].lower() == obj and item[LOCATION] == player_room:
            if item[PICKUP]:
                player_inv.append(item)
                item[LOCATION] = None
            else:
                print("You can't pick that up.")

    #--------------------------------------------------------------------------
    # Add get customizations here.
    #--------------------------------------------------------------------------


def cmd_drop(obj, player_inv, player_room):
    """Drop something in your inventory."""
    if obj:
        # 
        # See if the item is in inventory and put it back into the room.
        # 
        for item_index, item in enumerate(player_inv):
            if item[NAME].lower() == obj:
                item[LOCATION] = player_room
                del player_inv[item_index]
                break
        else:
            print("You aren't carrying that.")
    else:
        print("You can't drop nothing.")

    #--------------------------------------------------------------------------
    # Add drop customizations here.
    #--------------------------------------------------------------------------


def cmd_inv(player_inv):
    """See what you're carrying."""
    if player_inv:
        print("You are carrying: ")
        for item in player_inv:
            print(item[NAME])
    else:
        print("You aren't carrying anything.")


def cmd_quit():
    """Exit the game."""
    exit()


def cmd_help():
    """Print a list of possible commands."""
    print("You may be able to do some of these: ")
    for cmd in COMMAND_NAMES:
        print(cmd)


def intro():
    """Give the player some background on your game."""
    print("Welcome to my adventure game. Type 'help' to get a")
    print("list of commands.")

#------------------------------------------------------------------------------
#                            <<< The Game >>>
#
#------------------------------------------------------------------------------

def main():
    """The game starts executing here.
    
    Initialization and the game loop make up the bulk of any game.  Data and
    functions make up the rest.
    """
    
    #--------------------------------------------------------------------------
    # Game Initialization.
    #--------------------------------------------------------------------------
    player_room = 0
    player_inv = []
    rooms = load_rooms()
    items = load_items()
    
    intro()
    cmd_look(rooms, player_room, items, player_inv)

    #--------------------------------------------------------------------------
    # The game loop starts here.
    #--------------------------------------------------------------------------

    while True:
        
        #----------------------------------------------------------------------
        # Get player input and parse the command from the object of the command.
        #----------------------------------------------------------------------

        action = input(">").lower().strip()
        
        try:
            cmd, obj = action.split(None, 1)
        except ValueError:
            cmd, obj = action, ''

        #----------------------------------------------------------------------
        # Based on the players input. Call the corresponding function.
        #----------------------------------------------------------------------

        if cmd in STRING_TO_DIRECTION:
            cmd, obj = 'go', obj
            
        if cmd == "go":
            player_room = cmd_move(obj, rooms, player_room)
            cmd_look(rooms, player_room, items, player_inv)
        elif cmd == "get":
            cmd_get(obj, items, player_room, player_inv)
        elif cmd == "look":
            cmd_look(rooms, player_room, items, player_inv, obj)
        elif cmd == "inv":
            cmd_inv(player_inv)
        elif cmd == "drop":
            cmd_drop(obj, player_inv, player_room)
        elif cmd == "quit":
            cmd_quit()
        elif cmd == "help":
            cmd_help()

        #------------------------------------------------------------------
        # Add custom commands here.
        #------------------------------------------------------------------
        
        #------------------------------------------------------------------
        # End custom commands.
        #------------------------------------------------------------------
        else:
            print("Huh?")


if __name__ == '__main__':
    main()
If the game state would be combined into an object it would be easier to pass it around and to unify the signatures of the command functions so the ``if``/``elif`` construct in `main()` could be replaced by a mapping from command to function. Or by a naming convention for command methods and a dispatcher mapping the user input to the corresponding method.

Code: Select all

while not self.asleep():
    sheep += 1

emba
Posts: 19
Joined: Sun Jul 29, 2012 8:54 pm

Re: simple adventure game.

Fri Oct 26, 2012 12:39 pm

i too decided after reading through "byte of python" and a few other books that id make a simple text adventure, it was very easy to make a simple 3X3 grid of rooms and make a basic get yourself out type of game. since then i found that rather easy and quite cool (took me right back to my zx81 days!) i decided to do a bigger and more involved one. so now im coding the classic board game "Hero Quest" into a text adventure, once im comfortable with the text version i may get to using pygame to add in some graphics.

i know i havent exactly contributed much here but i felt like chipping in! but just so i feel ive done a bit more heres the sloppy code from the 3X3 maze game so you can tell me how terrible my code is! (it was only an exercise to reinforce the basics id learned really nothing serious)

Code: Select all

#!/usr/bin/python

def intro():
	print ("\nyou are trapped in a house and you must escape, you can move north, east, south or west\n")

def start():
	print("\nyou are in the center hallway of the house\n")
	move=raw_input("which way would you like to go? :")

	if move=="north":
		study()

	elif move=="east":
		bedroom1()

	elif move=="south":
		kitchen()

	elif move=="west":
		lounge()

def study():
	print ("\nYou are in the study, there are many books and an old typewriter on a desk. you can move east, west or south\n")
	move=raw_input("which way would you like to go? :")
	
	if move=="north":
		print("\nYou cannot go that way\n")
		study()

	elif move=="south":
		start()

	elif move=="east":
		hall()
	
	elif move=="west":
		office()

def hall():
	print("\nyou are in the entrance hall of the house, a large wooden door is to the north, it looks like the way out. you can move north, south or west\n")

	move=raw_input("which way would you like to go? :")

	if move=="north":
		print("well done you have made it out of the house")

	elif move=="south":
		bedroom1()

	elif move=="west":
		study()

	elif move=="east":
		print("\nyou cannot go that way\n")
		hall()

def office():
	print("\nyou are in an office, a man sat at a desk glances up at you and returns to his work, you can move west or south\n")
	
	move=raw_input("which way would you like to go? :")

	if move=="west":
		study()

	elif move=="south":
		lounge()

	else:
		print("\nyou cannot go that way\n")
		office()

def lounge():
	print("\nyou are in the lounge, there is a large fireplace and a comfy looking sofa but you have no time to sit. you can move north south or east\n")

	move=raw_input("which way would you like to go? :")

	if move=="north":
		office()

	elif move=="south":
		dining()

	elif move=="east":
		start()

	else:
		print("\nyou cannot go that way\n")
		lounge()

def dining():
	print("you are in the dining room, a large table with candlesticks on is in the middle of the room. you can go north or east\n")

	move=raw_input("which way would you like to go? :")

	if move=="north":
		lounge()

	elif move=="east":
		kitchen()

	else:
		print("\nyou cannot go that way\n")
		dining()

def kitchen():
	print("\nyou are in the kitchen, an angry looking chef glares at you. you can go north, east or west\n")

	move=raw_input("which way would you like to go? :")

	if move=="north":
		start()

	elif move=="east":
		bedroom2()

	elif move=="west":
		dining()

	else:
		print("\nyou cannot go that way\n")
		kitchen()

def bedroom1():
	print("\nyou are in the master bedroom, there is a large four poster bed in the center of the room. you can go north, west or south\n")

	move=raw_input("which way would you like to go? :")

	if move=="north":
		hall()

	elif move=="south":
		bedroom2()

	elif move=="west":
		start()

	else:
		print("\nyou cannot go that way\n")
		bedroom1()

def bedroom2():
	print("\nyou are in the servants quarters, not much to see here. you can go north or west\n")

	move=raw_input("which way would you like to go? :")

	if move=="north":
		bedroom1()

	elif move=="west":
		kitchen()

	else:
		print("\nyou cannot go that way\n")
		bedroom2()

intro()
start()

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am
Contact: Website

Re: simple adventure game.

Fri Oct 26, 2012 1:14 pm

@emba: Those endless recursive calls limit the runtime as the program will stop with a ``RuntimeError: maximum recursion depth exceeded`` eventually. I would go so far to say it is broken. This recursive programming style is not made for programming languages where the language specification doesn't promise tail call optimisation.

Code: Select all

while not self.asleep():
    sheep += 1

emba
Posts: 19
Joined: Sun Jul 29, 2012 8:54 pm

Re: simple adventure game.

Mon Oct 29, 2012 7:10 pm

to be honest half of what youve posted is basically greek to me at the moment, as i said it was never meant to be anything serious just something to to do with the basic commands id learned already to help me remember them a bit better.

the end result was what i wanted, it made me repeat what id learned already and it worked when i executed it so i was happy with that. im sure ill learn all about "recursive programming" and "tail call optimization" and such when the time comes, as it is time hasnt been on my side so all ive really learned is some basics from free online resources, by no means am i calling myself an experienced coder or writing anything id actually publish to anyone (except that i did above! but i did say it was sloppy at best)

im sure ill learn about those problems and more further down the line as i use it more, aside from a bit of simple scripting i havent really done any proper coding since the spectrum days so its a steep learning curve at the mo

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am
Contact: Website

Re: simple adventure game.

Thu Nov 01, 2012 9:02 am

@emba: You are using function calls somewhat as BASIC GOTO, as jumps to named code sections, but they are more like GOSUB. The code seems to do what you want, but only for a limited time, then the program will stop with an error. So while you can postpone learning about recursion, you should not use it until you know more about it.

A function call does not just jump to the functions code but also expects that function to return eventually. That is why all the local variables of the calling function and a reference to the code after the call are still kept around. This data is stored on a stack structure which overflows when nesting too many function calls.

Tail call optimization (TCO) is a technique of compilers to recognize cases where there is no code to be executed when the recursive call returns and therefore there is no need to keep anything on the stack for the call. The call becomes a simple jump instruction then. TCO is usually something functional programming languages guarantee because they have no loops — repetitions *have* to be expressed as recursion at some point. Most imperative programming languages don't guarantee TCO. So it is not safe to (ab)use function calls as jumps in those languages.

Code: Select all

while not self.asleep():
    sheep += 1

rpiguy
Posts: 25
Joined: Mon Mar 26, 2012 10:01 pm

Re: simple adventure game.

Sun Nov 11, 2012 10:53 pm

If you're interested in learning Python, a simple adventure game is a great project.

If you're interested in writing a massive, complex adventure game, your best bet is to use a language specifically designed for that purpose, such as Inform 7 [1]. I've been meaning to learn Inform 7 one of these days. Intriguingly, it's the most English-like programming language I've ever seen. Here's a small example copy-pasted from Wikipedia [2]:

Code: Select all

"Hello Deductible" by "I.F. Author"

The story headline is "An Interactive Example".

The Living Room is a room. "A comfortably furnished living room."
The Kitchen is north of the Living Room.
The Front Door is south of the Living Room.
The Front Door is a door. The Front Door is closed and locked.

The insurance salesman is a man in the Living Room. "An insurance salesman in a tacky polyester suit. He seems eager to speak to you." Understand "man" as the insurance salesman.

A briefcase is carried by the insurance salesman. The description is "A slightly worn, black briefcase." Understand "case" as the briefcase.

The insurance paperwork is in the briefcase. The description is "Page after page of small legalese." Understand "papers" or "documents" or "forms" as the paperwork.

Instead of listening to the insurance salesman for the first time:
say "The salesman bores you with a discussion of life insurance policies. From his briefcase he pulls some paperwork which he hands to you.";
move the insurance paperwork to the player.
[1] http://inform7.com/
[2] http://en.wikipedia.org/wiki/Inform

User avatar
jburke71
Posts: 14
Joined: Tue Oct 23, 2012 2:16 am

Re: simple adventure game.

Wed Nov 14, 2012 7:19 pm

Greetings Blackjack,

I appreciate your response to my posting. However, I think you misunderstand the purpose of the code I posted. The code was intended as a teaching tool for beginners to introduce syntax and basic object types. With that in mind, I will address your responses below...
BlackJack wrote:@jburke71: The program makes very little use of Python. It almost looks like it is written with another programming language in mind.
I disagree. It is indeed Python as it uses Python syntax, and runs in the Python interpreter. And, as I said earlier, it was written with simplicity in mind.
BlackJack wrote:No classes, not even dictionaries are used. But lists with magical numerical indices as constants to make it a little more readable instead. Truth values and `None` are also not used. Lists are made to look like index 1 based with dummy entries at index 0.
Correct. I stayed away from more advanced Python structures and objects on purpose. I'm certainly not going to start off a new programmer with a discussion of object oriented programming. As such, Lists are a very common data object, and manipulation of lists, in my opinion, should come before dictionaries. Readability was a high on my list of concepts. After all, if it's not readable, then it's not good programming.

The lack of truth values (and "None") are due to my own idiosyncrasies. The dummy entry was created on purpose to simplify certain areas of the code, and this is the one part that I consider bad programming, but it was done intentionally, and would be readily pointed out to beginning programmers.
BlackJack wrote:In addition to the clearly documented global variables, `cmd`, `obj`, and `action` are also global, because the main code is on module level and not in an own function.
This is a good point, I will add "cmd", "obj" and "action" to the global variable list.
BlackJack wrote:This functions is usually called `main` and is invoked with the following idiom:

Code: Select all

if __name__ == '__main__':
    main()
Now the module can be imported by other modules or in an interactive Python shell without the main code starting to run. This is helpful to test functions manually or with automated tests, or reuse them in other modules.
This module was not intended to be imported by other modules. It's a simple, proceedural based script designed to teach syntax and beginning data types in a fun, creative way. This is a Pythonism that I believe will confuse beginners.
BlackJack wrote:The comments above functions explaining what the functions do are better written as docstrings. Then they can be used by tools and IDEs to create documentation in various formats or to show tool tips in an editor.
This is your opinion, and while I agree that docstrings are very important in modules that are intended to imported, this is not the case. I'll stick to my own commenting methods since I find them more readable in the long run.
BlackJack wrote:When a name has to be bound to some value but there is no meaningful value at that time, don't bind it to the empty string unless that would be a valid value later on in the program. The value for ”nothing” in Python is `None`.
Please cite your source and reason for this. It's not that I don't agree with you on this point, but this is the first time I've heard it. Aside from Python having to collect the empty string when the GC runs at a later time, I really can't see any reasn why this should be avoided. I'm certainly not a python-guru, so I'm curious about this statement.
BlackJack wrote:Instead of 0 and 1 for meaning true and false the decicated boolean type `bool` and its literals `False` and `True` should be used. The the reader does not have to wonder if there are other valid values besides 0 and 1 for a variable then.
Well, there are other valid values for true/false testing (for a list go here: http://docs.python.org/3.1/library/stdtypes.html), so hiding this fact by always using True/False or None, may be doing a disservice to the beginner. I think I'd prefer to not to obfuscate this. However, this also highlights my own idiosyncrasies as I said earlier.
BlackJack wrote:A default argument for `cmd_look()` would make it unneccesary to call it explicitly with an empty string, which looks a bit odd to me.
Default arguments are not a beginning topic in my opinion, so I stayed away from them.
BlackJack wrote:Every value in Python has a truth value that is used in places like the result of the condition after ``if``. For sequence types it is usually `False` if the sequence is empty and `True` otherwise. So expressions like ``if len(sequence) > 0:`` can be written as ``if sequence:``.
Again, something that I think would be confusing to beginners, so I wouldn't change this.
BlackJack wrote:``if`` is not a function so it should not be written as if it where. There is no need for parenthesis around the conditional expression and there should be a space after the keyword.
True, but it's an old habit of mine, and not likely to change. Syntatically, it's not incorrect, and I find it more readable.
BlackJack wrote:If there is no concrete exception(s) given after an ``except`` statement, it handles *all* possible exceptions. That's is usually a bad idea because it will also handle exceptions the programmer may not have thought of, just like the ones he expected. Code sensible for a `ValueError` might not be suited well for a `MemoryError` or a `NameError`. Bare excepts should be used seldom, and if they are they should re-raise the exception or log it somewhere so the programmer has a chance to know which exceptions are going through this handler. Otherwise hunting bugs could get *very* frustrating.
Agreed. Your change to ValueError is a good one.
BlackJack wrote:If the game state would be combined into an object it would be easier to pass it around and to unify the signatures of the command functions so the ``if``/``elif`` construct in `main()` could be replaced by a mapping from command to function. Or by a naming convention for command methods and a dispatcher mapping the user input to the corresponding method.
All advanced topics that I wouldn't start a beginner on.

Regards.

User avatar
jburke71
Posts: 14
Joined: Tue Oct 23, 2012 2:16 am

Re: simple adventure game.

Wed Nov 14, 2012 7:38 pm

emba wrote:to be honest half of what youve posted is basically greek to me at the moment, as i said it was never meant to be anything serious just something to to do with the basic commands id learned already to help me remember them a bit better.
As a beginner, I think your solution was rather creative. However, if you're curious, your program will crash after they player has made 1000 moves. This is the limit on the number of time a function can be called while in another function. In any event, you'll pick up the advanced stuff as you go along. Just keep coding, and it'll come together.

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am
Contact: Website

Re: simple adventure game.

Wed Nov 14, 2012 8:36 pm

A programming language is not just syntax. There is also idiomatic usage. Not everything the compiler accepts and the interpreter runs, qualifies as being idiomatic Python. For teaching a language I think it is a good idea to teach idiomatic usage and conventions used by the community.

`cmd`, `action`, and `obj` are not globals. I mean technically they are but they are not used as such. They just pollute the module namespace unneccesarly. This gets confusing when functions use the same names for local variables as it is the case with `obj`.

You are already using functions, so introducing a `main()` function is no additional concept. And as there are functions in the module one could say it is intended to be imported. Especially for beginners it is a nice feature of Python to be able to import their modules in the interpreter and play around ”live” with the functions to test them and to find bugs. Here Docstrings would also be useful because they can be displayed with the `help()` function.

Binding a name in the same context to really different types is confusing. If there is a ``foo = ''`` at the top and somewhere in the function is a ``foo + bar`` expression, most readers would expect that to mean string concatenation. Regardless what code is between those two code fragments. If `foo` and `bar` suddenly are numbers this is surprising.

Almost the same reason for using `True` and `False`. If you see a 0 or a 1 somewhere it is clear there is a number. Other valid values might be 42, -1, or any other number. If it is a `bool` value it's clear that usually just `True` and `False` are valid. It makes the intention clear — this is a truth value, not any other object that just happens to be `True` or `False` in a boolean context. Making the intent clear is not obfuscating IMHO.

``if(condition):`` is more readable because you are used to it. But idiomatic Python uses a space after keywords and no parenthesis around the condition. Same for documenting comments above the ``def`` line of functions and docstrings after it.

Code: Select all

while not self.asleep():
    sheep += 1

Return to “Python”

Who is online

Users browsing this forum: No registered users and 12 guests