Some thoughts on Terminator

I won’t be able to make the TheoryClub ParserComp discussion, alas, but I wanted to make kind of an advance contribution to it about my entry. It’s not really going to be organized enough to be a post-mortem, but hopefully it’ll be of some interest.

My entry is Terminator, not Terminator Chaser, which brings me to the first lesson:

There is a good chance someone else will think of the same joke as you. Oh well.

This was unapologetically a tech demo entered into a competition, though I did try to make it as much of a playable game as I could. Entering tech demos into competitions is frowned upon, but without the competition I’d never have got the impetus to put this together… and I hope it was worth the tech demo, and also that some people enjoyed playing it.

There were actually two techs being tried out here–the commands to multiple actors and the grid-based movement. When ParserComp was announced, I thought I should try to do something that needed to be done with a parser rather than hyperlinks without requiring too much guess-the-verb, and Daniel Stelzer’s Multiple Actors extension seemed like a good fit–I could keep the verb set under control and use the parser to give lots of ways of specifying the actor. I had envisioned some sort of cooperative heist, but couldn’t really figure out a way to make that go with the sunrise theme. Commanding a team of astronauts to scramble back to a ship from an encroaching sunrise seemed like a match, and also would let me try out the grid-based navigation and movement that I’d been meaning to work on. Plus if I set everything on a relatively featureless planetary surface it would cut down on the amount of text description I’d need to generate.

Before long I switched to robots rather than astronauts as the ones to command, because it would have gotten impossible to control if the actors could do complicated stuff–and it would also have gotten impossible to convey the command syntax to the players. And I wasn’t going to have enough time to come up with descriptions that went much beyond the robotic. I’d have liked to include more ways for systems to interact–the initial plan involved lots of different types of robots, possibly creating rockslides and digging and things like that–but pretty quickly I had to decide only to bite stuff off as I was able to chew it, as it were. So I wound up with a pretty simple gameplay; the astronauts were treasures scattered around the grid, the robots had to find them and bring them back. The division between scouts and haulers stayed, though, because it seemed like it could create a nice two-stage dynamic where first you explored to find the astronauts (most likely with the scouts) and then you brought them back. Also it allowed more different ways to describe robots.

Initially I wanted to have the player unable to address the robots by name, so that she could only say things like “every robot that can see a hauler, move toward the hauler.” So she would have to set up her own logic puzzles–having concluded that these three robots could see a hauler, and these two could see a peak, then this robot was identifiable; etc. Part of the idea was to make it necessary to use phrases like “every scout” rather than “alpha, beta, and gamma.” But my attempt to set up the understand phrases for “robot that can see a hauler” led to infinite loops. This was probably for the best for all concerned.

So, how did it work?

I was really pleased with how the grid navigation and sight worked. Let’s say I was really pleased that it worked. After I had motion set up I was able to leave it in place and not worry about it.

Vision took a fair amount of tweaking. Initially I had planned to take a “ray tracing” approach where the vision for each robot was calculated by spreading a cone out from the robot in each direction and reporting each thing that the robot could see in that cone, while keeping track of which parts of the cone were occluded. But I couldn’t figure out how to make this work, and there were few enough potential objects in the field of vision that it wasn’t too hard or computationally expensive to loop through every object that was close enough to the robot and try to trace a line of sight to the robot. On the other hand this was fussy with the terrain features that were spread across multiple squares (I had to severely tweak the rules for craters, since initially I was measuring from the center of the crater which is always blocked by the walls) and it doesn’t seem like it’d scale well to a less featureless environment. I’d really like to be able to scale it to a more featureless environment.

I also included a fairly elaborate system for figuring out when one terrain feature is partially behind another which is either entirely bugged or never applies because I set up the terrain generation so no terrain feature can ever be partially behind another. Maybe I’ll try tweaking the terrain system and see if it starts applying.

Another thing that worked out pretty nicely was setting everything to be privately-named and including Understand lines that only worked when the robot in question could see the named item. That seems like it might not scale to other projects, though.

