When the Mood Strikes

My mental model for how I should finish Project Serpent is that I shouldn’t bother too much with forcing myself to work on it when I don’t feel like it, because that will just make me miserable. My ultimate goal is to develop the ability to finish projects that I can exercise for the rest of my life, so it’s pretty important that whatever procedure I end up with is not one that tends to make me miserable. So according to this model, I should wait until I am in the mood for programming, and only then should I program.

This actually worked pretty well for about a week after I started working for pay. After that, my programming moods seemed to slow down significantly — I haven’t felt up to programming in a week or two at this point. And so I’ve done no work.

While waiting until I can program without stress would seem to be the ideal strategy if it would get me to my goal… it’s clear at this point that completionism is more difficult than that. If I actually want to finish this, it will take some discipline: I’m going to need to force myself to start working even when I don’t feel like it, at least sometimes.

I would love to instantly become my ideal self, who can be perfectly comfortable at all times and still produce interesting, finished things. But that’s not an option. The only way forward is incremental improvement, and it seems that the most productive incremental improvement is to learn to finish things, even if it hurts.


Project Serpent, Day I-lost-track

On day 8 (which I believe was Sunday), I announced that I was happy with the progress I was making, and that I was confident enough in my ability to finish that I would treat the end of this next week as a deadline.

And then the next day I went to work, and realized that perhaps my pronouncement was premature.

It turns out it’s really hard to work on anything after coming home from a full day of work. Luckily, I still find it pretty easy to get some work done on Serpent before I leave in the morning, but even so that’s much less time than the four hours I told myself I’d spend. I’m no longer sure I can finish by the end of this week, and even writing up daily progress reports is kind of hard. Some concessions are due.

I will not give up on Serpent, but I’ll make the following modifications to my challenge:

  1. I will work on Serpent for a minimum of 30 minutes each day.
  2. My deadline (which is once again a soft deadline, like the one I set for the first week and unlike the one I meant to set for this week) is December 30.
  3. I will no longer require myself to post progress updates every day, though I’ll still try to do it every couple of days and I will still require myself to stick to my pre-Serpent schedule of one post per week.

Now that the unpleasant concession is out of the way, here is an actual progress update.

This morning I wrote up an abstract effect and handler for operations depending on user keyboard input. This is the next step of my “make everything interactive, thereby increasing my motivation” project: I have a snake that moves around at reasonable speeds, but right now it just plods inexorably toward the right edge of the screen until finally it crosses the threshold, never to be seen again. I don’t even get to see whether I’ve correctly handled cases of the game state evolution and drawing functions for when the snake does not consist of a single segment.

Over the next couple days (hopefully by the end of this week), I’m not going to work on actual collision with walls and eating food, but I hope to have the snake respond to the arrow keys by turning around!

Project Serpent: Day 8

Somehow Day 7’s post got lost amid my move and starting my first day of work1. My accomplishments for that day are probably smooshed into the post for Days 5 and 6, but I haven’t been paying close enough attention.

Needless to say, I’ve decided to extend my deadline to a second week. This isn’t really a problem, because the point was never to finish something fast, it was just to stick with it until it got finished at all. I think I’m pretty well on track for this (but I do think that this next deadline is going to be stricter: I can’t just pat myself on the back and be content that I’m making progress, so I want to be careful not to let this drag on longer than it needs to).

Anyway, a brief recap. At the end of days 5 and 6 (and maybe 7, I guess?), I had implemented my NONBLOCKING effect and its yield command, which allowed me to write much more natural-looking code. I felt very satisfied with my work, and proud for thinking of it and fighting my way through the type errors until it actually worked the way I wanted it to.

And then I called it a day, because it was late, but that meant that I ended that day of development without actually using my triumph to write a natural event loop to evolve and draw the game.

So on Day 8, I spent my time doing that. First, since I was now writing my entire game loop within the Effects DSL, I needed to write an effect that provides drawing commands rather than just using IO actions. I did this.

But I soon discovered that whenever I used these drawing commands, they would grind my game loop to a halt: I had previously put in a console.log every time my yield code fired, and whenever I used my drawing effect I’d get exactly one line of output from it. I worried that perhaps my yield effect wasn’t as composable as I had thought, that my entire architecture was flawed and that if I’d just thought about it more rigorously before spending a day implementing it I would have realized that it couldn’t possibly work, that it was too good to be true.

Actually it turned out to be an embarrassing example of the kind of mistake that doesn’t get caught by a type system. Interpreters for Effects programs are defined in continuations, and the thing that makes this powerful is that you can define when, how, and how many times the continuation is called. For instance, you can call the continuation under a lambda abstraction and pass it values that don’t actually exist. You can call the continuation many times and build up a list of the results.

Or you can call the continuation zero times, simulating a nonlocal exit. The code following a command that doesn’t call its continuation… doesn’t get executed.

Needless to say, this is exactly what I did.

