|J. Robinson Wheeler's Make IF Fast!|
MAKE IF FAST!by J. Robinson Wheeler
I've been on a pretty good run lately as far as writing good IF fast. My Comp 2000 entry, Being Andrew Plotkin (BAP), was written in 20 days. For the IF-arcade package, I managed to write Centipede, a small but detailed story game, in an even more ridiculously short period of time, from a blank document to the final version in less than ten days. For the SmoochieComp, I halved my development time yet again, creating The Tale of the Kissing Bandit in just five days.
I thought it might be instructive to describe how I've accomplished this. I'm not sure that the way I work is going to be applicable to any other authors. Everyone should find his or her own rhythms. I also don't recommend trying to write IF this fast; although the adrenaline rush is heady, it's ultimately more important to make sure to do quality work. With that in mind, let's look first at two things: Starting from mock transcripts, and Speed-IF. We'll then take a closer look at the development of my three IF games to see how I used these techniques in practice.
Starting with mock transcripts
For each of these projects, I started by writing a mock transcript for the game, a play session as it might look. I often write out what I imagine to be a successful playthrough scenario. Coding then consists of coming up with ways to allow the commands in the transcript to be accepted, and then to print out the text as written. Any actions that diverge from the transcript, such as plot branching and player interaction with scenery objects, requires more writing. The idea is to prioritize the winning solution of the game, and handle other stuff as time allows.
The first time I used this method of plotting-by-mock-transcript was for Four in One in 1998. Famously, the transcript was released several months before the actual game, but the finished version ended up being so different from the mock transcript that players who had read the transcript were just as confused as people who came at the game sight unseen. For my more recent games, I hewed fairly closely to the mock transcripts all the way through.
I should mention out that Four in One took about three months to write, and the TADS game I started before Four in One is currently at five years of intermittent development and counting. What changed in between was that David Cornelson came up with an exciting idea for a diversionary activity one night in October, 1998, that of writing an IF game in two hours. Speed-IF's parameters have since been explored by many others, but I think first credit is due where first credit is due. Without Speed-IF, I wouldn't be able to code IF in these ludicrously short development periods.
It was determined through Speed-IF trial and error that two hours is about the minimum time possible to create anything resembling a proper, if hasty, IF game. It gives you enough time to do one or two rooms and a dozen objects or so, and maybe one NPC if you rely heavily on default behaviors. After participating in a couple of these madcap sessions, I started to develop some intuitions and reflexes that have since served me well. One is the sense of the bare minimum you need to create an IF experience. Two, I became very fast at creating rooms and objects, of going from the notion "Hm, I need a sponge here." to typing its basic code:Object -> sponge "sponge" with name 'sponge' 'squishy' 'little', description [; print "It's a squishy little sponge, currently "; if ( self hasnt general ) "dry. "; "sopping with water. "; ], before [; ! something for soaking up water ! give self general; Squeeze: give self ~general; "You squeeze the sponge, and the water gooshes out. "; ], ;
In this example, I've already mapped out for myself the basic object behaviors and given the object a full description property. The next thing I would probably do is create a SQUEEZE verb, and then try to think how I would fill the sponge with water -- most likely by dunking it into a bucket of water or something. So, then I create a bucket_of_water object, taking another 90 seconds. What with the time to futz about creating new verbs (and the synonym "DUNK" for PUT or INSERT), maybe eight minutes has passed, and I've got two pretty good, fairly solidly implemented objects. Anyone playing this Speed-IF will have no problem using these objects, and I've still got a hundred and twelve minutes left.
This skill boils down to thinking ahead and leaving myself placeholders for what to do next, even as I rotely type in the basic object code. The speed at doing it comes with practice (and with your raw typing speed -- your mileage may vary). I practiced under the pressure of 2-hour deadlines, where every minute counted. That sort of speed has become automatic, and it now serves me well in creating larger IF works.
If I set aside an hour, I can create a whole load of scenery objects for a room with this method, complete with associated special verbs and action handlers. In an eight-hour coding day, these basic mechanics of coding items and rooms now take only a small proportion of my time, leaving me free to tackle knottier problems that still take a while to solve.
Being Andrew PlotkinWhen I started BAP, I was working without a plot or a transcript, mostly because I hadn't figured out exactly what I was going to do yet, but with only a month to go from nothing to a fully-tested and proofed Competition entry, I had to just launch into it. First room, first scenery objects, appropriate verb handling. Second room, second scenery objects, more verb handling. Even working at top speed, after the fourth room I realized I was working far too slowly to finish the game in time. By that point, I had done enough to figure out basically how I wanted the game to go, so I dedicated a precious afternoon's work to writing a mock transcript from where I was in the project all the way to the end of the game.
Working from a mock transcript saves time because I can cut and paste text instead of pausing to come up with it at each and every object and action routine. Also, the text that I paste in often has a little more zip to it, having come out of the natural flow of writing prose rather than the start-and-stop rhythm of writing a bit of code, writing a string, writing another few lines of code, another string, etc. However, if I get good ideas for extra things to throw in as I go, anything that takes only a couple of minutes to code up and toss in, I tend to implement immediately. This could be a new verb implied by the transcript's text, a special scenery object description, or anything of that nature.
A saved copy of the original mock transcript I wrote for BAP didn't survive the coding process intact. I pasted bits of text into it as I went, where in the original document I had left placeholders to myself (e.g., "ENCHANTER ROOM"). The basic text remains unchanged, just as I first wrote it. As a comparison, you might enjoy reading the playthrough transcript of the finished game's winning solution, to see how I ultimately deployed the mock transcript's text.
- Read the Being Andrew Plotkin mock transcript
- Read the Being Andrew Plotkin final playthrough transcript
- Play Being Andrew Plotkin
Centipede was a little more interesting. Having the experience of writing BAP under my belt gave me a good idea of what I could do, and what players would allow me to get away with. It also suggested a new idea, which is that the game would run like a little movie, playing out its story even if the player just typed >Z.Z.Z.Z.Z. until it came to the end. The motivation for this was to make it easy for the player to go through the game. A little daemon that ran each turn would print out the next bit of the story by default. That said, the player would have the ability, through interaction, to either pause the story daemon for a turn, or to make it jump to a different point in the story, or even a completely different plot branch, which would have its own little section of story to run by default every turn. I was hoping to allow for a fully-branching story, but as often happens, I fell back on the idea that the branches would eventually converge and play to a single ending, no matter which branches you started out taking. It was the pressure of time curtailing my wild imaginings.
I ended up liking the story daemon device quite a bit; it reminded me, as I refined it, of the mechanism used by animation programs like Macromedia Director and Flash. Let it play the movie until the user does something special, in which case, jump to a different movie and resume play from that point. Ultimately, I ended up with a lot of quite nearly redundant code, because for the arcade part of the game (shooting at things), the player could basically diverge from the scripted story every single turn, requiring me to handle two or three exceptions each turn. Since each of them printed a similar but slightly different paragraph of text, the game file swelled to about three times the size it might have been. However, it made the final experience vivid for the player, even upon multiple replays -- something I didn't expect when I was writing it, figuring that the average player only plays an IF game once. I was just making sure that, whatever the player did, a game transcript of that one experience would seem like a fully written story.
Centipede: The mock transcript
The mock transcript I used to plot out the game actually was not a winning solution; mainly, I was describing what I imagined as a typical session of playing the Atari coin-op videogame, in which you have three lives, and once they're all blown up, the game is over. I tacked a prologue onto the beginning of this arcade simulation, starting the game in the middle of some excitement, setting up the idea that this was going to be a game that was quick, exciting, and fairly gritty. I can write straight prose fairly quickly, so I whipped up my mock transcript in an hour or two, probably, and then set to writing the first pass at the source code for the game.
[ If you are familiar with the game, you will recognize that most of the transcript text appears unabridged in the final game, with a few interesting differences. For example, the Chub character is a man, not a woman. There are ideas which are entirely absent in this early version; e.g, there is no Charlene, just weapons referred to as "pulse rifles" (they become "plasma rifles" in the final version). Finally, there are many difference in the way that the text is broken up. The use of > as a turn marker in the mock transcript doesn't necessarily correspond to a turn break in the final game. (Often, one "turn" of the mock transcript is parcelled out to two or three final game turns.) ]
Centipede: Beginning the coding process
I began coding with a blank stub.h file. I then copied a chunk of code from Being Andrew Plotkin's source -- mostly new verbs. I cut out the ones that were specific to BAP and wouldn't likely be used here. I adapted the rest, giving me a head start on creating a specialized verb set for the game.
After that, my first goal was to simply have the game start as it does in the transcript, by printing the intro, the title banner, and the first room and object descriptions. All of this is just preliminary stuff I do whenever I start a game, and I can do it more or less mechanically. Only after I get the game to boot up and print the intro, banner, and the first room's text just like it looked in my mock transcript do I have to really start thinking about how to start turning the transcript into an interactive game.
For the story daemon, I decided to have an each_turn counter (a special kind of Inform daemon) that would simply print the next bit of text from the mock transcript. I implemented this as a switch() statement that would just print a new string every turn, and then I tested it by typing Z over and over until I died. (For a couple of days, I toyed with the idea of including a walkthrough or a hint command that simply said "Z.Z.Z.Z.Z..." or "Hit Z fifty-eight times.")
Centipede: Alpha 1 development
So far, so good -- the default story was already good to go, providing the basic spine for everything else I did with the game. What I needed next was a way to interrupt the daemon for a turn, so that I could print something else instead of the default. This was as simple as a flag. If I set the flag that turn ("give game_counter general"), the counter wouldn't increment, the text for that turn wouldn't print, and the flag would be reset for the next turn. I could then print whatever text I wanted, pausing the preset story for one turn.
At the time I did this, I didn't realize that I could just give the player object an each_turn property, in essence making the player a ticking clock (or perhaps, a walking movie projector). I put the counter in an object that the player would carry at all times. Thinking about what my options were, I decided that a belt of ammo clips was a good disguise. The belt of ammo clips is actually the game_counter object, in charge of printing the various sections of text.
After that, it was time to start implementing some scenery objects to give the opening of the game some robustness. I was already doing a couple of clever things here, as an author. One thing I've learned is that the most important place to flesh things out with scenery objects is at the very beginning of the game. Players are likely to examine everything they can, since they don't know what else to do, including >X ME and taking inventory. I really didn't want the player to have much time to fiddle around, so the story daemon only allowed about six turns of breathing room for the player to randomly explore the environment before the game required decisive action (pulling the parachute release levers). I also stacked the deck by putting two strong hints, in the intro text, about an action for the player to do: touch the green button on the holovid.
If the player bothers to investigate the holovid, the player has already wasted a few precious turns. As an author, I was trying hard to direct the player's attention, because I didn't have time to implement everything completely. That was my intention, anyway; it turned out that I took the time to code up a whole bunch of new objects, each with special descriptions and fiddly verb handlers, that I hadn't even thought of when I wrote the transcript. After I implemented the holovid, and made the game_counter appear to be a belt of ammo clips, I realized that it was odd for a soldier to otherwise have no inventory, no gear, no uniform, and no weapon. I launched into the task of creating a fully-stocked inventory of items and clothing. I'm glad I took the time to do this, because it added a lot to the game's apparent level of detail, to the sense of immersion in the game's world, right up front. It also meant that there were now more items, many more, than the player had time to inspect and otherwise interact with before the story daemon moved things on. The "movie" keeps playing. It doesn't really let up to let a player take a breath. (It's a bit relentless like that, but so was playing the arcade game.)
The best thing to come out of stocking the player's inventory was creating the player's weapon, Charlene. Somewhere in my brain, from watching war movies, was the notion that Marines name their weapons and give them the attention they would otherwise give to a woman. Once I hit upon this idea, I had great fun taking the metaphor to its extremes throughout the game. Every reference to Charlene and what the player is doing with Charlene ends up being a double entendre; ridiculously so at times. It's funny for the author and for the player who notices, but all of it is delivered in a complete deadpan by the game's narrative voice, because the player character doesn't think his relationship with Charlene is unusual. Therefore, the time invested in creating the player's full inventory was well worth it. It is also an example of what I noted earlier, of seizing inspiration as soon as I come upon it during the coding process.
And that's about as far as I got on the first pass through the code, which took me two days.
Over the course of the next seven days of coding (three days lost due to the Christmas holidays), I created four more alpha versions, one beta version that went to whoever I could find at that moment (the night before it was due) to test it, and then the final game release. This mainly involved expanding on what I had already laid out in the alpha_01 version -- adding new branches to the default movie, and handling exceptions where the player could pause or completely diverge from the default movie. For most of the exceptions, what I did was just copy and paste the game_counter switch() statement and then alter the text to suit what was happening -- shooting at a mushroom instead of at the centipede, for example.
I didn't get a chance to implement everything I wanted to (like diving back into the landing craft and setting off the self-destruct device), but what I did get a chance to do was solid, because it was still based on the story spine fashioned directly from the initial mock transcript. The final code for the game is not elegant, but it works, and you can't tell from playing the game what the source code looks like, anyway.
The Tale of the Kissing BanditBandit was a little different, and a lot crazier. I'd estimate that the breakdown for the five non-sequential coding days went something like this:
Angst & worry 70% Pure raw panic 20% Dumb luck 9% Coding skill 1%
In general, it's not a very good example from which to draw wisdom. However, I would like to step through this one in more detail and explain each of its five development stages. Don't read further if you don't want spoilers, because this is going to spoil everything about the game.
Bandit: The mock transcript
The mock transcript I wrote makes up only a tiny fragment of the game. It contains the intro text, then skips ahead to a second-act scene, then contains a note to myself about the first surprise reversal of the game. That's it. The game's second reversal, which constitutes the epilogue of the game, came sometime during the coding process, by which time I'd broken my own rules and was back to improvising one object and one room at a time, on the fly, without the aid of a transcript.
Here it is:
Bandit: Alpha 1 development
I started the coding process as I usually do, which was to make sure the game could start up, print the introductory text from the mock transcript, and place the player in the first room of the game. At this point, I was intending to base the game's basic design on that of Centipede, so I copied in the game_counter stuff, which did nothing for a couple of turns and then moved a young maiden from the start room to another one. I also loaded the player's inventory with a number of costume items, going for the same level of detail that I'd used the last time. This quickly became a problem for me, because I was already starting the game late, and I was just creating tedious busywork for myself. Worse was the starting location, a ballroom packed with people, in a hotel that would suggest an expansive layout and design detail. Yecch! I would rue that until the last minute, as I sat and tediously stocked a completely irrelevant location with five and a half dozen readable book titles.
Bandit: Alpha 2 development
Alpha 02 wasn't much of an improvement on Alpha 01. The name of the main character was officially changed to The Kissing Bandit instead of the Smooching Bandit. The inventory objects were fleshed out slightly, and kissing code was added to the Gabriella Cavortini NPC -- but not Lily Whitestone. It was a very short day of work, I think due to doubts about whether the project was going to turn out to be good enough quality to release.
Bandit: Alpha 3 development
According to the version date stamp in the file's header, I didn't pick up work on the Bandit project for the better part of a week. By this point, I had three days left until the (already extended) deadline of February 10. I think this might have been the point where I talked to Emily Short and told her I didn't think I was going to finish my entry. We talked around it for a little bit, and she made me start thinking again about I liked about the idea. Suddenly, I was interested in at least trying to finish, because there were fun ideas I really wanted to use -- including the epilogue, which had come to me in the meanwhile.
So, I decided to bite the bullet and just do the busy work to get me back up to speed. I'd only implemented the bare minimum of rooms in the alpha 01 version, and didn't add any in alpha 02. It was time to just go ahead and code up the whole ballroom and ancillary bits of the hotel, as well as the garden park outside. Then, I added a kissing action to Lily Whitestone, which prompted me to write the code that segued into the second part of the game, outside in the park.
While writing that, I thought of the idea that the player could decide to flee the hotel and run straight to the park. So, I established an Escape verb, allowing you to twirl your cape and beat a retreat. I think code for escaping over the balcony veranda came at the same time. Then I thought, what if you just walk south out of the ballroom? So, I added an extra room to the map I'd just built.
What seems to have occupied the rest of my writing time is a large set of specialized verbs. I made the barest headway into implementing the second scene, handling the prompt to the player to "sneak east". Besides Escape and Sneak, there was Lurk, Approach, Make (out), Snog, Chase/Follow, Steal (forward), Spin/Twirl, and Dance (and various dances by name, such as Waltz). Specialized library messages also appeared for the first time in this version.
Bandit: Alpha 4 development
In this version, I picked up with the chase of the maiden through the park, and just kind of powered through to the very end of the game. I first concentrated on the park chase, expanding quite a lot on the Chase/Follow verb, even for cases of the player trying to follow the NPCs in the hotel scene. To do the chase, I simply made a series of rooms with a description, a cant_go that insisted the player keep up the chase, and a special function that would move the player to the next chase room. This way, I could code the rooms fairly quickly, because there was very little code in them at all, and what there was could be copied and pasted. The special function was just a switch statement based on the player's current location. There you are, the chase is done. The only fiddly bits to take care of involved the player trying to look at or otherwise interact with the NPC at this point. So, she had to be moved around too, and some of her description properties had to be be variable based on her location.
After I wrote the chase code, I took some time to write a second mock transcript, starting from after you kiss the maiden on the tower and going to the end of the game. This was in order, once again, to work more quickly. Once this was written, I took a break for several hours. When I picked it up again, I cut up and moved around the story text, sticking it into a game_counter object -- once again my shorthand for making sure the text I wrote ended up getting printed no matter what the player decided to do. Thus, I finally had a place to reuse the Centipede technique. Finally, some of the transcript code went into the Kiss action of the Jenny NPC, as appropriate, as well as the specialized deathflag message.
One thing to note is that the transcript describes a segueway to the epilogue based on kissing Miss Adams, but this branch doesn't appear in the code until the final version of the game. I ended up improvising a second branch first, one in which you pretend that you have a rocket ship as a means of escape from Miss Adams' clutches. Since coding the branch that directly followed the transcript would be merely a matter of cutting-and-pasting, I decided to tackle the one that involved extra writing first, saving time later on. This was also a practical move in that I realized that later on I'd be more tired and less able to be creative.
In the first session of coding the Beta 01 version, I concentrated on the end of the game. The "rocket ship" story branch was implemented, one of two ways to segue from the end of the chase to the epilogue. I still had to implement the branch of the story that involved kissing Miss Adams. She had to go in as an NPC, and then the new segueway was coded, and some modifications were made to the conversation code with the Jenny character at the end. It was late, though -- still the same workday that had begun with the Alpha 04 version -- so I took a break to eat and sleep, with one afternoon left open for finishing touches.
My plan for the last afternoon's work was to simply create detailed scenery objects for the hotel at the beginning of the game. That was the only task for the day, a bit like gruntwork. I was going to take it easy and just spend maybe three hours doing it, making sure there weren't any typos in it, and then I'd call the game done, compile it as a Release version, and hand it in.
I ended up mainly concentrating on the extra rooms to the west of the starting location -- a sitting room, a costume changing room, and a smoking room with books and paintings. As long as I was going to take on this otherwise rote task, I decided to do my best to write interesting material. It seemed worth it; the player is likely to wander around at the beginning of the game. The more detail showed up there, the better the overall effect would be. Conversely, little or no implementation of these otherwise completely useless rooms (once intended to offer yet more NPCs with which to interact) would be a serious flaw, in my opinion.
I had a pretty good time writing the costume room and the descriptions of the paintings on the walls of the smoking room. The last task was writing titles for sixty-six invented books. I just made them up, one at a time, until I'd written something for each one. It was a pleasant little head exercise, trying to come up with plausible books (early on, I abandoned the idea of trying to make them funny or jokey) by plausibly-named authors. In my mind, this section of the game was taking place in the late 19th/early 20th century London. What kind of books would be found in that time, and in this place? Same for the paintings -- what paintings might have been found there? It was all very interesting, and a pleasant, non-hurried way to finish up what had turned out to be a good authoring experience on a fun project.
ConclusionThere's an adage that goes: "First you get good, then you get fast, then you get good and fast." At first I wanted to quibble with this. Why can't you get fast, then get good and fast? I think the answer is, if you get fast before you get good, you will never get good and fast, because getting good takes a long, patient time. Applicable also is the voice of experience which says that every writer has to write a million bad words before good ones start to come out. That's about four thousand pages of writing, and I believe this is true. Even if you have a natural talent at something, you still have to do a lot of work before you can apply that talent effectively. You learn by practice, lots and lots of practice.
I'm not the skilled programmer that some IF authors are, mainly because I haven't put in the hours for it that I've put into becoming a skilled writer. I'm improving as I go, but it'll be a long time before I've put in the same number of hours as someone who started programming at 12 or 16 and subsequently landed professional employment, where they were forced to work for eight or more hours a day, every day, writing code.
I'm hoping this article will be instructive to some, but I can't exactly fathom how. Perhaps something in the source code will help a nebulous idea in some author's mind click into sharp focus, or someone with more skill will see how to take my work and improve upon it. I don't think it's important for anyone to work fast if it isn't their natural pace. My habit of working fast might ultimately prove to be a long-term weakness as well as a short-term strength, because I'm always going to be skimping on something.
So, even though the title of this article is "Make IF Fast," I hope it instead encourages authors out there to Make Good IF. There's no substitute for quality.