It’d definitely be good to come up with more descriptive descriptions. It seems like shuffling things around to produce reports like “To the southeast it can see two scouts and a pointed ridge” shouldn’t be too bad. What I’d really like to do is have something like “To the southeast it can see two scouts in front of a pointed ridge”; and, better yet, instead of “It can no longer see Alpha” to have “Alpha has disappeared behind a rounded peak” or something like that. That’ll be more work.

As for how the multiple actors worked–well, I guess I learned not to try to do anything on top of a complex code base that I don’t understand unless I have a lot of lead testing time to correspond with someone who does understand it. That is, if I’m going to be upset when I wind up with a bug I can’t fix. The “You must supply a direction” bug was particularly cruel–a bug I had no hope of tracing that wouldn’t manifest in the IDE and that happened every turn in the release versions, and that I couldn’t have fixed myself if I’d understood it.

On top of that I wound up with some strange hacks to modify the behavior of the Multiple Actors extension–for instance, in order to get the Every Turn rules to run if a command to multiple robots gives a parser error for the last one (such as “robots, move toward astronaut” when only one robot can see an astronaut), I had to tie the every turn rules to printing the command prompt. This at least had the potential to break a lot of stuff–at least some of the time the code for ending the game was broken such that the game wouldn’t actually end when it was supposed to, but accepted one last command prompt and then ended. (This is why the endings have some odd prose about waiting–if the player encountered that situation I was hoping to get them to type “z” and then get the ending.) There was also some odd behavior with commands to multiple robots on the first turn and the first turn only, which I just avoided by forcing the player into a tutorial in which there was only one robot available on the first turn. Overall I’m lucky it even worked as well as it did.

Still one thing that seemed to work OK was the tutorial and the command syntax. I did the tutorial at the last minute (after the deadline for the first draft submission, in fact) and it’s crude in some ways–if the player goes off script the tutorial will get lost–but it seemed like few players got completely brickwalled by the format in which commands were entered, which is nice because it was nonstandard even for parser games.

As for the game; it was OK, I guess? Some people didn’t like tweedling around the grid looking for all the treasure, er astronauts, which is understandable and it’s a bit unfortunate that the comp format tended to put them under some pressure to play it. Others seemed to like it. I’m not sure if anyone was enthralled enough to want to tweak the settings to make it more challenging or whatever, though one of my goals was to make something in theory replayable. For what it’s worth I didn’t envision it as something that you ought to have to map–just doing a lot of general sweeps across the map should give you a fair idea of which robot is where and where you need to explore.

One thing that really didn’t work very well, I think, was the elaborate system in which higher terrain gets caught in the terminator sooner and sometimes it casts shadows behind it etc. None of this is conveyed to the player at all.

I have some other thoughts about the state of the Inform 7 parser and how easy it is to modify but I have to run now. Thanks to Carolyn, the judges and other participants, and especially my testers and Daniel!

So, my thoughts on the parser: A modifiable parser is something that should’ve come up in the missing tools discussion. Of course there are reasons why the Inform 7 parser, a very complicated piece of code that was built up over a long time, is the way it is; refactoring it would be an incredible amount of work and it usually works well enough for its purposes that it wouldn’t be worth it for most people.

But it can be overwhelmingly difficult to change the behavior of the parser except in some approved ways. I don’t even necessarily mean the kinds of things that are going on in Multiple Actors (which really does violate a lot of expectations the parser reasonably has–for instance, that only one command is really being issued every turn, so if there’s a parser error you can cut off the processing of the turn without worrying about whether previous actions that turn succeeded).

Take disambiguation, for instance. It took me a long time to start coding, mostly because of normal procrastination, but also because I wanted to make sure that “robot, go north” would cause every robot to go north rather than disambiguating, and I couldn’t see how to do that. One thing that would work would be, when the parser is disambiguating the addressee, to skip the command prompt and automatically feed the word “all” in instead; if “all” is entered at the command prompt Inform automatically tries a multi-action. And if it was an ordinary command prompt I could do that with a “For reading a command” rule. But I couldn’t do that for disambiguation, because responses to disambiguation commands don’t get processed with “For reading a command” rules as far as I can tell. It seems to me from some debug text that they run After reading a command rules but not Before reading a command or For reading a command rules. (I eventually worked this out by having “robot” etc. understood as a plural name when the game is trying to process the name of an addressee–I think this would scale really badly in situations where the player could interact with robots directly but I deliberately avoided that here.)