Despite all those problems, I soon ended up with a simple program that just makes the snake run off the edge of the screen. But the program was written entirely in my little Effects DSL, and looks very natural, and I’m confident that it will be easy to write many different variations when it comes time to add more features.

Day 8 was well spent.

  1. I am so excited about this. So much that trying to find the words would distract me unacceptably from Project Serpent, so unfortunately I won’t. 

Project Serpent: Day 4

Halfway through my first week, and it looks like unless things pick up I’m going to be taking a second. That’s fine though, just as long as I keep working.

Right now I have a very preliminary “step game” function that allows the snake to move around, maintaining its length, but doesn’t do any collision detection (including whether it has eaten any food). The obvious thing to do here would be to detect collisions and remove food (code is already in place to grow the snake if the collision detection signals that food has been eaten), but instead I’ve decided to take a little break to switch to working on the front end.

What have I been doing on the front end? Well, as in everything, there are degrees to this, and I’ve only been working on the “middle” front end so far — actual rendering and user input aren’t coming yet, but I’m one abstraction level closer to that. I am writing code that is “aware” that user input and frame rates exist and must be taken into account.

I’ve been handling this as follows. There are a few rough “parts” to the Effects library: the specification itself (defined in Spec.idr), the “resources” that are required to back those effects (which I define in GameState.idr), and “effectful programs” that make use of the operations defined in the specification to manipulate the resources. The great thing about effectful programs is that they can have a list of effects available, and can use operations from any of them. So for example, I’m writing programs right now with access to [GAME (Playing False rules), FPS 5]: I can manipulate a game that’s unpaused, and… hey, this FPS thing is new!

FPS is an effect I wrote yesterday night when I realized that if I’m writing code that will run on every browser frame, I shouldn’t necessarily have that code run a simulation tick every time. The coordinates I’m planning to use in Serpent are relatively low resolution, and moving forward by an entire “square” every frame would be way too much. So the FPS effect manages a little counter that can be queried to say “Hey, this much time has passed since I last talked to you. Should I run another simulation tick yet?”. Currently I have it set up to run 5 ticks per second, but that can be easily adjusted later.

Of course, 5 ticks per second is fine for the game clock, but a UI that runs at 5 FPS is going to be annoying. I plan to solve this using tagged effects: [FPS 5] represents the ability to respect a clock that runs at 5 ticks per second, but ['GameClock ::: FPS 5, 'UIClock ::: FPS 60] represents the ability to respect both a game clock that runs at 5 frames and a UI clock that runs at 60 frames per second. This is similar to how many RTS games run on a coarse-grained clock used for the simulation and a finer-grained clock so that nice animations can be shown between the game’s relatively abrupt state transitions.

And finally, right now I’m just writing a function from user inputs to effectful programs with access to [GAME (Playing False rules), 'GameClock ::: FPS 5, 'UIClock ::: FPS 60], but I plan to soon write an effect representing input as well. After that, I hope to be able to write code like this:

