Skip to content

Latest commit

 

History

History
238 lines (180 loc) · 10.6 KB

ex52.rst

File metadata and controls

238 lines (180 loc) · 10.6 KB

Exercise 52: The Start Of Your Web Game

We're coming to the end of the book, and in this exercise I'm going to really challenge you. When you're done, you'll be a reasonably competent Python beginner. You'll still need to go through a few more books and write a couple more projects, but you'll have the skills to complete them. The only thing in your way will be time, motivation, and resources.

In this exercise, we won't make a complete game, but instead we'll make an "engine" that can run the game from Exercise 42 in the browser. This will involve refactoring Exercise 42, mixing in the structure from Exercise 47, adding automated tests, and finally creating a web engine that can run the games.

This exercise will be huge, and I predict you could spend anywhere from a week to months on it before moving on. It's best to attack it in little chunks and do a bit a night, taking your time to make everything work before moving on.

Refactoring The Exercise 42 Game

You've been altering the gothonweb project for two exercises and you'll do it one more time in this exercise. The skill you're learning is called "refactoring", or as I like to call it, "fixing stuff". Refactoring is a term programmers use to describe the process of taking old code, and changing it to have new features or just to clean it up. You've been doing this without even knowing it, as it's second nature to building software.

What you'll do in this part is take the ideas from Exercise 47 of a testable "map" of Rooms, and the game from Exercise 42, and combine them together to create a new game structure. It will have the same content, just "refactored" to have a better structure.

First step is to grab the code from ex47/game.py and copy it to gothonweb/map.py and copy tests/ex47_tests.py file to tests/map_tests.py and run nosetests again to make sure it keeps working.

Note

From now on I won't show you the output of a test run, just assume that you should be doing it and it'll look like the above unless you have an error.

Once you have the code from Exercise 47 copied over, it's time to refactor it to have the Exercise 42 map in it. I'm going to start off by laying down the basic structure, and then you'll have an assignment to make the map.py file and the map_tests.py file complete.

First thing to do is lay out the basic structure of the map using the Room class as it is now:

.. literalinclude:: ex/ex52/gothonweb/gothonweb/map.py
    :linenos:

You'll notice that there are a couple of problems with our Room class and this map:

  1. We have to put the text that was in the if-else clauses that got printed before entering a room as part of each room. This means you can't shuffle the map around which would be nice. You'll be fixing that up in this exercise.
  2. There are parts in the original game where we ran code that determined things like the bomb's keypad code, or the right pod. In this game we just pick some defaults and go with it, but later you'll be given extra credit to make this work again.
  3. I've just made a generic_death ending for all of the bad decisions, which you'll have to finish for me. You'll need to go back through and add in all the original endings and make sure they work.
  4. I've got a new kind of transition labeled "*" that will be used for a "catch-all" action in the engine.

Once you've got that basically written out, here's the new automated test tests/map_test.py that you should have to get yourself started:

.. literalinclude:: ex/ex52/gothonweb/tests/map_tests.py
    :linenos:

Your task in this part of the exercise is to complete the map, and make the automated test completely validate the whole map. This includes fixing all the generic_death objects to be real endings. Make sure this works really well and that your test is as complete as possible because we'll be changing this map later and you'll use the tests to make sure it keeps working.

Sessions And Tracking Users

At a certain point in your web application you'll need to keep track of some information and associate it with the user's browser. The web (because of HTTP) is what we like to call "stateless", which means each request you make is independent of any other requests being made. If you request page A, put in some data, and click a link to page B, all the data you sent to page A just disappears.

The solution to this is to create a little data store (usually in a database or on the disk) that uses a number unique to each browser to keep track of what that browser was doing. In the little lpthw.web framework it's fairly easy, and there's an example showing how it's done:

.. literalinclude:: ex/ex52/gothonweb/session_sample.py
    :linenos:

To make this work, you need to create a sessions/ directory where the application can put session storage. Do that, run this application and go to /count. Hit refresh and watch the counter go up. Close the browser and it forgets who you are, which is what we want for the game. There's a way to make the browser remember forever, but that makes testing and development harder. If you then go to /reset, and back to /count you can see your counter reset because you've killed the session.

