Learn » Intro-Projects » Python Dojo - Text Adventure Game.md

Coding Dojo June 2015

This dojo will be done a bit differently. The problem will be more practical, and unlike a normal dojo we encourage you to keep your solution to project I for project II

You will be paired up to work on Phase I from 7 to 7:45. We will then have a 15 minute group discussion, change partners, and either work more on project I or work on project II.

Everyone one is at different level. If you can't figure something out together, ask another pair or ask the facilitators. The idea is that we will all learn something by the end of the night and learn at our own pace.

Setup pip

This dojo will make use of 3rd party libraries which don't come with Python. There is a whole ecosystem of interesting libraries you can make use of, and once pip is setup it's easy to access them.

However, setting pip up is still a bit challenging. If you run into a problem please ask someone to help you.

The Python 3 Way

To make setting up pip easier it has been included in Python 3 when you install it.

If you are running the Windows the easiest way to get access to pip is to install Python 3.4 using the installer (https://www.python.org/downloads/release/python-343/ - scroll to the bottom of the page).

If you are running other OSes you should be able to something similar. Note you may need to use the command python3 to access a Python 3 prompt.

pip 2014

You can follow the instructions here if you are using a Mac or Linux

https://pip2014.com/

You won't need virtualenv or a C compiler for this dojo, but some libraries you use in the future may require you to be able to compile some code on your laptop.

Assuming the app/script ran correctly, you should be able to test it out. Check the "Success" section below

The pip 2014 app/script makes it so you don't have to pass --user to pip when installing packages, but since not everyone will be install pip this way the flag will be in our examples.

pip Direct

To install pip the direct way try this one-liner on your command prompt under either Mac OS X or Linux:

$ curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | sudo python

This will download the get-pip.py script and run it with Python. On Windows you can download get-pip.py another way and then just execute it with Python.

$ python get-pip.py

If it ran successfully you should be able to test pip by following the instructions in the "Success" section.

This way of installing pip doesn't change other things on your laptop like your PATH. When you install a 3rd party library which provides an executable command with the --user flag you won't be able to run it easily until you modify your PATH. Tonight we won't be installing any libraries which provide a command, but Django's django-admin.py would be an example of command you would want to be able to access after you installed Django.

On Windows another option which should work in a pinch is easy_install pip.

Success

You should be able to run the following without an error:

$ pip install --user flask
...
$ python
...
>>> from flask import Markup
>>> Markup.isdigit(u'23')
True
>>> exit()

Project 1: Text Adventure Game

A text adventure game is a game without fancy graphics. It is completely text-based. A typical game will provide a set of "rooms" for the player to explore. The player will interact with the game by entering simple commands, such as walk north, take book, or swing sword. As the player travels through your game world they should be presented with room descriptions and actions to take. The details are completely up to your imagination. Have fun with it!

Text Input in Python

We recommend you use the cmd library. It provides a command-line interface with some useful functionality, such as tab completion, history browsing, and a built-in help system. You will create a subclass of the Cmd class.

The textwrap library in python provides functionality to wrap lines of text at a desired length. This can be useful for making your commandline game a little nicer to play.

>>> import textwrap
>>> long_text = "The purple dog jumps over the blue moon when the yellow sun is gone for the day. The striped cat just stares, because that's what cats do."
>>> print long_text
The purple dog jumps over the blue moon when the yellow sun is gone for the day. The striped cat just stares, because that's what cats do.
>>> for line in textwrap.wrap(long_text, 50):
...     print(line)
... 
The purple dog jumps over the blue moon when the
yellow sun is gone for the day. The striped cat
just stares, because that's what cats do.
>>>

Goal

Your goal is to write a Python function which you can call with the user's chosen action to get the next step of the scenario. Start by making a file called game.py and copy this into it:

import cmd, textwrap


class GameCmd(cmd.Cmd):
    prompt = '\nWhat would you like to do? > '

    def default(self, arg):
        """The default action to take when the action is not understood."""
        print('I do not understand that command. Type "help" for a list of commands.')

    def do_quit(self, arg):
        """Quit the game."""
        return True


if __name__ == "__main__":
    game = GameCmd()
    game.cmdloop()

Success

If you have successfully implement your function you should be able to do something like this:

$ python game.py

What would you like to do? > help

Documented commands (type help <topic>):
========================================
help  put_on  quit  remove  walk


What would you like to do? > walk north
You walk to the north. It starts snowing.

What would you like to do? > put_on hat
You are getting warmer.

What would you like to do? > walk south
You walk to the south. It is now getting really warm.

What would you like to do? > remove hat
You start to cool off.

What would you like to do? > quit
$

Bonus points for making it fun :).

You can keep running the script to test your progress or you can try Test Driven Development and writing unit tests to guide your coding.

Unit Tests