if !('GameClock :- tick delta)
  then turn !getDirectionCommand
  else pure False
if !('UIClock :- tick delta)
  then {- draw a new frame somehow -}
  else pure False

Of course, I’m not quite there yet. But I think I should be by the end of today!

Project Serpent: Day 3

Yesterday I spent some time improving my MenuInput data type. I realized I haven’t mentioned that at all in this series of posts, so I’ll talk about it a little.

A MenuInput basically describes an editable entry in an options menu. Simple menu inputs might be things like NatBox "Number of heads" 3. This represents an input box that can contain a natural number, labeled “Number of heads”, and whose default value is 3. Similarly, FloatBox "Game speed" 1 is a box called “Game speed” that contains a floating point number defaulting to 1. There are also selection boxes, like Options "Hair color" ["blue", "green", "red"] "green", which means that there are three choices for hair color defaulting to green. And finally, there are toggle options with additional configuration available if they’re toggled “on”: Toggle "Gravity" [FloatBox "Strength" 9.8, Options "Direction" ["up", "down", "left", "right"] "down"] means that gravity can be either on or off, and if it’s on you can also configure its strength and select one of the four cardinal directions for it.

Every kind of menu input has a type of values that it accepts. A NatBox accepts natural numbers, a FloatBox accepts floating point numbers, an Option accepts one of the available strings from the list of choices1, and a Toggle can either be “off” or it can be “on” with a list of values for the extra configuration options. Because Idris is a dependently typed language, we can actually encode this statement as a function from MenuInputs to Types, and that’s exactly what valueFor does in Spec.idr.

So part of my time yesterday was spent writing additional utilities for working with lists of menu inputs and the values they can take. For example, I have a function to apply an update2 to a particular entry in a menu, or to get the value of a certain entry.

Anyway, that is one system I have developed that I hope to get a lot of use out of when I get to programming the menus. So far, I’m not there yet, but I’m getting there: the other thing I spent time on yesterday was my skeleton implementation of the game state, with a focus on states in the “in-game” phase. Right now I’m working on what you might call the meat of the game: stepping a game state by moving the snake, doing collision detection, and possibly growing it or ending the game.

Today I’ll finish writing functions for stepping the game state, and hopefully by tomorrow I’ll be ready to start hooking those functions up to some kind of UI!

  1. we represent these in Idris as strings paired with proofs that they’re in the list of available options. 
  2. I also have a function mapping MenuInputs to the kinds of updates you can perform on them: for example, a Toggle can be toggled and a NatBox can be incremented or decremented, and any input can be set to an exact value. See updateFor in Spec.idr for details. 

Project Serpent: Day 2

Yesterday I only barely got my four hours in — a reminder, I suppose, that even in the wake of my accomplishment, finishing larger projects will still be difficult for me. I’ll have to be more careful about starting to work a bit earlier in the day.

Once I did get started working, I think I got a reasonable amount done. I started by defining a type for the concrete game state (parameterized by the “phase” category I talked about yesterday), which should store everything that’s necessary to represent and evolve the game. After that I wrote a skeleton implementation of a handler for every command I defined in Spec.idr. But while working on this, I realized that my types were looser than they needed to be.

I took a break to think about this and ended up storing in all Phases the set of active mutators and their parameters. For now, this mostly lets me specify the menus more tightly: the Reset command, for example, now states in the type that it resets all mutators to their default states, and the SaveMenu command states that it does some validation checking and then either stays at the menu without changing anything or returns to the main menu with the new parameters in effect. All the other commands also state that they don’t change the set of active mutators.

Making the mutator state available to this set of rules also opens the way for a much more dramatic change later: I’ll be able to add to the Playing phase parameters like the snake’s length and score, so that rules about how the snake grows can be encoded in types too!

Of course, with all this type fun, I’ve still done little work on the game’s implementation. I’ll have to pick up the pace soon.

Project Serpent: Day 1

My work today mainly involved planning out Serpent’s features in more detail. I decided on the screens that would be featured and, on the options screen, what gameplay parameters can be tweaked. In my Day 0 post I referred to these parameters as a “Bastion-style difficulty system”, but from now on I’ll be referring to them as mutators — ways to change the game. A rough outline of what I came up with is available at spec.txt in the git repository.

Once I’d decided on the game rules and the relationships that would exist between various screens of the game, I wrote a small description of them in Idris. (This doesn’t involve any implementation yet, just stating relationships and enumerating the actions available from each screen.) You can see the results of this in Spec.idr in the git repository (the relevant definitions are Phase and Serpent), but this part merits further explanation:

The standard way to do game programming (or anything else that you might think of as having a “main loop”) in a functional language is like this. You define a data type for storing the game state, and instead of a traditional main loop you have a function from game states to game states. (In Haskell-like languages, this might return an IO GameState instead, or it might be a pure function that also takes a representation of the user input state like I used in Pong.) And then you just recursively call this function forever, and that’s the equivalent of a game loop.

The method encouraged by chapter 6 of this Effects tutorial is a refinement of the standard one. Instead of having all game states be the same type, you introduce an additional layer of categorization: a game state that represents the player looking at the main menu might be a different type from one that represents a player in the middle of a game. The reason for this is to encode the notion that some actions only make sense in certain situations: you can only “turn left” if you’re in the middle of actually playing a game and haven’t paused. The Effects library lets us make these kinds of statements known to the compiler.

Here’s the categorization of game states I ended up using, and a brief explanation of my reasoning:

  • First, Playing, which represents actually being in a game. Playing states are further divided into paused and unpaused, which I represent as Playing True and Playing False respectively. In unpaused states, the player can pause or issue commands to the snake; in paused states, the options are unpausing, restarting, or quitting to the main menu. I thought of storing extra information in this category, such as the length of the snake and how many free spaces or pieces of food were on the board, but I decided against it for the moment because the way these things change is heavily influenced by mutators. I might factor these into the categorization if I find a neat way of doing so.
  • Next, the MainMenu. Here, the player can click one of a set number of buttons, such as “new game” or “change mutators”. Since I have a pretty good idea of what the buttons are going to be, this category doesn’t have any internal structure. All game states representing the main menu have the same type.
  • Subsidiary Menus (of which my current design calls for only one, the mutator menu, but there could conceivably also be a sound/graphics options menu) are further sub-categorized by the set of inputs available, where an “input” might be a named toggleable button or numerical slider. The available actions are then to update one of the inputs (the first half of Spec.idr is concerned with describing the different ways to do this) or to exit the menu with or without saving.
  • Finally, the GameOver screen. Like the MainMenu, game-over states are not further categorized: from any game over screen, the available actions are to restart or quit to the main menu.

None of this says anything about what information any kind of game state should contain, or how to apply one of the actions once the player has chosen one to reach another game state — so that’s what I’ll be doing tomorrow.