And I think the existing disambiguation situation already illustrates this. Currently I think the options for modifying the behavior of disambiguation are: use Does The Player Mean rules which can be hard to get to work; use an extension that rips out the disambiguation mechanism and replaces it with a different wodge of I6, and whose author no longer supports it; or, I guess, write your own wodge of I6? Which is not flexible.

Maybe the problem is just that I don’t know I6, and if I learned I6 I could hack the parser with relative ease. That’s not the impression I get, though. I did love Ron Newcomb’s I7 reinterpretation of the 6G60 parser, and even almost managed to do something with disambiguation in it once, but that’s outmoded now (also it had some weird array overflow error when dealing with disambiguation).

Anyway! As I said I’m not saying “Hey you should rewrite the parser so it’s easier for me to deal with” because that would be a heroic amount of work and also would presumably break everyone else’s code. It’s just that, thinking of it, I’m surprised this didn’t come up when we were talking about missing tools. And occasionally in discussions of The Way Forward For IF people say things like “We can do pretty much everything we want to in parser IF and the remaining technical innovations are going to come in choice-based IF.” The first half of that, at least, doesn’t seem right to me.

Since I7 I never really felt as though the parser was stagnant. I always got the feeling that people were getting bolder and bolder, and I7 has been accomodating enough (hurrah to Graham and the rest of the development team!) to allow more and more complications and expectations than ever before.

It is quite possible that if people start making games that necessitate more than one command per turn I7 will one day support it much more easily. It sounds a bit like patchwork, to be honest, but no more so than the Custom Messages extension - which was eventually rewritten into the Responses system. So maybe I7 will churn out a patchwork working system and later, in due time, polish it up.

Basically: I doubt that the parser’s finished growing, and before it gets rewritten it’ll probably grow some more. :slight_smile:

Out of curiosity, how would TADS deal with something like this? Does anyone know if any of these issues apply to TADS? Because if not, TADS has been ahead of the game even before I7 knew there was a game! (I’m just pointing it out because we keep talking about “the parser”, but what we really, really mean is “the Inform (7) parser”)

Yeah, one thing I’ve been wondering about is whether a lot of the things I want to do would be much easier in TADS. But it seems like TADS games aren’t as portable as Inform games–there’s the whole thing about having to do a special build for online play which seems intimidating.

Ayuh, I do wish TADS games were more portable. There’s a few interpreters around, and I do know that the maintainer of iFrotz has been quietly working on adding TADS support - a daunting task in what is already a fully-fledged ZMachine and Glulx interpreter! - but obviously the online thing is the deal-maker/breaker on portability.

From a complete layman’s POV, reading the instructions on making a special online build, I got the impression that if you game is like 98% of the TADS games out there you won’t have any trouble making the online version. Also from a complete layman’s POV, I’m not sure whether you can use Eric Eve’s “recent” simpler TADS libraries with the online release, you may have to use the standard ADV library (meaning, I’ve no idea whether it’s compatible with AdvLite. If it were, I’m sure it would make a big difference).

See, the good thing about comps like this is that it raises awareness on a load of things! :smiley:

Plus, you know, more released games. Yay!

As long as you aren’t doing any special display stuff (e.g. opening new windows) your source code should compile with both the standard and web libraries unchanged. Although, of course, the more ambitious your game is the more likely something in your code will become incompatible with one of the UIs.

I think Mike Roberts’s long-term goal is/was for all offline interpreters to support webui games, so authors wouldn’t have to use the standard UI any more. That hasn’t happened yet - and in fact Gargoyle still can’t play the most TADS 3 games regardless of which library they were compiled with. Currently you can play webui games offline with HTML TADS for Windows or (after some messing around) FrobTADS. But it’s hardly convenient.

