Replacing State Variables with Function Assignment
When writing my game for the last Ludum Dare, the structure of game flow evolved considerably as development went on and vague ideas like "have a game over screen" or "show the computer thinking" got implemented. The structure I used to handle game states is probably one you've seen before:
if state == "turn.player" then playerturn() elseif state == "turn.cpu" then cputurn() elseif state == "cpu.thinking" then -- todo animate thinking if frame - thinkstart > 90 then state = "turn.cpu" end elseif state == "message" then ...
This isn't very elegant, but all possible states are in one place so they're easy to see, and it's easy to make additions. It's not the worst thing I could have done.
Looking over the code afterwards though, while a lot of things would be different if I rewrote it from scratch, this structure reminded me of a section in Thinking Forth I'd read just recently, specifically part of Chapter 8: Minimizing Control Structures about device-appropriate text output:
If the only purpose to setting a flag is so that later some code can decide between one function and another, you're better off saving the address of the function itself. (PDF pg 274)
Or, more compactly:
Don't set a flag, set the function.
To clarify how this applies to my situation above, I could turn my large if-else statement into this:
def _update do_update() end
That's the punchline, but how do I actually do this? The answer is pretty
straightforward - I just need to take each of the parts inbetween the
elseif above and make them into functions, and instead of changing the
state variable, just assign
do_update. So, for example, whever I had this...
state = "turn.player"
I should instead have something like this:
do_update = playerturn
playerturn is the update function that would have previously been
functions are first-class values this is trivial, and even in C you can use
function pointers to achieve the same effect.
I took a little time to rewrite my LD submission using this style, and overall it increased my line count while decreasing my character count - the line increase is probably due to function declarations.
You can see the results here.
For this code the benefit of doing this wasn't particularly dramatic (though every byte counts in pico8), but for larger projects it's pretty easy to imagine the appeal of having valid program states defined as a number of driver objects that fit a single interface rather than as a soup of statements inside a big switch. I'm sure at some point I'll run into that kind of big switch lurking in legacy code before too long, and now I'll know what to do when I find it. Ψ