Iron ChIF: Season One Episode 2 (Lancelot vs. SomeOne2, using ZIL)

A couple things worth noting in Max’s snippet above:

Here we see a few things for the first time.

REPEAT is ZIL’s fundamental looping construct. There are a few other ways to write loops, but they can all be rewritten in terms of REPEAT. As the name suggests, the code inside will run repeatedly, until it explicitly breaks out of the loop or returns from the routine[1]. The ((X 0)) bit defines a local variable that only exists inside the loop.[2]

PUTB writes a byte into the table.

IGRTR? is short for “increment and test if greater” (or INC + GRTR?), which Inform programmers may know as the @inc_chk opcode. The loop will end when X exceeds 809—i.e., when it reaches the end of that 810-byte table.

The comment is referring to some of ZIL’s confusingly rich syntax for defining tables.[3]

(STRING), as used above, means that the table entries are bytes instead of words, and also that if you put a string in the table initializer, it will be compiled as a series of bytes in the table, instead of as a pointer to a string:

<TABLE (STRING) "initial">
;; Gives a seven-byte table spelling "initial" in ZSCII

<TABLE "initial">
;; Gives a two-byte table containing the address of the packed string "initial"

(Unlike a string array in Inform, a STRING table doesn’t automatically have a length prefix.)

ITABLE is a convenient way to define a table whose initial contents are repetitive: <ITABLE 100 3> creates a table filled with one hundred copies of the number 3.

In this case, the STRING table filled with 810 zero bytes would seem to be equivalent to <ITABLE 810 (BYTE) 0>… but perhaps the Iron Chef plans to put a string in the initializer later? Laying out such a large table by hand would be an unusual choice in most kitchens, but at this level, one hesitates to call anything accidental.


  1. In this context, the RETURN is breaking out of the loop, not returning from the routine, although the effect is the same since the routine ends after the loop. ↩︎

  2. Technically, it exists for the whole routine, because of how Z-machine locals work. But it can only be referenced inside the loop, and the compiler may reuse the Z-machine variable for other purposes elsewhere in the routine. ↩︎

  3. Confusing not only because of the variety of options, and not only because <ITABLE BYTE 100> and <ITABLE 100 (BYTE)> do different things, but also because I just noticed that one of those was documented incorrectly. I’ve updated the ZILF Reference Guide. ↩︎

5 Likes

4 Likes

So we have GLASH/TWAM = 144 in one kitchen and 810 not-yet-initialized strings in the other… it’s fascinating to speculate about the scope of something looking only at its groundwork.

I’m reminded most directly of Mathbrush’s Color the Truth, which, while it involves no actual time travel, does deal with state differences across both the present and multiple remembered time periods. That game controls the variations through rules triggered by changing which character Inform’s “player” variable is applied to; our chefs, most likely, will have to tie those rules to use of the required artifact.

I know doing this kind of work in Inform requires some custom code or extensions like Emily Short’s Room Description Control suite or Jeff Nyman’s Contextual Descriptions, but it sounds like Max has found a way to use ZIL’s built-in central machinery to make it happen (so long as the options are limited to a mere 810 variations).

8 Likes

I’m hitting a wall and will take a break. Maybe getting some fresh air helps, it was getting quite hot in my attic.

11 Likes

Oh, yeah, I know that feeling. So many times during my episode, I’d be struggling with some design question or bug, then have to leave the house to go, I dunno, pick up my daughter from the bus stop or something, and on the drive, in a different context, and more relaxed, I’d be able to figure everything out, at least enough to take the next step.

In general, this post reminded me that IF creation (like a lot of creative endeavors) are moments and periods of all sorts of different emotions: moments of joy; moments of satisfaction; moments of slog; moments of frustration. For ‘course correction’, the moments of joy were largely concentrated in times with unexpected worldbuilding: I would be describing a room or an object and suddenly my brain would fill in some unexpected detail, like why the doors to the great hall were wide, or why you couldn’t fly while wearing the ancient hunting cloak. (Actually I think I discovered the fact that you couldn’t fly while wearing the cloak while writing the cloak’s description.) Moments of satisfaction came when some puzzle finally worked from end to end: when you could trigger getting chased; when you could use X to escape; when you could then alternatively use Y to escape. When I was able to capture the essence of a conversation with the right turn of phrase. Slog-followed-by-satisfaction often came when I was bug-hunting (slog) then finally figuring it out and fixing the bug (satisfaction). (When I finally solved like eight different bugs at once when I finally realized I shouldn’t call ‘stop’ from inside an ‘Every Turn’, that was particularly satisfying. And more than a little galling, but hey.) Moments of frustration lurked around every corner, mostly when I ended up blocked from my joy/satisfaction moments: times when I couldn’t find the right phrase; when I didn’t have time to put in something I really wanted and the game really needed; when a bug just evaded me at every turn.

