Historically this “black box” style of parser interpreter has been accomplished with running a cheap mode (only single window) interpreter in a subprocess. For many use cases that works great. But obviously you’re limited to plain text in and out without even any formatting. Running subprocesses also doesn’t work on some platforms.
RemGlk/GlkOte isn’t hard to implement and then lets you use as much of the full Glk API as you want. A minimal GlkOte implementation is only about 200 lines, and the cheap-glkote package, which wraps the GlkOte API, seems exactly like what’ve been saying you’d like.
Cheap-glkote does look like the best option so far, although still substantially more convoluted than Ink.
(Main problem with the remglk-as-subprocess option is it doesn’t work for web, and would be somewhat annoying to bundle & distribute for multiple platforms; conversely, the main problem with the web-based options is that they won’t work when previewing in the Godot editor. With the Godot integration for Ink I can write something that works when previewed in the editor, and export for multiple OSes and for web, and it all “just works”)
To be fair, that’s mostly because the Ink community has written interpreters for Ink in C#, C++, JavaScript, and so on. Since there hasn’t been as much demand for integrating Glk-based systems into other applications, there hasn’t been as much call to specifically implement a Glk Z-machine (or whatever) interpreter in each language you’d want to use—hence the subprocesses instead.
Yeah, that’s basically my original complaint (community priorities for what type of tools/interpreter implementations should be available not matching what I personally want).
My response on the API question is what people have said above. “GlkOte is basically what you’re asking for, and I apologize but what you’re asking for isn’t as simple as you hoped.” (And if you insist on simpler, that’s cheap-glkote.)
But there’s another point about embedding parser games, which is that the Z-machine (and later Glulx) were designed to be self-contained game engines, whereas Ink was designed to be a component of a game. This isn’t just a question of getting text in and out. The parser VMs have all their world state, map, and objects wrapped up in a compiled form which isn’t meant to be accessible from the outside at all.
(You can get at that info. I’ve done it, the BBC did it with Hitchhiker’s, there are other examples. But it requires occult levels of VM hackery.)
There just aren’t many use cases for a little parser game running inside a larger game that can’t pass any information in or out. I think this has been the bigger obstacle than the code side.
The issue I see with this is that you either commit to storing a history of the entire world state until all pending output has been rendered, or you accept that certain information will be long gone by the time you’re trying to render output. For instance, suppose when the player turns on a lamp, I want to list all the objects in the room in a particular format. In Inform or TADS that’s trivial, because I can examine the contents of the room at the time the output is being rendered. If I just store something like (turn-on player lamp), assuming it was later going to be rendered into “You turn on the lamp”, the world state may have changed entirely by the time of rendering. Of course the world is not so complicated that storing the last turn’s worth of history would be an issue, but knowing exactly what to store from which objects seems extremely fiddly. And that’s even assuming that all objects in the game have a similar layout. In my mind, part of the reason to write an IF engine in a general-purpose language is that authors would be able to write new objects with special-purpose implementations, which were usable in the game because they implemented the appropriate interface. So do they now have to implement storage of their history too, or what? It just seems like a can of worms. Any thoughts on that?
EDIT: I guess maybe you could include queries in your output templates, which would run when the action runs and collect the information you need?
Well, in theory you could still pass information in and out as text not intended to be displayed to the user and/or commands that are not available to the user.
What are going to do about player actions that don’t change the world state, but still need to have distinct output? For example, LOOK, WAIT, and INVENTORY don’t typically alter the world in any way, but they require distinct responses.
Those are still actions recorded in the event source, but I see a problem. If the visual state changes after that look command, pulling the world state for the previous look would report incorrect information.
The event source would need to contain all relevant information somehow.
Not a trivial problem.
Adding this information to my discussion with Claude and it confirms that maintaining the graph for this reason is optimal so saving a read only snapshot for each command is very simple.
To see this running, open your browser console and type:
CheapGlkOte.start_game()
The function will return a string containing the opening text of Adventure.
To play a turn, type something like:
CheapGlkOte.game_turn('east')
Those two Javascript functions contain the whole game engine.
As the page says, this is the simplest possible implementation. It throws away all formatting and the status line. It’s just a starting point. But it runs.