Is it bad when you put off writing about putting things off?
I really ought to get back to working on GSL. I’m using it as an excuse to go to Protospiel, even though I haven’t been doing game design proper for a while.
I’ve had the idea rolling around in my head for a while that I need to find some like-minded society. Protospiel is the best concentration I’ve found so far, though certainly no one could accuse me of trying too hard.
I’m also picking FutureRuby to try and get in touch with the programming elements. It’s not a cheap way to do it, for certain, but they ruby community, judging from the online conference videos, has large concentration of artistic, fun-loving souls.
I ought to look for more local groups as well, but I don’t really know where to start – short of meetup.com, for which the nearest groups are invariably in Chicago.
However, I’ve got a bit of problem with GSL. I’ve finally got to a point where my sample game is mostly working – with a little help given to the AI. The problem it has started taking a perceptible amount of time to run, and I hate waiting. I also expect things to only get slower as I do more analysis and add features.
I’ve played around with ruby-prof a little bit. Unfortunately, the problems are essentially diffuse – no one method leaps out as a problem point. Some stack traces confirm the obvious suspicions, however.
Examining all future states absolutely stinks from a performance perspective. And I’m only going one level deep – the lack of foresight explains why the computer players need some crutches to avoid painfully boneheaded plays. It’s the most straightforward way to handle it, and I’m in the uncomfortable position of trying to handle the problem in it’s full generality. One of the stats I’ve got is that in a typical run, over 6000 states are considered, but only around 100 of them might become ‘real’.
It’s spending a lot of time in Yggdrasil, which is sort of a little mini-database. Unfortunately, I’m storing every important piece of game state there, and accessing it ‘live’. Constantly jumping up and down the timeline defeats any hope of optimizing it much. I might be able to collapse a few layers internally for a small constant improvement.
One candidate is the Passport class. It’s sole purpose is combine an object id with a field name and look them up in Yggdrasil, the rest is overhead – init, print, etc. I actually starting rolling it into Citizen when I ran into the syntax problem. Having Passport as a separate class allows me to use the “world[:symbol] = x” notation. Collapsing it into Citizen, I’d have to use something like “set :symbol, x”, which obfuscates the assignment nature a little bit. I might also use attr-like methods; those are currently only used for public attributes, so I’d have to work out visibility management.
Another candidate for merging is State/World. I might be able to reduce State to a hash (it contains a hash) managed by World, removing one level of method calls. I still have to examine this more looking for consequences.
I’ve also been pondering if other representations for Yggdrasil might be more efficient. I’ve done some very simplistic micro-benchmarks, and it appears that I could get at most a (small) multiple by doing this, so it’s not worth the conceptual complexity.
Of course there is the ultimate option of writing the critical paths in C, but I don’t considered the design nearly settled enough for that.
Another sort of nuclear option is try and get it to generate a more efficient program instead of executing directly. It seems that this would require some high level reasoning about the actions and their effects. While I might theoretically execute an action in an alternate environment to examine it’s effects, thee is one fatal flaw – actions are arbitrary code, so they might branch and leave possible side effects unanalyzed.
At one point I was seriously considering re-writing in Python to use a cell based library. Then I realized that while it could handle basic rollbacks, it couldn’t handle hypothetical states. It could avoid illegal plays, but not bad ones.
A few small changes made GSL Ruby 1.9 compatible. 1.9 uses a virtual machine, which seems to run about twice as fast in my own micro-benchmarks. A bit less than the three times I’d heard, but maybe I’m testing the wrong things.
One of the solutions I’ve considered for avoiding manual intervention on the AI is a sort of genetic algorithm. Unfortunately, this is based on running really large numbers of runs, so it’s quite impractical with multi-second run times – unless you got a lot of time or a lot of CPUs.
So, have I procrastinated talking about procrastination? The point is that the problems are hard, and I’m not seeing any obvious solutions. I could sit around staring at the screen, but instead I’ve switched over to Canvas P.J.s, which I’ve had something of an itch to work on. I’ve now done a little work on that, and I’m still only writing this now ;^)
It’s no picnic either – I’m at a point where I should really have a set of tests to make sure I’m not breaking anything. I starting setting up the framework for this – isolating tests into a different file, breaking up features from my one composite image, generating eps files, (manually) using Imagemagick to convert them to png, and then generating a web page to view the results.
At this point, I realized I’m probably not the first person to want to test a bunch of canvas features. I went looking, and with a little help, found Philip Taylor’s. This is some high-class software. The individual tests are created by a code generator filling in templates. Some of them are, via a combination of regex hacking and some DSLish macros, converted to python to generate independent images for comparison. It also supports multiple test suites, though I’ve hacked out most of the mochitest stuff to try and speed up the generator.
I’ve already got it mostly running with PJs, although I still have to run the eps-png conversion manually, and it’s debatable whether I’ll get the automatic assertion checking running. A large number of tests (transparency, pixel access) will be irrelevant.
Since it’s a package which may independently evolve, I’m experimenting with Mercurial Queues to develop my changes as a series of patches. There is a small learning curve involved with regards to when operations are allowed. Generally, having modifications will prevent you from doing anything, so one must learn to make sure things are set up properly before making any changes.