Writing a game is an emotional rollercoaster!

9 Likes

It is 1:04pm here and I know that I haven’t posted anything in some time. There is a lot going on, but I think I’m back on track. All NPC tracking has been mapped out and coded, and let’s just say it isn’t basic or easy. Furthermore, I really tried to avoid any sort of NPC, so I, uh, did something that may massively annoy any players. So, yeah. While I may admit here my chances of success are lowering by the second, I am going to not show any of that and try to be overly confident in every way so that this doesn’t become a depressing show, lmao.

Now! I’ve had my first set of testing back and there’s a lot of stuff to say. One of which is: the game, while technically fine, is extremely aimless - which, yeah, seems to be a common thread among my games and so I think I’m going to be spending most of my time on making it engaging without going too off the rails. And, uh, finding a name, lol. Maybe I’ll make some cover art and then come back and see if I have more motivation, because I love looking at and perfecting all the extra details of a piece of work - titles, cover art, blurbs, etc. (For example, if I’m listening to an album I’ll look at the track order over and over to see if I think it works, then try rearranging them to prove/disprove my point. That kind of stuff.)

For my semi-daily game code snippet, proving the point that I absolutely don’t know how to use macros:

;"If it's READBIT, it's a file.
  If it's CONTBIT, it's a folder."

<OBJECT DATA-OBJ
    (LOC LOCAL-GLOBALS)
    (DESC "Betelgeuse Pharmaceuticals, Incorporated")
    (SDESC "Betelgeuse Pharmaceuticals, Incorporated")
    (FLAGS CONTBIT)>

<OBJECT FILE-04
    (LOC DATA-OBJ)
    (DESC "04/12/21")
    (SDESC "04/12/21")
    (FLAGS READBIT)>
7 Likes

This is depressing. Let me sit in a corner and cry…

[error ZIL0404] <top level>: too many properties: 32 defined, only 31 allowed
    [info ZIL0403] this would be legal in other Z-machine versions, e.g. V4

I’ll see if I can work around this by creating some lookup functions.

RangeError: Invalid array length

This is new. I didn’t get a runtime error before. If nothing else, I am going to learn new things :smiley: .

4 Likes

I really want to help you with the first issue, because it’s actually quite an easy fix.

… But I’m not sure if I’m not allowed to. Read the second line again…

5 Likes

No probs here. I am here to get some true experience with ZIL. Reading manuals gets me only this far. This week motivates me to get down the trenches and start digging. Sometimes the easy things are overlooked. E.g. I wanted to access the DESC property of a time period object, and got this:

[error ZIL0207] /work/Projects/add69b1d2c204f2986a5bf7dc673d515/main.zil:534: undefined global or constant: P?DESC

This was the culprit:

<TELL "Time period:" <GETP <GET ,AGES .N> ,P?DESC> CR>

And then I remembered: within a TELL, I can use D to get the DESC of an object. Much simpler:

<TELL "Time period:" D <GET ,AGES .N> CR>
6 Likes

I believe the challenger has uncovered a… highly spirited interaction within the compiler. Naturally, once Keyboard Stadium’s battle has concluded, I will enter my own battle and examine the matter very carefully. Try to save a copy of the code that caused it if you can!

Ah, indeed. DESC, like LOC/IN[1] and FLAGS, is a pseudo-property: it appears in an object definition like a property, but it represents part of the Z-machine object that isn’t stored in a property.


  1. The location of an object can be specified with either (LOC ROOM-NAME) or, equivalently, (IN ROOM-NAME). ↩︎

4 Likes

Thank you for pointing that out. As someone who has never authored a parser game, I only thought about the difficulty of actually writing multiple descriptions for different time periods – I never realised displaying them properly in a game with an underlying world model can be its own challenge! In Twine, I would simply wrap all of the descriptions in an if/else statement. It turns out no world model can make certain things easier (but often it’s the opposite. Building a simple inventory for a puzzle game in Twine is a lot of work, and it took me like 5 games to do in a reasonable manner. Learning from scratch, in my first games, like Lux and even the 4x4 games, I mostly resorted to a disgusting amount of if/elses, because that was the one hammer I discovered early).

Now that I think of it, if I was writing a game with rooms, puzzles and time travel in Twine, if there were many changes between time periods, I could make the same room in each era a separate passage in editor, and the player would never know – the names displayed would be the same, because they don’t have to be passage names. I could also use “widgets” (Twine macros) for objects/puzzles that appear in different eras, or even include certain passages (like an interactive object that never changes, no matter the time period) in other passages. Is separating rooms like that possible in ZIL (and other parser systems), or does the underlying world model prohibit, for example, two areas with the same name?

