Search Results for: quixe

Introducing Lectrote, an interpreter

Today I posted the beta of Lectrote, a new IF interpreter application for Mac, Windows, and Linux. This is both more and less exciting than it sounds!

If you're familiar with the IF scene, you know that there are several applications which can be used to play these games. Zoom (Mac), WinGlulxe (Windows), and Gargoyle (multiplatform) are the most commonly used. And then there's Quixe, which is a Javascript-based interpreter used on iplayif.com and other web sites.

When I was looking to release Hadean Lands as an app, I found that none of these were really what I wanted. Zoom is unmaintained and buggy; WinGlulxe is weird about scrolling; Gargoyle has problems on hi-res displays. (I'm summarizing, it was a long messy story.)

Quixe had the UI that I wanted -- no surprise; it's the one I wrote the UI for! -- but it wasn't really meant to be used as an app. It exists as a web page, or a component of a web page. Also, it's slow. So I put it aside and went with Gargoyle.

However, the long messy story didn't end there! A few weeks ago I was gazing over the endless cycle of dev-tools and noticed Electron. Electron lets you wrap up a Node.js tool as a standalone app for Mac, Win, and Linux. And Node.js is, well, I don't really know what it is but it's a web thing. Seems ideal, right? Stuff Quixe's web page into Electron and we're done.

It wasn't quite that easy. Node.js has full filesystem access (unlike a web page), so I had to extend Quixe's load/save system to deal with ordinary files. (So you can exchange save files between Lectrote and other interpreters.) But that was still pretty easy. I stuck the IF postcard in a menu, too.

And now you can try it.

So what does this have to do with getting Hadean Lands onto Steam? Well, it's a very simple tweak to drop a Glulx game file into Lectrote. Then you've got a Mac/Win/Linux app that plays a single game. And it looks nice and the text layout is pretty and you can adjust the font size without editing a text file.

I haven't done that yet. I'll have to adjust the menus -- knock out all the support for opening multiple games.

More important, I'll have to add autosave. Right now, if you're playing a game and you close the window, your game is gone. Hope you typed SAVE! That's okay for an interpreter (used by IF habitués), but it's not ideal. It's really not acceptable for a Steam standalone game release.

Autosave for Glulx games is a bit of a nuisance, but I got it working on iOS. I will get it to work with Quixe. It will just take a few more weeks.

...oh, and then there's the speed. I mentioned that Quixe is slow, right? It's faster than it was but it might not be fast enough for Hadean Lands. If you own HL for Mac/Win/Linux, try it! In particular, try loading a mid-game save file and typing a command which requires many stages, like GO TO BAROSY.

(If you don't own HL, may I remind you that it's on sale for the next two days? I probably don't have to. But I do it anyway.)

Anyway, I may try plugging a different Glulx VM into Lectrote to speed it up. I can probably run RemGlk/Glulxe as a subprocess of the Node.js server... We'll see.

For now, Lectrote is a multi-platform interpreter app which has the UI I want, and that's a good start.

Posted in Zarfplan | Tagged , , , , , , | 8 Comments

Javascript wonkery

Here I will take a break from the ever-burbling stream of IF and Myst news, and talk about Javascript optimization.

You could say it's relevant because it came up in a Quixe bug report. ("I tried loading Emily Short's Counterfeit Monkey.... Load time dropped from 29.4 seconds to 10.4 seconds...") IF interpreter improvements are a high priority for me -- particularly if they could be big speed improvements for a fairly small code change. Double-particularly if they imply I've had crappy Javascript code out there for years.

Whoops.

I usually build my Javascript libraries according to the private namespace pattern. I can't remember where I learned this. I assume it originated in this blog post (Douglas Crockford), but I use the cleaner layout described here (Ben Cherry) or here (Todd Motto).

With this trick, all your internal functions and variables wind up hidden from the outside world. You decide which ones to export, and the rest remain private.

Here's a toy example:

Lib = function() {
    var counter = 0;
    var prefix = "The current value is ";

    function add(val) {
        counter += val;
        return counter;
    };

    function get() {
        return prefix + add(0);
    };

    return {
        add: add,
        get: get
    };
}();

Here counter and prefix are private variables. The add() function increases counter and returns it. The get() function returns it as part of a string. (I've set get() up to call add(0) just to demonstrate that private functions can call each other.) Finally, we return an object which exports the add and get symbols. This becomes the global Lib object, so a user can call Lib.add() and Lib.get(). But there is no Lib.counter; that's private.

Crockford says "This pattern of public, private, and privileged members is possible because JavaScript has closures... This is an extremely powerful property of the language." Okay, that's top-grade hand-waving. What he means is "Javascript is a terrible language, but it has stolen enough features from other languages that you can pull this crap off if you're incredibly careful and don't mind confusing onlookers."

Anyhow. The trick works pretty well until you start constructing Javascript functions on the fly. Let's extend our example:

Lib = function() {
    var counter = 0;
    var prefix = "The current value is ";

    var compiledfuncs = {};

    function add(val) {
        var func = compiledfuncs[val];
        if (func === undefined) {
            var text = "counter += " + val + ";";
            text += "return counter;";
            func = eval("(function func() { " + text + " })");
            compiledfuncs[val] = func;
        }
        return func();
    };

    function get() {
        return prefix + add(0);
    };

    return {
        add: add,
        get: get
    };
}();

What the heck is going on in add()? Imagine that this is an IF virtual machine interpreter, like Quixe. We're doing just-in-time (JIT) compilation. We take a Glulx function -- that is, a string of Glulx opcodes -- and convert it into a Javascript function. The browser's Javascript engine will then convert that function into native machine code and the result will run really fast.

Since this is a toy example, our only opcode is "increase counter by N". When we see a call to add(1), for example, we convert that into this Javascript function:

function func() {
    counter += 1;
    return counter;
}

We eval that source (compile it) and stash the function (in case another add(1) comes along). Then we execute it.

So that's great, and it works. But if you profile this in Chrome, for example, you'll see a couple of little yellow warning flags:

  • Not optimized: Function calls eval
  • Not optimized: Reference to a variable which requires dynamic lookup

You see, Javascript is a terrible language, and its scoping model is a haystack of ideas jammed together without any planning, and it can never be fixed because backwards compatibility. Certain operations in closures are legal, but slow.

(To be fair, every language's scoping model sucks. You know how the only hard problems are naming and cache invalidation? This is naming. But Javascript is worse than most.)

We could get rid of the eval() if we used a Function() constructor:

func = new Function(text);

But then the code would fail, because the function would exist outside the library closure. It wouldn't have access to the private variable counter.

I fussed around with a couple of different solutions. I tried inner and outer closures, I tried using this a couple different ways. None of them worked. (Crockford progresses from hand-waving to passive-aggressive sniping: "...an error in the ECMAScript Language Specification which causes this to be set incorrectly for inner functions." You tell 'em.)

I eventually settled on this:

Lib = function() {
    var self = {};
    self.counter = 0;

    var prefix = "The current value is ";

    var compiledfuncs = {};

    function add(val) {
        var func = compiledfuncs[val];
        if (func === undefined) {
            var text = "var self = this;";
            text += "self.counter += " + val + ";";
            text += "return self.counter;";
            func = new Function(text);
            func = func.bind(self);
            compiledfuncs[val] = func;
        }
        return func();
    };

    function get() {
        return prefix + add(0);
    };

    return {
        add: add,
        get: get
    };
}();

Our compiled function can't get access to the private namespace. We need a second namespace which can be handed in for that purpose. We'll call that self. We create self up top, and put the counter in it. We'll have to write self.counter to access it now.

To hand self in to our generated function, we call bind. This is a confusing Javascript feature which lets us glue any object in as the function's this. Then we compile it this way:

function func() {
    var self = this;
    self.counter += 1;
    return self.counter;
}

This is more verbose, but it works. And if we check it in Chrome's profiler, the yellow warning flags are gone.

Note that if we wanted the generated function to call private methods, we'd have to copy them into the self object too:

function get() {
    return prefix + add(0);
};
self.get = get;

Not too messy.

Now the real question is, does this actually make Quixe run faster? I don't know! I've started converting the code to this new model, but it's going to take more work. (The virtual machine has lots of state to shove into the self object.) The person who filed the original report got good results, but I'm not sure I've snagged the right tricks yet.

The toy example in this post seems to run a little slower with these changes. That's not encouraging, but of course it's not a realistic work model. Hopefully I'll have real results in a few days.


UPDATE, May 29th:

Got an updated Quixe branch working.

Turns out the func.bind(self) plan is terrible. Performance doesn't improve as much as it should; on Firefox it actually gets worse.

Instead, I've given each generated function an explicit self argument, and called them all as func(self). With this plan, Quixe is 65% faster in Safari, 55% faster in Chrome, 10% faster in Firefox. Woot!

Tagged , , , , , | 11 Comments

PAX East 2011: Zarf's anecdotes

I wrote a whole lot about last year's PAX IF events, because that was my first PAX and everything was exciting and new. Now it's my third (two in Boston, one in Seattle) and... everything is ho-hum and tired? No. It was an exciting weekend. But I may gush less about it this year.

Day -1

I spent Wednesday running around collecting the inventory. That includes the projector screen we used (thanks to Rick Kovalcik for letting us borrow it), and also a whole pile of books for the IF Suite. And I'll get that list out of the way right now...

From Nick Montfort's collection:

  • CYOA 1: The Cave of Time, Edward Packard
  • CYOA 12: Inside UFO 54-40, Edward Packard
  • Neither Either Nor Or, Joey Dubuc
  • You Are A Miserable Excuse For A Hero, Bob Powers
  • Eunoia, Christian Bök
  • Exercises in Style, Raymond Queneau
  • IF Theory Reader, Kevin Jackson-Mead, Rob Wheeler, ed.
  • Persuasive Games, Ian Bogost
  • Genesis II, Dale Petersen, ed. (contains a rare interview with Will Crowther)
  • Heart Suit, Robert Coover (a story on shufflable cards)
  • Knock Knock, Jason Shiga

From my collection:

  • Creating IF With Inform 7, Aaron Reed
  • The Inform Designer's Manual, Graham Nelson
  • The Knot-Shop Man, David Whiteland
  • Riddle & Bind, Nick Montfort
  • A Telling of the Tales, William J. Brooke
  • Engines of Ingenuity, Kit Williams
  • The Book of the War, Lawrence Miles
  • Meanwhile, Jason Shiga
  • 3-Dimensional Maze Art, Larry Evans
  • The Hole Maze Book, Greg Bright
  • The Book of Signs, Rudolf Koch
  • The Book of Adventure Games 1 and II, Kim Schuette
  • Reality is Broken, Jane McGonigal

Last year we brought a lot of narrative-theory and game-studies books. This year I wanted the theme to be "playful books", because, after all, these are things that visitors might read for a bit while relaxing in the Suite. So I brought maze books, fairy tales, and CYOA books and parodies. Some of the fairy tales were about narrative theory and mazes, but that's because such things amuse me.

Day 0

Thursday some of us met up for brunch (the Friendly Toast, your home for ridiculously fancified breakfast food). Then, oh yes, Mike and Jmac and I dragged The Inventory over to the hotel and got the room set up. (We also got screwed at this point on the hotel room rate, but we wouldn't realize this until Monday.)

Dinner was at the Tavern in the Square. (Thanks to Mike for getting us a private room where we could carouse all night. In the matter of geeks getting together. Which is to say, drinking heavily and talking about software.)

Day 1

I scrambled to make flyers announcing the IF Suite, and barely got to the convention center in time for my first panel:

How to fund your game development project with Kickstarter -- Cindy Au, Andrew Plotkin, Joshua Newman, Evan Balster, Max Temkin

This wasn't packed, probably because it was early on Friday. I think about two-thirds of the room was filled. (I'm pretty sure that it was the last event at PAX that didn't completely fill up.)

I've blogged about my Kickstarter success before, so my contribution to this panel will not be news to you. I was joined by the creators of three other projects:

  • Infinite Blank, a multi-player, casual, very lightweight world-making videogame (or toy)
  • Cards Against Humanity, a card game in the style of Apples to Apples for cynical people
  • Human Contact, an RPG patterned after the stories of Iain M. Banks, Vernor Vinge, and Ursula K. Le Guin

Cindy Au is the community-manager person at Kickstarter; she set this up. We all talked about our projects and then answered questions. I completely failed to plug the IF events at PAX.

Interactive Drama: Dialogue as Gameplay -- Jonathon Myers, Daniel Erickson, Jeff Orkin, Aaron Reed, Dan Tanguay, Martin Van Velsen

I didn't make it into this panel; I saw the line and decided I wasn't up for waiting.

This was supposed to be a panel discussion between Jonathon Myers, Stephen Dinehart, Evan Skolnick, Emily Short, and John Gonzalez. As I understand it, four of the five panelists bailed. Emily was at PAX but completely hammered by the cold she brought back from GDC. I don't know the other stories.

However, the panel wound up with a fine list of substitutes. Aaron Reed, the author of Blue Lacuna and Creating IF with Inform 7, represented the text-IF side of the universe. Better yet, he didn't fail to plug the IF Suite, using the flyers that I smuggled into the room.

I ran around the expo floor for a little bit, and then it was time for:

Non-gamers gaming -- Caleb Garner, Tim Crosby, Heather Albano, Sarah Morayati, Andrew Plotkin

This was the first of our IF Suite events, and it was packed as expected. Of course packing the IF Suite is not exactly the same as packing a PAX function room, but we were still pretty pleased.

I'm not going to try to recap the discussion -- we'll post video eventually -- but we got around a variety of angles on the topic. My stumper question, or at least the question that made everybody pause and look thoughtful, was: "Are we talking about writing games for non-gamers, or writing games that teach non-gamers to be gamers?"

I got one of the convention center's patented Extremely Boring Sandwiches for dinner. (They must have been patented. Highly trained food chemists must have worked for years to develop a sandwich that boring. However, it was food.) We then gathered for:

Meet the IF community

...which means, we all hang out in the IF Suite. Just like the rest of the weekend, but we wanted to name a time for newcomers who might be hesitant about it.

And people showed up! It was exciting.

MIT Tunnel Tour

This was an impromptu expedition to visit the MIT steam tunnels (or at least the more interesting MIT basements). I didn't go along with this, because I wanted to stay with the room and continue to greet my loyal fans. Or stay with the room, anyway.

Marius Müller took some video: Video 1, 2, 3, 4 (on Youtube).

Day 2

Saturday was our big day, for circumstantial reasons: Dave Cornelson arranged for us to rent a full-sized hotel function room all day. (That's full-sized for a hotel. Still smaller than the monster PAX event rooms.) So we crammed all the events we thought would draw crowds into Saturday.

Oh, you want photos? Start with Mark Musante's PAX photo collection. Marius Müller and Jesse McGrew also took some, but those are on Facebook, so, you know, wear galoshes.

Our first event...

PAX Speed-IF

The topic list, shouted out from the audience: (And apologies to those of you who tried to shout and got overshouted -- it was disorganized in there.)

  • A character whose name starts with the letter "M"
  • Sending Jim and Kevin on a mission to locate something
  • The Tomb of the Unknown Tool
  • A 100 year old typewriter
  • Pluto
  • Braintree or Alewife
  • One of the titles on Juhana's poster of imaginary IF titles
  • Chicken fingers
  • Explosions
  • Vacuum

We had the traditional (two PAXes in a row is tradition, right?) crowd of people intently hacking away outside the IF room all afternoon. Looks like nine entries were turned in that day; you can download them from the Textfyre SpeedIF page.

Setting as character in narrative games -- Andrew Plotkin, Rob Wheeler, Stephen Granade, Dean Tate

The joke here is that I submitted this as an official PAX event. They didn't take it, because Irrational Games had submitted a panel that was essentially "Setting as character in Bioshock Infinite", and that was deemed to have more appeal to the PAX crowd. Fair enough. So we talked about settings in every game except Bioshock Infinite.

(We cheated a little, because while Dean Tate is with Harmonix Studios, he was with Irrational when Bioshock and Bioshock 2 were being designed. So he had some insights from that story-universe.)

This was fun; we basically gabbed about our favorite game settings for an hour. My panel-ending stumper was "What non-game setting would you love to see in a game?" but this turned out to be the kind of stumper where nobody has a great answer. Oh well.

Everybody Dies

We fired up the projector and played Everybody Dies by Jim Munroe. The run-through took about an hour, and then Jim answered a few questions from the audience.

The transcript will be up soon.

A lightning introduction to Inform 7 -- Jason McIntosh, Andrew Plotkin

Unfortunately we didn't get video of this; I was late getting back from dinner and so we didn't get as many laptops set up as we wanted. However, Jason recommends Aaron Reed's I7 screencast; it's the same sort of presentation.

IF Demo Fair

This was the IF event at PAX, and kudos to Emily Short for inventing the idea and making it all happen in just six weeks.

We packed the room with laptops -- and other hardware -- and packed those with sample games. In some cases, with full games. People circulated for two hours, trying everything and discussing it. It was a tremendously exciting place to be. If you found PAX's show floor to be a disappointment, you were missing the ferment of game-design discussion going on next door.

Emily covers a few of the Demo Fair entries on her blog. More detailed discussions will appear in the next issue of SPAG.

The one that I've been thinking about ever since PAX was Juhana Leinonen's Vorple, a Javascript library for animation tricks in an IF interface. This is not as frivolous as you might think. Web-based text can be very polished -- look at the CYOA engine Undum for examples -- and there's no reason IF shouldn't benefit from this.

Vorple showed in-line dynamic images, pop-up help, and smoothly-positioned overlay elements. It's not directly integrated with an IF system yet, but it clearly can be.

My job for the next two weeks is to integrate my old ideas about CSS for Glulx and Vorple's approach to dynamic content, and design a framework that will (a) fit into Quixe, (b) be practical in native (non-Javascript) interpreters, (c) be effective in native interpreters that choose to use HTML display (WebKit or whatever), and (d) be easily usable from Inform 7. Extra fun! But it's the next stage in my VM/API work, and it's time to start it.

Anyhow -- I don't want to make the Demo Fair all about me. There were a pile of other projects and games, including the promised Automatypewriter, so check out Emily's post and future discussion.

Speed-IF wrap-up

Everybody was worn out by the end of the Demo Fair, so we packed up the function room and retired to the IF Suite to look over the absurdly-named creations of the day.

Day 3

Sunday was deliberately light, but we did have time for:

Curveship -- Nick Montfort

Curveship was part of the Demo Fair, but Nick wanted to give a more in-depth presentation for IF cognoscenti. (Sorry about stuffing it into the smaller IF Suite, but it was mildly apropos to see his slides projected onto the unflat surface of an upturned mattress.)

Curveship is an experimental IF system (written in Python) which explores different ways of narrating stories. I keep writing one-line intros in that vein, and it doesn't seem to deconfuse people about what Curveship is. Basically, Curveship has two unusual qualities. First: its world model includes not just facts about the current world state, but a history of past world state, the actions that got from there to here, and (for NPCs) their knowledge of the world -- the subsets of the current and past states that they're actually aware of. Second: its text output system can easily switch point-of-view, tense (past or future), level of detail, and other narrative variables.

The result is not a fully mature IF system. The parser is simplistic, and the generated text is too -- the degree to which you hand-craft the output is somewhat (not completely) at odds with the templating that Curveship uses to vary the text. But the point is to explore these capabilities. Once we know what they're good for, then either Curveship can be improved or the features can be adapted to existing IF systems.

That leaves the question of what the features are good for, and that's an ongoing discussion in the community. I don't have a good handle on an answer. I certainly use point-of-view tricks in crafting IF; I vary descriptions based on the player's knowledge, distance, and state of mind. Do I need these features to be first-level constructs that underlie every object and description? I'm not used to working that way, but maybe if I were I'd be writing different games.

And then we packed up the room and went out to a fancy Mexican place for dinner. Followed by random card games in the hotel lobby until everybody was too tired to think.

Day 4

Brunch at the Friendly Toast again, followed by a quick expedition to the MIT Museum to see Art Ganson's work. Once again, two PAXes makes a tradition.

What have we learned?

We really need a bigger IF Suite next year. Holding a hotel function room for three days straight is certainly a possibility, but we can't serve snacks there, and it's not great for sitting and relaxing. This will be discussed further.

PAX itself was almost completely uninteresting to me this year. I think this is just a phase of the game industry. My first console love is plot-heavy exploration-puzzle-environment games, and they're out of style right now. It's not like I ever went to a PAX and saw lots of big-name games I wanted to buy; it's usually one or two a year. This year it was Child of Eden, I guess. (I'm discounting Portal 2, since there was never a chance I wouldn't buy it.) Smaller games I ogled: Warp, Fez, Blinding Silence.

Not really related to the above, except thematically: I spent the weekend wondering whether PAX was the best place for an annual IF Summit and Hangout. The fact is, we are lost in the crowd; we'll never regain the in-PAX visibility that we had when Get Lamp hit. We've had a solid game-design panel at each of the last three PAXes, and that's good, but it's not necessarily a reason to do all this other stuff at PAX. And indeed, quite a few people in our rooms didn't bother to get PAX badges.

The camelly straw for me was when I went to the PAX info desk and said "Can I put these flyers here?" (For the IF Suite and events.) I did this at PAX East and PAX Prime last year, and they said "Sure." There was a place for independent but related events on the table. This year they said, "Sorry, not permitted." That's for the big sponsors, not for the likes of me.

I feel like I want to be part of a game-design convention, not a game-consumer exposition. Of course I spent last week saying "must attend GDC in 2012", which I will, but that's crazy expensive -- not worthwhile for most IF fans. At the other end of the scale is Boston Gameloop, which I also attend, but which is probably too small to organize around. Where's the full-weekend Boston game-design conference with interesting out-of-town guest speakers and multiple tracks interesting to both indie developers and game studios?

I know, I know, the answer is "run it." Funny story: I went up to a local Boston indie game person -- I won't incriminate by name -- and said "We should run a conference." The individual in question looked at me, nodded wisely, leaned forward, and said "Fuck you."

Posted in Boston | Tagged , , , , , , , | 13 Comments