An experimental precompute mode for Glulxe

In mud discussion today, we got to talking about games that do a lot of computation at startup time. (Reliques of Tolti-Aph is a familiar example.)

I7 has good facilities for positioning objects at compile time (e.g., declaring their positions directly in the source code). Similarly, it’s good at setting properties, variables, and table contents at compile time. However, you sometimes need to run code for your startup state, and I7 doesn’t support that at compile time. You have to write a “when play begins” rule. Sometimes that takes a long time to run.

(To be clear, it’s no big deal to move a dozen objects around in a “when play begins” rule. That’s cheap. But if you have to move hundreds of objects, or fill in a table with hundreds of entries, you might see a noticeable pause when the game starts up. This only gets worse in Quixe, of course.)

There are several possible solutions to this problem:

  • Write a script that does the precomputation work in some other language and generates I7 code (or I6 code). Paste that into your project.
  • Save your game after the first move. Distribute the save file. Hack the interpreter to load this save file when the game starts.
  • Hack the interpreter to run one move, and then write out a new game file containing the new state.

I’ve whipped up a test of the third idea. That is, it’s a Glulxe (interpreter) feature to run (just) the precomputation work, and then write out a new game file with all that work already ready in memory.

This is currently available on github, at github.com/erkyrath/glulxe/tree/precompute – this is a branch of my repository; I haven’t decided whether to include it in my next release. You’ll have to download my source and build it.

To use it, you’d write your game to check a global flag, do the precomputation work, and then quit. (An I6 example is pasted below.) Then you’d run it through the interpreter, once, in precompute mode:

glulxe --precompute newgame.ulx game.ulx

The newgame.ulx file will run just like the original, except that it starts up with the precomputation work done. The flag you set tells your code to skip the precomputation work (and not quit), so everything continues from there.

Please note that this is an experimental facility. I have only tested it with the simplest I6 programs. If you try to work this into an I7 game, you will almost certainly generate unobvious bugs, because the I7 library code is not built for this kind of nonsense. (There is no correct place to integrate the precomputation step in the startup rules. Feel free to ask for details…)

If you use the --precompute option with an existing game, the new game file you generate will certainly be buggy. Play around at your own risk. :slight_smile:

The I6 sample:

Global precomputed = false;

Global glob = 5;

Object Kitchen;
Object lamp;

[ Main win;
    if (~~precomputed) {
        precomputed = true;
        glob++;
	move lamp to Kitchen;
        quit;
    }

    @setiosys 2 0; ! select Glk I/O system
    win = glk($0023, 0, 0, 0, 3, 0); ! glk_window_open
    glk($002F, win); ! glk_set_window

    print "Global variable is ", glob, "^";
    print "The lamp is in ", (name) parent(lamp), "^";
];

Probably I don’t understand the problem as I’m new to Inform, but my first thought was - back when I was programming for a MUD, when lots of calculations had to be done that the interpreter wouldn’t be able to do in time, we used “Call Outs”, equivalent to StartTimer under I6, and splitted the calculations.Like, do a part of the calculations every “second” - “turn” under Inform.

This is for cases where you need to do a lot of computation before the game starts.

In some cases you might be able to get away with spreading the computation over the first few turns. That’s a legitimate approach.

Looks cool. But if I understand it correctly, this would only be for deterministic calculations, right, not for random generation of content?

For everything, since it runs the game proper and saves a new VM image.

I think Victor is asking whether it would be useful for random content, and I think probably not. You could use it, but then everyone you distributed it to would have the same content, making it’s randomness useless.

This is really a tool for filling in data structures that are easier to programatically fill than to fill by writing code in the first place.