I also see things are heating up for our contestants. If I were them, I would already be looking for the time-bending artifact in real life, to buy myself an extra couple of development days – but they’re probably too honourable for that! I’m in awe of the Challenger being bold enough to learn the cooking methods while working on the dish, and I admire Iron Chef’s sportsmanlike willingness to help his competitor.

That’s interesting to me, because I always have the name ready before I start working on a game. With a couple of my games, I actually came up with a name first, and I thought it was good enough it deserved a game. Sometimes, the name is all I have for a long, long time :wink:

11 Likes

I did something like this once with Escape From Summerland, in which the perspective shifts between a ghost, a monkey and a robot. This essentially meant writing three times the number of descriptions for the game. It was a fun challenge, to reimagine every thing from the different perspectives, but was so much work for so few rooms that I probably wouldn’t do it again!

I think this is a common design state to be in with parser games. The games are often open environments where players can, in their own sweet time, slowly poke at the furnishings until something happens. Creating forward momentum is often about giving motivation, usually towards something (treasure, answers to mysteries, plot resolution), but sometimes away (e.g. from being hunted). A feeling of progress can be engendered through opening up new areas, changing environments, giving new capacities, but even then it helps if the player has a feel for what they’re trying to achieve overall.

9 Likes

Agh, screw it. I don’t have anything to show. Oh, maybe except this…

I truly love using the overlay filters (like Multiply, HSL Color, etc.) so that’s exactly what I did with a ton of layers - and this is my cover. I’m kinda proud of it.

Anyway, what’s next? Maybe a bit of improving of my dry writing, lol…

11 Likes

Well, at least you got a working game. I keep running into matters which should be easy to fix. I copy stuff from existing games to save time but it doesn’t seem to work at all for me.

4 Likes

ZIL does suck in that way of having close to zero proper documentation that applies to the situation at hand… There’s a lot that makes you feel like maybe you should stop, then a week later you realise what it was and go “DAMMIT!” but in this case that’s too late.

5 Likes

I second this! This is why I’ve endeavoured to make my games character-focused. In a typical, old school parser game, NPCs are pretty static. They stay in one place and they mind their own business. Like well-behaved Victorian children, they are seen and not heard and only speak when spoken to!

Real people aren’t like that; they move about, they initiate conversations and they react to things other characters do. If you’re poking about in their furnishings, they will want to know why you are poking about in their furnishings and they will probably try to stop you doing it!

Interactions with NPCs can drive forward momentum by guiding the player to focus on what’s important. Why won’t Hubert Booby remove his hat? Why is Lord Hamcester so keen to distract you from the watercolour painting in his office?

So if you’ve gone against your own instincts and included NPCs in your game, and yet somehow it’s still feeling aimless, why not use your NPCs to help your player focus?

12 Likes

Talk about NPCs… Here’s one… I try to go for flavor since I can’t expect to win on a technical level.

Miyamoto Musashi is practicing his forms.
His movements are precise and unhurried,
each strike flowing into the next with quiet
inevitability.
 
> x musashi
He studies his surroundings briefly, then
returns to his practice. There is no ornament
to his style; only efficiency and intent. He is
preparing for the duel that will define his name.
 
> x sword
The weapon is simple and unadorned.
Its surface bears marks of repeated use,
worn smooth where it has been gripped
and struck countless times.
 
> take sword
Musashi does not release his weapon.
 
> break sword
You cannot do that.

But the maddening part about NPCs and their belongings is that I can’t get the sword to work when I place it in Musashi:

> x sword
You don't see that here.

I guess I’ll remove all portable items, they give me too many headaches.

13 Likes

Have you tried TRANSBIT on Musachi?

5 Likes

Zork had (FLAGS ACTORBIT INVISIBLE CONTBIT OPENBIT TRYTAKEBIT) on the Thief which carries a stiletto and a large bag. I haven’t heard about TRANSBIT before, I’ll give it a try.

Now this is dumb, but it works! But TRANSBIT is for transparency of closed containers, why would I need it here, it is already marked as OPEN…

I now have

(FLAGS NARTICLEBIT ACTORBIT CONTBIT OPEN TRANSBIT TRYTAKEBIT)

Not sure if that TRYTAKEBIT is needed, though. I don’t expect players to try and pick up people. Although…

5 Likes

INVISIBLE? That definitely should be removed. Not sure why they even used that when they could have just moved the thief between rooms…

4 Likes