Take the time to understand this code so you can see how the session starts off with the count equal to 0. Also try looking at the files in sessions/ to see if you can open them up. Here's a Python session where I open up one and decode it:

>>> import pickle
>>> import base64
>>> base64.b64decode(open("sessions/XXXXX").read())
"(dp1\nS'count'\np2\nI1\nsS'ip'\np3\nV127.0.0.1\np4\nsS'session_id'\np5\nS'XXXX'\np6\ns."
>>>
>>> x = base64.b64decode(open("sessions/XXXXX").read())
>>>
>>> pickle.loads(x)
{'count': 1, 'ip': u'127.0.0.1', 'session_id': 'XXXXX'}

The sessions are really just dictionaries that get written to disk using pickle and base64 libraries. There are probably as many ways to store and manage sessions as there are web frameworks, so it's not too important to know how these work. It does help if you need to debug the session or potentially clean them out.

Creating An Engine

You should have your game map working and a good unit test for it. I now want to make a simple little game engine that will run the rooms, collect input from the player, and keep track of where a play is in the game. We'll be using the sessions you just learned to make a simple game engine that will:

  1. Start a new game for new users.
  2. Present the room to the user.
  3. Take input from the user.
  4. Run their input through the game.
  5. Display the results and keep going until they die.

To do this, you're going to take the trusty bin/app.py you've been hacking on and create a fully working, session based, game engine. The catch is I'm going to make a very simple one with basic HTML files, and it'll be up to you to complete it. Here's the base engine:

.. literalinclude:: ex/ex52/gothonweb/bin/app.py
    :linenos:

There are even more new things in this script, but amazingly it's an entire web based game engine in a small file. The biggest "hack" in the script are the lines that bring the sessions back, which is needed so that debug mode reloading works. Otherwise, each time you hit refresh the sessions will disappear and the game won't work.

Before you run bin/app.py you need to change your PYTHONPATH environment variable. Don't know what that is? I know, it's kind of dumb you have to learn what this is to run even basic Python programs, but that's how Python people like things.

In your terminal, type:

export PYTHONPATH=$PYTHONPATH:.

On Windows do:

set PYTHONPATH=%PYTHONPATH%;.

You should only have to do it once per shell session, but if you get an import error, then you probably need to do this or you did it wrong.

You should next delete templates/hello_form.html and templates/index.html and create the two templates mentioned in the above code. Here's a very simple templates/show_room.html:

.. literalinclude:: ex/ex52/gothonweb/templates/show_room.html
    :linenos:

That is the template to show a room as you travel through the game. Next you need one to tell someone they died in the case that they got to the end of the map on accident, which is templates/you_died.html:

.. literalinclude:: ex/ex52/gothonweb/templates/you_died.html
    :linenos:

With those in place, you should now be able to do the following:

  1. Get the test tests/app_tests.py working again so that you are testing the game. You won't be able to do much more than a few clicks in the game because of sessions, but you should be able to do some basics.
  2. Remove the sessions/* files and make sure you've started over.
  3. Run the python bin/app.py script and test out the game.

You should be able to refresh and fix the game like normal, and work with the game HTML and engine until it does all the things you want it to do.

Your Final Exam

Do you feel like this was a huge amount of information thrown at you all at once? Good, I want you to have something to tinker with while you build your skills. To complete this exercise, I'm going to give you a final set of exercises for you to complete on your own. You'll notice that what you've written so far isn't very well built, it is just a first version of the code. Your task now is to make the game more complete by doing these things:

  1. Fix all the bugs I mention in the code, and any that I didn't mention. If you find new bugs, let me know.
  2. Improve all of the automated tests so that you test more of the application and get to a point where you use a test rather than your browser to check the application while you work.
  3. Make the HTML look better.
  4. Research logins and create a signup system for the application, so people can have logins and high scores.
  5. Complete the game map, making it as large and feature complete as possible.
  6. Give people a "help" system that lets them ask what they can do at each room in the game.
  7. Add any other features you can think of to the game.
  8. Create several "maps" and let people choose a game they want to run. Your bin/app.py engine should be able to run any map of rooms you give it, so you can support multiple games.
  9. Finally, use what you learned in Exercises 48 and 49 to create a better input processor. You have most of the code necessary, you just need to improve the grammar and hook it up to your input form and the GameEngine.

Good luck!