Start by making a file called test_game.py and copy this into it:

import sys

from unittest import TestCase
from game import GameCmd
from StringIO import StringIO


class QuitTestCase(TestCase):

    def test_do_quit_returns_true(self):
        game = GameCmd()
        result = game.do_quit("")
        self.assertTrue(result)


class DefaultTestCase(TestCase):
    def setUp(self):
        self.output = StringIO()
        # We need to capture standard out to verify the output of print
        sys.stdout = self.output

    def test_default_returns_expected_text(self):
        game = GameCmd()
        game.default("")
        self.assertEqual(
            'I do not understand that command. Type "help" for a list of commands.',
            self.output.getvalue().strip()
        )

Here we have used the concept of monkey patching to switch out the normal stdout with a StringIO that we can easily inspect. You could also use a library such as mock to more safely patch code.

To run the unittests:

$ python -m unittest test_game
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

Project 2: Simple Interactive Website With Forms

Using the command line isn't intuitive to most game players. A more flexible interface is the web, and it's pretty easy to write and run a website on your own computer which interacts with a open data API or even your own laptop.

There are a few different Python web frameworks, but the easiest to get started with is Flask. You can use pip to install Flask

$ pip install --user flask

With Flask installed let's create the file web_interface.py beside game.py. You can start by copying the example flask provides:

# web_interface.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run(debug=True)

You can run your own small self contained website with this command:

$ python web_interface.py
 * Running on http://localhost:5000/

The output of running web_interface.py is a bit strange. When you run it, it doesn't return you to a command prompt, and it doesn't output "Hello World!" Instead it tells you that if you open a browser and put "http://localhost:5000/" into the URL bar you will be taken to a web page generated by this Python script. Try it out!

When you make a change you will want to restart the website, and when you are done you will probably want to stop it. Go to your command line where you see output like this:

$ python web_interface.py 
 * Running on http://127.0.0.1:5000/
127.0.0.1 - - [15/Dec/2014 17:12:48] "GET / HTTP/1.1" 200 -

And type control-c - that is hold down the control key (sometimes abbreviated as ctrl) and type the c key. You know it has worked when you see a prompt again.

$ python web_interface.py 
 * Running on http://127.0.0.1:5000/
127.0.0.1 - - [15/Dec/2014 17:12:48] "GET / HTTP/1.1" 200 -  
^C$

Now when you go back to your web browser and try to reload your page you will see that it can no longer be found.

HTML

The web is built on a markup language called HTML. We won't go into too much detail here, but combining HTML with Python enables you to create dynamic websites. Let's use a triple quoted string in Python to output some HTML.

# web_interface.py
from flask import Flask
app = Flask(__name__)

html = '''
<html>
<head>
<title>My Dynamic Website</title>
</head>
<body>
<h1>{}</h1>
</body>
'''

@app.route("/")
def hello():
    return html.format("Hello World!")

if __name__ == "__main__":
    app.run(debug=True)

Run web_interface.py again and checkout the difference. html holds a string of text that is a template we are using to render our web page. The {} placeholder is populated with the value "Hello World!" via the format function on strings.

Forms

To make your website interactive, you will want to gather user input. For this you will use a form. You will also need to deal with both the GET and POST http methods.

A GET call is sent from the browser when you first load a web page. You will want your program's response to a GET call to be html that contains a form.

The browser sends a POST call when the user submits the form. This is where you will process any data from the user and return updated html.

Update the code in web_interface.py to gather the user's name and say hi to him or her.

from flask import Flask
from flask import request


app = Flask(__name__)

html_get = """
<html>
    <head>
    <title>My Interactive Website</title>
    </head>
    <body>
        <h1>{}</h1>
        <form method="post" action="/">
            <p><label>What is your name?</label> <input type="text" name="name" required></p>
            <p><button type="submit">Send</button></p>
        </form>
    </body>
</html>
"""

html_post = """
<html>
    <head>
    <title>My Interactive Website</title>
    </head>
    <body>
        <h1>{}</h1>
    </body>
</html>
"""

@app.route("/", methods=['GET', 'POST'])
def hello():
    if request.method == 'POST':
        welcome_text = "Hello {}!".format(request.form['name'])
        return html_post.format(welcome_text)
    else:
        return html_get.format("Hello World!")


if __name__ == "__main__":
    app.run(debug=True)

Bring it together

Your final task is to take your GameCmd class from Project 1 and combine it with your interactive website. Make a website which will allow your users to play the game.

Ideas for further development:

  • Investigate using templates instead of including html in the python code. Flask works with the Jinja2 templating language.
  • Investigate using a form library for added functionality and ease of use. Flask-WTF is a popular form library.
  • Add validation of the user input. It is generally unsafe to just blindly work with data submitted through web forms.
  • Use sessions or cookies to remember the state of the game between commands