Archive for the ‘python’ Tag
time so far: 16 hours
Not much more than another mockup, but I like this one better than having the big sidebar not really doing much. The descriptive style of the header in this one is a bit of an experiment.
Spent a lot of the day working on saving and loading, choosing json instead of the tried and true YAML. Went for a walk in the afternoon and realized that was probably going overboard. Do I even need save games? Probably not, though I want them. In any case it’s worked out now, so I’ll go with it.
Tightened up the design, eliminating the mountain and slum map generation parameters. Also realized I should have a power limiter of some kind for boons, so put in limited uses. As you complete tasks you get more uses; at the same time the chance of more dangerous enemies and traps increases. I don’t want this to be a difficulty scale per se, though — there still should be low power enemies you can just blow away later in the game.
addenda: I couldn’t resist a couple of more hours post-post. For some reason I’ve decided to try a publisher/subscriber for the first time with this game. I got the game components to register handlers with an event broadcaster, and now keypresses send an event to the broadcaster, which propagates them to the appropriate handlers.
time so far: 8 hours
Nothing playable yet; just created a mockup in libtcod after making some notes and sketching out the code structure with Leo. The extent of my design:
I had the basic idea a couple of weeks ago and I figured the rest out in the notebook this morning. You play a bidden, a creature brought into a fantasy world to fulfill the wishes of the summoner.
In trying to keep this simple, yet still interesting and a roguelike, I’m planning the following:
* summoners bind you to a task, either to hunt (kill something), to seek (get or activate something), or to bear (deliver something).
* these summoners represent groups of different interests in the game world, but this aspect will be largely cosmetic.
* tasks take place in a terrain (valley, mountain, desert, cave — mostly cosmetic), and a place (tower, ruin, temple, labyrinth, slum) that influences what creatures are encountered (elevated, ancient, divine, beastly, mutated).
* Your bidden can wield one weapon and wear one piece of armor; these will be somewhat unlike conventional weaponry (in a cosmetic sense), and generated.
* Your bidden can have two boons (powers), with one active at a time. I have six boons planned, three each for combat and movement.
* Your bidden also can be warped. Each warp changes the active boon. I’ll have two warps, so counting the unwarped state, there are 18 possible powers. Particular victories and items/locations create warps.
* all attacks hit automatically, with a chance to destroy weaponry (based on the weapon/armor involved), or inflict a wound. The bidden can take five wounds before death. Particular items and victories heal wounds, and there’s a heal up when the bidden completes its task.
* the command interface:
* cardinal movement only
* use boon, ‘x [direction]‘
* pick up or manipulate, ‘ [ctrl + direction]‘
* cancel or main menu, ‘[escape]‘
* confirm, ‘[space or return] ‘
* switch active boon, ‘[shift]‘
* wait, ‘.’
* scroll message history, ‘ [pageup or pagedown] ‘
* command help, ‘?’
* bump to attack or open doors (maybe open doors is configurable)
* a list of achievements (tasks completed)
* a victory file
* a death file
So….probably way too ambitious! We’ll see.
I’ll try to summarize what I’ve learned over the last week or so. What I was looking for was a simple way to get into this stuff, and as I discovered it’s not so simple, for a hobbyist like myself, once you combine two or three frameworks in a project. Maybe this post will help someone else in the same situation.
Python already was the choice for the server-side script. What I then needed was a way for the browser to communicate with the script. There are literally dozens of ways to do this, and the Python wiki has a good page on web programming. I also found this conversation springing from a post by the BDFL at Artima a good read, though it’s a few years old now.
I decided to keep it simple (though I still want to learn frameworks like Django, TurboGears, etcetera at some point in the future). I considered looking into using CGI with Python, but then I found web.py, a lightweight web framework, and liked it immediately.
The web.py framework can run its own basic web server or interface with others such as Apache or lighttpd. It includes a simple HTML template language that basically is just HTML + Python. It works with databases or not. In short it has all you need to get things running quickly.
OK, first let’s look at the basic project structure. Note that I’m using web.py’s templates, though this isn’t required:
Our app.py is the web.py script that controls our app. It looks like this:
import web def make_text(string): return string urls = ('/', 'tutorial') render = web.template.render('templates/') app = web.application(urls, globals()) my_form = web.form.Form( web.form.Textbox('', class_='textfield', id='textfield'), ) class tutorial: def GET(self): form = my_form() return render.tutorial(form, "Your text goes here.") def POST(self): form = my_form() form.validates() s = form.value['textfield'] return make_text(s) if __name__ == '__main__': app.run()
Other than the simple echo function defined for this tutorial, there are some critical parts to the code above, so let’s look at it more closely.
First of all web.py will map the URL to classes in the script. That’s why we have urls = (‘/’, ‘tutorial’), which says map the root URL address to what happens in the tutorial class defined later.
Second, if we’re using templates we tell web.py where to find our HTML templates with render = web.template.render(‘templates/’). The my_form creates an HTML text entry box that we’ll render later. We give it an empty string for a name simply for aesthetic reasons (otherwise you’ll see it on the HTML page).
Finally we define the tutorial class. We create the GET and POST methods ( web.py intentionally requires this)to define the behaviour of our web.py app in response to browser client requests. Looking at GET:
def GET(self): form = my_form() return render.tutorial(form, "Your text goes here.")
Defining form = my_form() gives us a copy of the form instance we created earlier. The method render.tutorial() returns a rendering of the tutorial HTML template (using the same name tutorial here in the class definition, the call, and eventually the HTML template are important here!), passing the arguments form and the text string to the template at the same time.
Now for POST:
def POST(self): form = my_form() form.validates() s = form.value['textfield'] return make_text(s)
Again using a copy of the form instance, we automatically validate it (simply because I haven’t learned how to validate forms yet!). We can then access the value of our textfield box with form.value['textfield'], and send it to our echo function defined earlier.
That’s all you need in the app.py, now to look at the HTML template (located in the templates directory):
(we’ll see how WP handles the syntax styling with that…)
There’s a little more going on here so let’s take it one step at a time.
In a web.py template the first line of the file defines what data is passed to the template by web.py. So here we have $def with (form, text), which as you’ll recall parallels the call we made in the GET method above. So when a client requests this page, those arguments are passed along.
It’s actually pretty simple. All this does is send what’s in the textfield box to the server when the button is clicked. If the action is successful (i.e. the server accepts the input data and returns it OK), jQuery replaces what’s in the foo element with the data returned. This uses the ajax() function — so the page doesn’t reload (it’s not really AJAX as you may realize, since there’s no XML exchanged — that’s OK, I don’t like XML anyway! )
I’ll point out some conventions that a beginner should know. Saying “.button” just refers to the element with the button class (defined in our app.py script, look above). Saying “input#textfield” refers to the input element with the CSS id of textfield (also defined in app.py).
The success function passed through ajax() is called if the action is successful; the part with hide() and then fadeIn() is just a prettier way of displaying the new text on the HTML page (otherwise, it just replaces it with no transition effect).
Lastly, returning false prevents the page from reloading.
Moving on to the body of the HTML page:
<body> <br> <form class="form" method="post"> $:form.render() <input class="button" type="submit" value="send"/> </form> <br><br> <span id="foo">$text</span> </body>
All we do here is enclose a rendering of the textfield and button in form tags. This isn’t totally necessary, but as I discovered, without the form tags pressing return in the textfield box doesn’t activate the button (i.e. you have to click on the button). Apparently you can process keyboard events with jQuery but this seemed simpler.
An additional note about $ is that you need to use $: if the expression will contain some HTML that you don’t want escaped, as we do with $:form.render.
The foo span element is just a basic container that the jQuery function can replace with the new text.
There are some obvious improvements to be made here. First of all there’s nothing stopping the user from clicking the button multiple times and sending multiple POST requests (probably not a desirable situation). There also isn’t any user session setup or form validation. In any case these things are next in my list to learn, but my experience with web.py and jQuery has been great so far. I’m looking forward to more!
Also, the documentation for web.py and jQuery are good, so check those out for more information on their APIs.
I’d like to thank the members of the web.py mailing list, particularly Branko Vukelić, for their feedback on an earlier draft of this post. Naturally all errors are (most definitely) my own.
If you’d like a zipped copy of the files in this tutorial, you can get it here. Run app.py and then point your browser to http://localhost:8080 (on Windows; use the appropriate address for your setup).
I found a neat little site for game development in Python, PyedPyers. Apparently it’s been around for more than a year (and this is the first time I’ve heard of it). The neat thing about it is it has some additional resources besides just a forum, such as a spot to host projects. It’s rather small at the moment and very quiet, but perhaps with a core group of some active people there some interesting stuff could happen. Check it out!
Recently I started reading about livecoding. I was aware of it in the context of music performance, but in regard to games it might be more accurate to call it code reloading or simply interactive programming, but of course ‘livecoding’ is more catchy. Regardless of what you call it, it’s a lot of fun, and if you like programming as an iterative process as I do, it’s just fantastic.
A few years ago there were a few articles about livecoding and Python, which you can track down through this post by Richard Tew. He’s the author of the livecoding module which I’ve started to use with pyglet.
The basic idea is that you can modify a game’s code as the game is running; this isn’t any different than what you can do with many game development frameworks and some mud servers (mush softcode allows this, for example, and I believe LPMud has this as well), but the livecoding module lets you do it simply with your text editor of choice. Some IF language development environments like the Inform 7 IDE and TAD 3′s Workbench have a similar function with their skein and replay features, but livecoding is much more immediate and in the flow of the game itself.
The livecoding module achieves this by managing which files are to be tracked for changes, and then executing a file’s contents over again when you change it. As the programmer you need to structure your program so that you’re cognizant of what is in the global scope, as executing that code again will clash with the previous version (for example, you don’t want to re-execute a file that instantiates the pyglet window, unless you delete that window from the global scope). You also want to manage how instances of a class know about changes to their class; the module won’t automatically update old instances, but you can reassign the class to the instance to get the new changes (this at least is what I’m doing currently — I’m still learning how this works).
Here’s a simple example — get the livecoding module and put it in a new directory. Then create a code directory within that new directory. In my code directory I put two files, an initialization.py and a classes.py. Here’s initialization:
import pyglet window = pyglet.window.Window(width=640, height=480, visible=True, caption='livecoding')
Remember that the important thing is to separate things in the global scope from the things you’ll change, to avoid clashes with the running game when you change things. Now in classes.py I put this:
import pyglet class Hero(pyglet.sprite.Sprite): def __init__(self): pattern = pyglet.image.SolidColorImagePattern(color=(255, 255, 255, 255)) solid = pattern.create_image(200, 100) image = solid.get_texture() super(Hero, self).__init__(solid) def set_pos(self): self.x = 400 self.y = 200
In the main directory you’ll have a script to run the game, which I just called run.py.
import pyglet from livecoding import reloader cr = reloader.CodeReloader() cr.AddDirectory("server", "code") import server hero = server.Hero() @server.window.event def on_draw(): hero.__class__ = server.Hero hero.set_pos() server.window.clear() hero.draw() if __name__ == '__main__': pyglet.app.run()
The reloader keeps track of changes to any file in the code directory (of course, you can call the code directory anything you want). This replaces the normal Python import statement for those files. It’s similar in a way to pyglet’s method of managing data files.
You see that the game creates the hero object, and in this hackish example I reset the class of the hero on every frame. That’s to account for the possibility of changes to the Hero class (in classes.py). What you do then is execute run.py, and while the pyglet window is sitting there, change the set_pos method of Hero in classes.py. Then mouse over the pyglet window or alt-tab to it, and see the changes!
In a less trivial example you could move the class reset to an update function called on a pyglet timer. That then opens up, fairly transparently, making changes to the game process and data as you’re playing the game.
The only sticking point so far is managing the structure of the program to avoid any weird errors in the global scope. Also from reading some of those other posts I linked above it seems like I might run into more problems with things like animation as I go forward — but we’ll see what happens!
Originally when I started the IF with Python series I thought I would methodically go through each Python IF lib, write a small game, compare and contrast, and so on. I still plan to do that. But as might be expected, I’ve just gone down a rabbit hole, so I’d like to see where it leads.
I’ve always been interested in LP, but now I suspect it was for the wrong reasons — or at least for reasons that aren’t really fundamental to literate programming. I’d checked out PyLit a bit, and my experiment with Pawsy was in a similar vein of trying to find out how to weave IF’s prose with its code in a pleasing way. I was attracted to LP because it seeemd like ‘prose, with code’. Since I like prose, I thought, hey, this might be for me.
But I had some free time today, so I finally opened up the Leo zip I downloaded a few weeks ago. Now I’m not so sure that LP is about ‘prose with code’ — at least with Leo, LP seems to say instead that a program is not just a set of code instructions, and not just a commented explanation of itself, or that thing combined — but a program is a structure that operates on multiple levels, and restricting yourself to one view of that structure, or to one way of investigating, exploring, and manipulating that structure, is an extremely limiting way to work.
I know I’m not being that coherent, as I’ve been working with Leo for less than a day. Maybe some examples will help. Here’s a screen of Leo with a pyf IF project:
Here are some closeups — first, the top left pane, the outline view.
The right hand pane is simply a log window — the bottom pane is the code view:
The @others keyword there is a Leo directive that says, ‘insert here all nodes below this one in the outline’.
So what you can do here is create the world model structure of your game, and in the top level, organize these nodes into your program structure. Already I find that this makes for much more readable code.
In the outline view you’ll see node names (headlines) that begin with @thin. This is one way of telling Leo to write that node to an external file; when Leo writes the file it expands all @others type directives, so basically you end up with a file that assembles all the code in your nodes into a single game file ready to run.
My workflow, then, involves creating the Leo outline, and then running from within SciTE the assembled program. In an ideal world I’d be able to do this directly from within Leo; Leo stores its outline files in a .leo directory, so I guess it’s possible to run the Leo outline from the project directory instead, in order to have easy access to any modules that I’m importing into the game but are not available to the whole system from site-packages (like the pyf module, for example).
So far this may not seem much different from an IDE with class/function browsing, or an editor with multiple levels of code folding. However there are some important differences.
First of all you can put anything you want in a Leo outline — you can mix Python code, xml, html, plain text, etcetera, and choose what goes into the assembled file that is the actual game source. In this way a single outline can hold many disparate components you’re using for your game, but still produce the working game code as you need it.
Second, you can do more with the outline structure than you might expect. You can clone nodes in Leo, such that a single chunk of code or text can exist at multiple points in the outline hierarchy. When you change the node at one of these points, all of its clone nodes change as well.
In other words you have multiple views of the same piece of data. When I first read about that I immediately thought this would be a great way to write IF conversation. Of course I have no idea if that will work in practice, but imagine writing an IF conversation as an outline, where you can easily reuse quips as necessary. Compare that to a flat view of conversation, as you’d find in any IF source. The question is whether the additional overhead of the outline is a net plus over a conventional layout, but intuitively it seems like this could really work.
Anyway, the real test will be to write a non-trivial game in pyf using Leo, so that’s my next step. I have to say I’ve never been this excited about a programming tool though. Most of the IDEs I’ve checked out are not that inspiring; this feels completely different.
From: Neil Cerutti
Date: Mon, 13 Aug 2007 13:37:26 GMT
Local: Mon, Aug 13 2007 5:37 am
Subject: Re: Adventure-Engines in Python
As far as I know, no full game was ever written in PAWS. Once you try it, you’ll possibly see why no interactive fiction system distributed as a library for a general-purpose language has ever caught on. Every system that’s enjoyed even moderate success has been a language+library implementation.
This is my jumping off point for an exploration of writing IF with Python.
Every so often on RAIF someone will make a comment about why people “don’t just use the power of a general programming language” instead of bothering with boutique languages like Inform and Tads. I think it’s clear that IF languages solve a problem probably unique to writing iF — how to weave prose and code — not to mention that their years of development have resulted in very strong libraries that make writing IF much easier.
Nevertheless, as someone who enjoys IF, and programming with Python, I want to look at this more closely. More after the cut.