Posts Tagged ‘perl golf’

Make All Sad at TWIFcomp

April 26th, 2010

TWIFcomp is a competition for works of interactive fiction with source code not exceeding the length of a tweet. (Unlike on Twitter, whitespace characters do not count toward the total.) There were 61 entries, of which the largest proportion were in Inform 7. My entry, Make All Sad was written in perl. If you really insist on playing it unspoiled, and you have perl installed, you should go there before reading any further.

Aaron A. Reed at >TILT AT WINDMILLS has a good post highlighting a selection of the games. The space of 140 characters really only suffices for one gag, but a pretty wide variety of gags were tried. Adam Thornton gets a special, “if they ever do this again there will have to be a new rule” award for encoding a full length game that’s been years in the making as several hundred megabytes of whitespace characters, with a short perl script to decode it. (Assuming that gets a normal release soon, I’m looking forward to playing it.)

Choosing to use perl instead of a dedicated IF language meant I didn’t get a parser, world model, built in verbs, or even a prompt for free. (I spent 9 characters on the “>” prompt, which was extravagant, but I felt it necessary to give my game the feel of IF.) I do however get text mangling for cheap, and a good set of general programming operators.

My first important design decision was that I was not going to attempt to parse input, but would instead map the length of the input to the effect. This way, you could type any normal IF command, (“TAKE ROCK”, “GO NORTH”, etc.) and it would have an effect on the game, giving the illusion that there is a parser doing something there. (For the sake of having a better way to leave the game than CTRL-C, I did catch just one command: QUIT.)

The other piece of the puzzle was creating something not utterly trivial to do with this piece of information. I settled on having a set of three “objects” and a set of three possible adjectives, with a goal of getting all of the adjectives into a particular state. The game would print “The x is y.” for each object and its current adjective. (In the end, I could find no other way to excise the last character I needed to get down to 140 but to change “The” into “My”. I’m actually happy with that; this way feels more flavorful and implies an NPC behind the words.) Saving characters by having the nouns and adjectives share their final letters was an idea I had before I started coding, and gives the output a bit of visual poetry.

Finally, I needed a function to map the input length to the effect. I wanted different lengths to do different things to the three adjectives, sometimes changing all three, sometimes keeping some of them the same. And I needed something extremely terse. The shift operator “>>” saved me here. The function has period 12, so “MAKE ALL SAD” (12 characters) has no game effect. As a fortuitous coincidence, none of the 12 possibilities can win the game in a single turn.

The rest was perl golf, as the pastime of writing perl programs in as few (key)strokes as possible is called. There is a primer here that gave me some helpful ideas. In standard perl golf, whitespace counts; since it didn’t here, initializing the noun/adjective array by splitting on the space character was a win.

If I had a few more characters, I would have liked to end the game with a victory message in the event of all being sad. But I’m reasonably happy with how it turned out.

The source:
@o = split ' ', 'ma cu to sa re od';
@s = 0..2;
do
{
    map
    {
        $a = $s[$_] += $x >> $_;
        printf "My %sp is %sd.
", $o[$_], $o[$a % 3 + 3]
    } 0..2;
    print ">";
    $_ = <>;
    $x = y///c - 1
}
until /quit/i

Edit: Naturally, after the competition deadline and after I wrote up this post, I found a couple good ways to squeeze out a bunch of characters, and a feature I could add that could fit into the extra space. The new feature is that “G”, the standard abbreviation for AGAIN in IF, repeats the last command. This only took 11 characters to achieve.

The code for the new (and hopefully final) version follows:

@o = split ' ', 'ma cu to sa re od';
@s = 0..2;
do
{
    print "My $o[$_]p is $o[($s[$_] += $x >> $_)  % 3 + 3]d.
" for 0..2;
    print ">";
    $_ = <>;
    /^g$/i || ($x = y///c - 1)
}
until /^quit$/i