"I aten't dead"
- Granny Weatherwax
Warning: blog post contains factual inaccuracies. See comments section for discussion.
So I keep meaning to write up my experiences in India, and I keep getting distracted. For example I've developed an interest in clockwork (don't ask).
Even more braincell-consuming is my ongoing effort to produce some sort of computer game worthy of the title. This is going very slowly, mostly because I'm taking a very anal approach to it. In short, I want to make it Pythonic.
An explanation. My favourite programming language, Python, puts a lot of effort into inculcating its users with good programming style. This is something very different from simply being able to program in the language, in the same way that not every writer of English prose can compete with Shakespeare. Good Python code, like any other form of poetry, should be elegant, non-kludgy, readable, evocative... Pythonic.
In particular, Pythonicism discourages "procedural" programming. This is code that consists primarily of single instructions linked by commands to go to a given line. For example, if you wanted to print out the numbers from one to twenty, a procedural approach would look like:
line 1: x = 0
line 2: add 1 to x
line 3: print x
line 4: if x < 20 then go to line 2
line 5: quit
The problem with this approach is that, unless you read and comprehend the entire program, it's a pain to figure out what the blazes it's doing. Let's say you start reading at line 3. What does x equal? From line 4 you can figure out that it's a number, but there's no clear indication of what x starts out as or how it changes over time. This is not a problem for this program as it's so short, but a 1000-line example would soon make your brain explode.
By contrast, a Pythonic approach would look like:
line 1: for x in range(20):
line 2: print x
If you start reading at line 2, you'll see that the text is indented, which means it's a consequence of some preceding statement. "Oh," you'll say, "I need to look to see what this command is a subclause of. I'll look at the line above." And you look, and lo and behold the line above contains all the info you need to understand x: its starting point, its limit, and its mode of change. Not only is this code shorter, it's vastly more readable. That 1000-line program starts to look manageable.
The Python idiom actually has a very simple root: the principle of minimum power. Always use the most restrictive command that can achieve a given goal. It takes a bit of thought to realise that a "for" statement is less powerful than a "go to" statement, but it's true. A "for" statement imposes certain constraints: the list of values must be predefined, and in any iteration you can only move to the next value in the list.
By contrast, with "go to" statements, you can achieve any pattern of recursion, however convoluted. When you read code, what you're mostly doing is narrowing down the possible explanations for what the program actually does and how it does it. "Go to" statements do not narrow things down at all, so code with them in is harder to read than code that uses more restrictive statements. This is why "go to" statements are evil: any program containing them tends to become an unmaintainable mass of spaghetti.
So Pythonicism makes for nice programs. But it also makes for headaches on the developer's part as he/she desperately wrestles with how to make a program not only functional but elegant. This is not easy. It's like trying to write instructions for an educationally subnormal employee that at the same time read like the most beautiful sonnet.
The example I'm hitting is game design. The game I'm working on is extremely simple in concept: it's a turn-based game, and the actual game logic is not complicated in the least. But I'm having real trouble because I can't see how to structure the code Pythonically. Procedurally, things look like this:
1) Start the program up
2) Draw the menu screen
3) Once the "start new game" option is selected, initialise all the game variables
4) Draw the game-board screen
5) Let the user play a round (updating the board as they go)
6) Let the computer play a round (updating the board as they go)
7) Go to 5
This is perfectly viable... and completely procedural. A more Pythonic approach is still forming in my brain, but certain subtleties have become apparent. Firstly, the user actually has two roles: "dungeon-master" and "player". As dungeon-master, the user gets to choose the game settings (difficulty etc) and save or load games. As player, the user is limited to playing with the pieces they're given - no metagaming.
Secondly, the core logic of the game should be agnostic as to whether a given player is human or AI - no cheating. There will therefore be two equivalent components that provide player decisions, one of which happens to have a lot of AI code in it and one of which happens to have a user interface. The user-interface component will have to be tolerant of occasional switches from player mode to dungeon-master mode. This raises a number of design questions, for example what happens if two players try to use dungeon-master mode simultaneously?
So what's the pay-off from this incredibly abstract approach to designing the game? Well, there are two main advantages. Firstly, once I've got the structure sorted out to my satisfaction, inserting the actual game logic will be a piece of cake: I won't have to worry about unexpected interactions between the various bits of code, because each will have a well-planned interface to the wider world. Secondly, extending the game will also be very easy - for example I could make it a network game with minimal effort.
It's still painful though. That's the joy and despair of poetic Pythonic programming.