Porting Storyteller to Haxe: Part 2

Warning: Technical post ahead!

The past week has been a bit busy with talks, mail catchup and other accidentals, but I still made progress and wanted to share how it feels to rewrite Storyteller in Haxe so far...

Writing software with open source tools feels good

I SVN track my combo FlashDevelop + Haxe daily, and so I am aware what's going on and what features/fixes to expect. It is reassuring to know that if I bump into a nasty problem with any of them, I could potentially jump into the code and try to fix it. Whereas with Actionscript and Flash Builder I am at the mercy of Adobe's priorities, which might not be the same as mine.

Of course, I am still targeting Adobe AIR as the runtime platform, but I can always switch to NME, a multiplatform Flash API implementation by adjusting the code a bit. I might end up doing that anyway, since Adobe is not supporting Linux AIR anymore.

Local variable name reuse makes better code

In Actionscript, you are not allowed to do this:

for each(var i:Thing in things) {...}
for each(var i:Thing in things) {...} // WARNING: Duplicate variable
for each(var i:OtherThing in other_things) {...} // ERROR: conflict

Now the way I program now is way simpler than I used to, and sometimes I have fairly big methods involving several loops. Being unable to reuse variable names makes me write code like this:

for each(var i:Thing in things) {...}
for each(var i2:Thing in things) {...} // ARGH, I hope I don't write "i" by accident!
// or
var i:Thing; // ugly. I only care about "i" inside the loops
for each(i in things) {...}
for each(i in things) {...}

This is nonsense wrestling with the language. The haxe way:

for(i in things) {...}
for(i in things) {...} // compiles! Also, no need to type "var", "each" or the variable type.
for(i in other_things) {...} // other_things can contain a different type, it's fine.

But there's actually a bigger problem behind Actionscript's hoisting approach. Take this code:

trace(i); // "i" will be null, but NO COMPILER ERROR!!!
var i:String;
i = "hello";

trace(i); // compiler error
var i:String;
i = "hello";

In small code it seems stupid to make such a mistake, but in a mass of code while you are refactoring stuff around, it's easy to end up with a reference to an uninitialized variable! Bugs like this can be hard to find, and I shot myself in the foot a few times. Haxe makes this risk go away.

Snappier IDE, snappy compiler

Flash Builder is built on top of Eclipse, and sometimes it goes haywire and becomes unresponsive. FlashDevelop has been behaving pretty well up to this point, and it feels lighter.

The Haxe compiler is instantaneous so far, but let's see if it withstands the same amount of code as the Actionscript version, which used to take 11 seconds to compile (to be fair, I was using mxmlc instead of the better and faster ASC2).

FlashDevelop's code autocompletion is super slow for some reason. I hope it improves over time.

The rewrite is fun

I chose to rewrite the game instead of making a straighforward port because I wanted to take the chance of getting rid of a few core features the game is not actually using, and restructure old code that I know how to do better now. A major problem of the Actionscript version is that long stories took forever to solve, making the game unresponsive. Solving that problem involves an optimization of the core story simulator, which is the most difficult piece of code, so a major rewrite was in order anyway. Everything feels more solid now and raises my confidence in trying new features and refactoring old ones.

What's next

I am waiting for FlashDevelop to catch up with Haxe 3, which provides a mechanism to embed fonts that I had to solve with a plugin for now. Additionally, it would be nice to try features like Array Comprehension, Generic Functions and a more general Map class (isn't it great to be working on an evolving language?).

So for now, working on Haxe has been a pleasure! BUT the real proof will come as Storyteller's Haxe code becomes bigger and I test it on different platforms.


  1. I like post like this, good luck with the re-writing :D

  2. I'm curious, can you talk at some future point about your solver? Are you using a traditional AI planner or perhaps a constraint solver like Google's or-tools or just something you came up with yourself?

    1. Yes, I am planning on writing a series of articles about how the simulator actually works, one of them will even be technical.

      I am not using any traditional algorithms (that I know of!) for two reasons: 1) I need the results of the simulation to be easily predicted by a human being 2) In Storyteller's stories *order matters*... sometimes hate happens before love and sometimes it's the other way around.

      Reason 1) rules out mystic numeric simulations like the one used by The Prom and The Sims. 2) Ruled out all the logical solvers like prolog, which draw conclusions regardless of what happened when.

      I am always trying to figure out a cleaner, clearer way of implementing the simulator, but I think the current solution can't be substantially improved. That said, every now and then an idea comes up that incrementally makes it be more flexible, general and easier to work with, so I think I will wait until the model is fixed before writing about it!