No, Eric Eve’s adv3lite also has a webui version, so that’s not a problem.

I haven’t played Terminator, so I only skimmed matt’s posts, so I can’t say specifically what TADS 3 does or could do that would assist matt here. In general, I’d say that modifying the parser to deal with non-traditional mechanics is almost always going to be tricky.

The thing with the TADS 3 parser, though, is that it’s all written in TADS and heavily commented. So with enough patience, any TADS user should be able to figure out how it works and what needs to be modified. If you’re on Windows, you can even use the IDE’s debugging tools to help you study the code. Whereas (my impression is) the I7 parser is largely made up of terse, mysterious I6 code. I wanted to tweak the standard I7 disambiguation process for Robin & Orchid, but it turned out the bit I was interested in was in the I6 code, and heck if I could even figure out where to start with that.

Butbutbut the WebUI look is so UGLY in my offline T3 terp! :’(

(okay, okay, maybe I just need to customize it. But still! Doing away with the Standard UI! I hate doing away with things, generally…)

Hmmmm, so if you’re brave enough to delve into the Standard Rules and know some I6 you can hack away at the Inform parser in a similar fashion, right? And isn’t there the goal of eventually making all of the I7 parser completely I7ised? Isn’t there even an extension that already does it (though it may have broken with the new update)?

But if I7 will always translate to I6 I suppose it’s never completely possible to forego I6 knowledge if you want to get really messy. Unless, I guess, every single possible I6 action has a corresponding I7 action… and I’m so way out of my depth now I think I can see some weird deep-sea creatures staring at my soft flesh and wondering how it tastes. Yikes! Better tread back to the shore of ignorance.

Heh. Can’t argue with that. Though I’m not especially keen on the default look of the standard TADS UI either.

Sure. It’s just that hacking the Inform parser is (even) harder than hacking the TADS parser.

Oh yeah, that’s the other reason I’ve never tried TADS.

The idea that any kind of reasoned approval process occurred is, er, overstating things tremendously. :confused:

The parser is the most brittle part of the Inform technology stack. It’s the only part which hasn’t been completely rethought since Curses. (Major surgery and expansion, but not a complete rethink.)

It’s also the piece that I don’t know how to tackle in an incremental way. A “Modern Inform Parser” would require I6 language support and maybe VM support, because I don’t see how to do it without closures and reliable dynamic data structures(*). It’s already been dragged as far as it can go on globals and fixed-size arrays.

(* Are the I7 versions of these features sufficient? I don’t think so, although maybe I’m wrong.)

Yeah, “approved” was the wrong word. I meant something like “supported” or “implemented.” You know, it’s hard to change it except in the ways that it isn’t hard to change it.

What do you mean about the closures and dynamic data structures exactly? That sounds interesting but I’m not hep enough to really understand it.

You want a typed input to map to one or more commands, each of which maps to one or more actions, each of which maps to one or more constituent actions (if there’s a multiple object list that needs to be handled as separate actions). This can be naturally described with nested function calls with each call frame containing lots of state, perhaps including lists of objects.

But the parsing process has lots of state and lots of work to do. So either you have giant monolithic functions with zillions of local variables, or you break it up.

In a lexically-scoped language like Lisp, this is easy. (You can write inner functions inside other functions that have access to their local variables.) You can make it happen in Python (etc) with some tedious boilerplate about context objects. In Inform 6, the workarounds pile up until it’s not worth it. You wind up with the current parser: giant monolithic functions with local and global variables, studded with “goto” statements, plus occasional attempts to copy state onto the stack so you can pretend recursion is possible.

Gotcha, I think.

I’ve been pondering a bit about what a fully modular language would look like–one in which you could swap out the parser for a link interface, or swap out more different kinds of parsing (so that authors could write complex parsing rules if they liked, or rules that returned a parse “do this to this list of things,” or something like that). And mostly it seems to me that what I’m thinking about is a programming language–you have to make some assumptions about what your domain-specific language is for, or you don’t have a domain-specific language. But I wonder if what you say about letting functions pass their local variables to each other would be important for that kind of thing.