CoffeeScript as a domain-specific language for interactive fiction

2012-06-22

coffeescript logo

While my latest gaming project fizzled out before I could announce it (better that way, arguably), and I've been distracted by other shiny things, something has been on my mind, and recently culminated in an epiphany.

Javascript is increasingly used to program computer games, and interactive fiction could not avoid the trend for long. That's a good thing for players, who get easier access to their favorite stories. Programmers on the other hand are in trouble because, you see, JS just isn't a very good language for the job.

Mind you, the original Adventure must have been written in Fortran IV, a.k.a. Fortran 66, which had terrible support for strings; that's one reason why the data was in a text file (though other reasons were more important). But even newer languages, while having all the tools needed to deal with text, are not so good at simply encoding it. Here's what I mean:

function init() {
	start_room = foyer;
	cloak.moveInto(player);
	say(
"Hurrying through the rainswept November night, you're glad to \
see the bright lights of the Opera House. It's surprising that \
there aren't more people about but, hey, what do you expect in \
a cheap demo game...?");
}

That's what the intro to Cloak of Darkness might look like in a naive Javascript implementation. Note how a multi-line string needs those backslashes, and everything must start in the first column lest we end up with unwanted whitespace. You can see it in Versificator and Undum -- it's ugly to say the least.

The alternative, namely concatenating successive strings, is no better:

	"Hurrying through the rainswept November night, you're glad to"
	+ " see the bright lights of the Opera House...";

That's like writing with a blunt pencil on paper that tears easily. You want to be focusing on what you're writing, not on the process. But don't blame the language! A general-purpose one simply can't be expected to flow naturally for every kind of code out there. A better metaphor would be a multitool: no matter how good it is, it just can't match up to a real toolbox.

This is where domain-specific languages come in. Professional programmers are probably familiar with SQL and regular expressions. For interactive fiction authors, the best examples are Inform 7 and Alan 3. It would be exceedingly difficult to write an address book program in I7, but a small adventure game with three locations is a single sentence:

	The Living Room is south of the balcony, southeast of the kitchen
	and east of the hallway.

Of course, the disadvantage is that I7 requires its own compiler, and a virtual machine for running the games. And while that's standard procedure in IF, there are reasons why it may not be what you want. For example, if you're making your own authoring system in order to learn what goes into it.

That was the point of Jaiffa; my second goal was to make expressing IF in Javascript feel natural. Judge for yourself:

	current(room("Hallway"));
	exit("North to bathroom").altname("n").to(room("Bathroom"));
	exit("East to living room").altname("e").to(room("Living room"));
	exit("West to the outside").altname("w")
		.to(room("11th floor"))
			.via(door("outside door"));

This approach is called a fluent interface. While elegant, it can only go so far. Note how little text I used; there are longer descriptions in the game, but none over two lines. That dictates what kinds of adventures I can make with Jaiffa, and Cloak of Darkness is right out. Which also means most typical text-filled games.

Are general-purpose languages unsuitable for the task, then? Not necessarily. By design, languages such as Lua and Ruby provide syntax shortcuts to make code look less like higher mathematics and more like plain English descriptions. But running either in a web browser is resource-intensive. We need a lighter solution... and we have it!

I've known about CoffeeScript for a while now, but had no use for it, since I can write JS that's as compact, clean and fast by hand. And then it struck me: that was missing the point. The real advantage of CoffeeScript is that you can write code that looks like this:

	room "Foyer of the Opera House"
		desc: """
		You are standing in a spacious hall, splendidly decorated
		in red and gold, with glittering chandeliers overhead.
		The entrance from the street is to the north, and there
		are doorways south and west.
		"""
		exits:
			north: """
			You've only just arrived, and besides, the weather
			outside seems to be getting worse.
			"""
			west: room "Cloakroom"

	room "Cloakroom"
		desc: """
		The walls of this small room were clearly once lined with
		hooks, though now only one remains. The exit is a door to
		the east.
		"""
		exits:
			east: room "Foyer of the Opera House"
			
	hook = supporter "small brass hook"
		desc: () ->
			"It's just a small brass hook, " +
			if this.has cloak
				"with a cloak hanging on it."
			else
				"screwed to the wall."

I dare say it looks more natural than most IF languages, and as a bonus it's truly at home on a web page. The only question is whether that would be enough to justify a new entrant in an already crowded market. Then again, there are other game families out there that could benefit from a DSL. But that's another story, which may lack an end.

P.S. The authors of the language have thought of it as well...

Tags: , .

(CoffeeScript logo used for illustration purposes.)