Fictionally interactive

Test-driving Inform 7

I mention interactive fiction quite often in my articles, but the truth is, I am no longer familiar with the state of the art. Back when I was still serious about writing text adventures, Inform 7 was in alpha and TADS 3 was in beta. Nowadays the two have effectively replaced older systems, and being familiar with them can be useful even if you don't care about IF as an art form:

Now, I am superficially familiar with the syntax of both, from Cloak of Darkness, as well as from skimming the official manuals. But I wanted a closer look and, as it happens, I have just the thing.

Last year I wrote a toy authoring system in Javascript, and a demo game to go with it. While tiny — it can be completed in about 85 moves with perfect knowledge — Catch That Cat does have nine locations, a couple of NPCs and a handful of puzzles. Enough that porting it will be easy, but not trivial. As for the target, I started with Inform 7, simply because it has a Fedora 14 package.

I must admit cheating a little here: I already had some idea of how the language is supposed to work. Enough to try this for a start:

The Living Room is south of the balcony, southeast of the kitchen and east of the hallway. "This is you one-room apartment in eastern Bucharest."

To my great delight, it worked on first try, creating four rooms and describing the starting one. But wait... it's a bit difficult to navigate. Jaiffa lists exits automatically, but Inform 7 doesn't. A quick trip to the extension repository yielded Gavin Lambert's Exit Lister. It doesn't do exactly what I want (the PC should know a-priori where exits lead in this game) but it's a good start.

Now that I have map building figured out, let's see about things.

The remote control and the empty beer bottle are on the coffee table.

Again, this was accepted immediately but... the items in question were nowhere to be seen. Looking in Index→World gave me the answer (and proved that Inform really is designed to be used with its GUI): I had just defined a bunch of out-of-play objects. The easy fix was to add The coffee table is here. to the code. As a nice touch, making the table a supporter (implicitly) also nailed it in place. Also note that all rooms are lit by default, as they should be. Perfect.

The next one wasn't as straightforward.

The TV set is here. "Unfortunately, it's broken. Again." It is fixed in place. It is a device. Instead of switching on the TV: say the description of the TV.

Sure, skimming through the manual allowed me to declare it a device, and to handle "switch on" correctly, but now its description appeared in the room, and looking at it gave me a blank line. Turns out, I had set its initial string instead. The fix was trivial:

The TV set is here. The description is "Unfortunately, it's broken. Again." It is fixed in place. It is a device. Instead of switching on the TV: say the description of the TV.

Now, to allow the player to watch some TV, or rather try to.

Watching is an action applying to one visible thing and requiring light. Understand "watch [something]" as watching. Carry out watching: try examining the noun.

Instead of watching TV: say "You look at the blank TV screen for a while. It's actually better than most channels."

Trouble is, no matter what I tried, "watch tv" always had the effect of examining it, as if my override didn't exist at all. Can you guess what I was doing wrong? Keep in mind, at this point I had been using Inform 7 seriously for only one hour; surely I was missing something obvious! Sure enough, after playing with other features of the system (and marveling at how you can include a help menu with a single line of code, or release a version playable online with a mouse click), and a little nap too, I figured it out: the verb "watch" already exists as a synonym for "examine". Chapter 16, section 3 of the manual gave me the solution: Understand the command watch as something new.

There are details I left out in the first room, such as the couch (The couch is here. It is an enterable supporter.) and descriptions for the remote/bottle. I also omitted the header line so far — "Catch That Cat" by "Felix Plesoianu". — simply because Inform 7 allows me to get away with it. Adding all that might just bring my Inform 7 code at the length of the original:

s = story("Catch that cat"); = "Felix Ple┼čoianu";

	"Living room",
	"This is you one-room apartment in eastern Bucharest."));
tv = thing("TV set", "Unfortunately, it's broken. Again.");
thing("couch").$use = thing("couch").$sit_on =
	"You rest on the couch for a few moments. Aah, comfy!";
thing("coffee table");
thing("remote control").is("portable");
bottle = thing("beer bottle", "It's empty and sad.").is("portable");

exit("North to balcony").altname("n").to(room("Balcony"));
exit("Northwest to kitchen").altname("nw").to(room("Kitchen"));
exit("West to hallway").altname("w", "out").to(room("Hallway"));

tv.$turn_on = tv.$switch_on = tv.$use = tv.description;
tv.$turn_off = tv.$switch_off = "It's already very much off.";
tv.$watch = function() {
	say("You look at the blank TV screen for a while.");
	say("It's actually better than most channels.");

Which, considering one is based on natural language, while the other was specifically designed to be a very compact notation, is quite an achievement! And note that in the Jaiffa code above, no exits are created back from the adjacent rooms, so I7 wins even before counting its many technical advantages.

I was planning to end the article here, but the next day I continued playing with the system, and I must say that the experience changed completely. It turned into one of swearing, browsing the manual frantically and trying things at random. Look, I understand that I7 is still a programming language in the end, but seriously? You have three different ways to phrase something and none of them works? And you give me a verbose, friendly but totally useless error message? Sorry, but this just reinforces my view that a computer should never, ever try to second-guess a human being. Give me a real programming language. Next time, TADS 3.