how and when does I7 guess which indirect article to use?

You’re right about the space; my original post contains the leading space, but I hadn’t actually pasted that into (or from) my code. I should make sure I do that to try to avoid this kind of confusion! My bad.

There is a z-machine/Glulx check in PrefaceByArticle that I think explains what’s going on, but the I6 code here is opaque enough that I can only guess what’s going on from the annotations in Appendix B. Not that I understand I6 anyway, but occasionally I can puzzle out the logic, and this isn’t one of those occasions. As someone who is better versed in I6 than me you might have better luck.

So I looked at PrefaceByArticle and it appears we were both under the mistaken impression that Inform would override the article selection based on our definitions — whoops. The real problem here is: why does the same code yield different results when compiling to z vs. G?

Your supposition that it’s the fork in PrefaceByArticle is logical, but I can’t find anything messed up there. I’m by no means an expert, but I think this must be something in the list writer’s output. I’ll keep looking.

Here’s one problem that arises from the way Inform does things:

[code]Enigma is a room.

A cipher is a kind of thing. A cipher can be one-time, Caesar, or public-key (this is its cryptotype property). Understand the cryptotype property as describing a cipher.

Before printing the name of a cipher: say "[cryptotype] ".

One one-time cipher and one Caesar cipher are in Enigma.[/code]

(This took an embarrassingly long time to get compiled and working, BTW.)

Output:

PrefaceByArticle (or whatever is doing it) looks ahead at “one-time cipher,” sees that it starts with an “o,” and guesses wrongly that the indirect article should be “an.” Unlike with the unicorn and the hourglass, you can’t just set the indirect article of ciphers directly (well, in this case, every cipher should have “a,” but that won’t generalize). I guess in a case where you do know exactly which element is going to come after the indirect article you could do something like this:

[code]Enigma is a room.

A cipher is a kind of thing. A cipher can be one-time, Caesar, open-woffle, or public-key (this is its cryptotype property). Understand the cryptotype property as describing a cipher.

Before printing the name of a cipher: say "[cryptotype] ".

One one-time cipher, one open-woffle cipher, and one Caesar cipher are in Enigma.

The indefinite article of a cipher is usually “a[if the item described is open-woffle]n[end if]”.

A room has a cryptotype.[/code]

…you have to give a room a cryptotype to avoid this bug. (Also open-woffle isn’t a real thing, I got tired of looking up types of ciphers.)

But if it’s not entirely predictable which property is going to get printed first when you print the name of something, this won’t work. The code I’m working on allows for some more sophisticated and customizable ways of figuring out which article to use but as I said it doesn’t play nicely with the places where Inform uses indirect articles.

So I’d really like to figure out what Inform is doing by default, but that code does seem unusually opaque.

…OK, winkling through it there does ultimately seem to be a call to LanguageContraction in Language.i6t, which checks whether the letter in question is aeiouAEIOU. If I could figure out a way to have that call an I7 phrase instead I might be able to avoid a lot of the machinery I’ve been trying to use.

[OT: Go GATORS!! and in your face LSU!]
Meanwhile back at the ranch…

Alright, this is really starting to piss me off. I thought the theory you originally posited that taking the beetle somehow changed the article of the jug was merely coincidence. However, I’ve tried different actions (jump, etc.) and none of them change the article for the jug under z-code. It doesn’t even matter if the player is holding the jug. Consider:

Since both the room description and the inventory list (both of which use the list writer) change only after taking the beetle, it seems like there are only two possibilities: the taking action somehow interacts with the list writer in a screwy way (which seems unlikely) or this has to do with containment (which seems a little less unlikely considering that the LW leans heavily on containment relationships).

As far as your cipher example — which doesn’t involve taking or containment — I’m not sure just how related these bugs / misbehaviors are. Ultimately, figuring out how inform chooses articles is the solution which of course, was the point of your OP.

Gah. I was convinced that it was something about overflowing StorageForShortName and so avoiding the call to LanguageContraction. And so I was convinced that making the string that you’re planning to print long enough would reproduce the weird jug behavior (in z-code). So I wrote an example with a super-long word before the name of the item, and then with a super-long phrase before the name of the item, and then with a lot of extraneous words after the name of the item. None of them reproduced the behavior; I’m getting output like

You can see an oblate jug and an oblong jug (with a whole bunch of filler nonsense to overrun StorageForShortName) here.

when I was expecting the last one to be “a oblong jug (with a whole bunch of filler nonsense to overrun StorageForShortName)” because, well, I included a whole bunch of filler nonsense to overrun StorageForShortName. I still am pretty convinced that the deal with the original example has to do with the amount of stuff that’s printed after “jug” tripping the test in the z-code branch that means StorageForShortName doesn’t get called, but I don’t know how.

I feel like I have a pretty good handle on the cipher example–there it’s going through LanguageContraction in order to figure out which article to print, and LanguageContraction just tests whether a text begins with aeiouAEIOU. Since “one-time cipher” does begin with “o,” LanguageContraction thinks its indefinite article should be “an.” If I could figure out how to make LanguageContraction call my I7 code instead, I think I could take care of this. It’s partly a question of exactly what you can put in those (+ +) markers that let you call I7 from within I6.

Coincidentally, the markers for calling I6 and I7 from each other are a pretty good emoticon representation of the mental state this leaves me in. (- -) (+ +)

[OT: Go MAGIC!! and suck it Mavs! Sorry, but yesterday was a good one for sports in our house — for a change. Anyway… ]

So I did some experimenting to verify your conclusion that extra - long names don’t affect the article selection and I concur:[code]The Lab is a room.

A ball is a kind of thing. A ball can be supercalifragilistickexpialidosious, Supercalifragilistickexpialidoozey, or extrasuperspecial (this is the dumb property). Understand the dumb property as describing a ball.

Before printing the name of a ball: say "[dumb] ".

In the Lab are a supercalifragilistickexpialidosious ball, a supercalifragilistickexpialidoozey ball, and a extrasuperspecial ball.

test me with “get ball / supercalifragilistickexpialidosious / get ball /extrasuperspecial / i / get ball / both / i”.[/code]
yields:[spoiler]Lab
You can see a supercalifragilistickexpialidosious ball, a Supercalifragilistickexpialidoozey ball and an extrasuperspecial ball here.

test me
(Testing.)

[1] get ball
Which do you mean, the supercalifragilistickexpialidosious ball, the Supercalifragilistickexpialidoozey ball or the extrasuperspecial ball?

[2] supercalifragilistickexpialidosious
Which do you mean, the supercalifragilistickexpialidosious ball or the Supercalifragilistickexpialidoozey ball?

[3] get ball
Which do you mean, the supercalifragilistickexpialidosious ball, the Supercalifragilistickexpialidoozey ball or the extrasuperspecial ball?

[4] extrasuperspecial
Taken.

[5] i
You are carrying:
an extrasuperspecial ball

[6] get ball
Which do you mean, the supercalifragilistickexpialidosious ball, the Supercalifragilistickexpialidoozey ball or the extrasuperspecial ball?

[7] both
supercalifragilistickexpialidosious ball: Taken.
Supercalifragilistickexpialidoozey ball: Taken.
extrasuperspecial ball: You already have that.

[8] i
You are carrying:
a Supercalifragilistickexpialidoozey ball
a supercalifragilistickexpialidosious ball
an extrasuperspecial ball[/spoiler]Note that the output is the same with both z and G. The article behavior works as expected. The only problem demonstrated in my example is that really long names can cause problems with disambiguation (because they’re shortened internally), but I suspect many of us have run into this before, so this isn’t really new news.

I’m working on getting your I7 code inserted into or replacing LanguageContraction. It would be helpful if I actually had your I7 code to work with. You could pm it to me if you’d rather not post it in unfinished form.

Oh thanks! The I7 code is actually pretty short and in decent shape; here goes:

[code]To decide whether (string - a text) starts with a vowel sound:
let the first word be punctuated word number 1 in string;
if the first word is a word listed in the Table of Words That Start With Vowel Sounds, yes;
if the first word is a word listed in the Table of Words That Don’t Start With Vowel Sounds, no;
if character number 1 in the first word is a vowel, yes;
no.

To decide whether (letter - a text) is a vowel:
if letter exactly matches the regular expression “a|e|i|o|u|A|E|I|O|U”, yes;
no.

Table of Words That Start With Vowel Sounds
word
“hour”
“hourglass”
“honest”
“yttrium”

Table of Words That Don’t Start With Vowel Sounds
word
“uniform”
“unicorn”
“united”
“United”
“one”
[/code]

(with the latter two tables being extended as you wish with whatever relevant words show up in your project). There are some other things I’d like to do here–insert a use option so you can make the comparisons case-insensitive, and hook in a rulebook in case the author wants to do some more complicated calculations about what starts with a vowel. (For instance, to allow calculation that you say “a 1000-watt bulb” but “an 11-watt bulb.”) But that can all be handled by adding extra stuff to the “To decide whether (string - a text) starts with a vowel sound” phrase; if you have a way of calling that phrase from I6 then I should be good to go.

…it occurs to me that I’ll probably need to name the phrase for this to work.

Dude, I’m getting close! Here’s the output of my test-bed without the inclusion:

This is with the inclusion:

As you can see, some of the problems are fixed, but some aren’t. Hopefully, I can figure out what went wrong. Either way, I’ll post the code shortly.

Excellent! Looking forward to it.

EDIT: Is this z-code, Glulx, or both?

Both have the same output in this example. I still can’t figure out why “honest” isn’t working correctly.

Post the code! Post the code! I wanna see it!

Sorry Matt, I fell asleep watching the Magic game. I don’t even know who won, but don’t tell me — I DVR’d it. :wink:

Okay, so here’s the dealeo: I couldn’t figure out how to get i6 to access the phrases in your I7 code (which should be possible, but whatever) so I took a different tack. I created a global variable accessible to both and used that in the replacement of LanguageContraction. It makes more sense if you see it so, without further ado:[spoiler][code]The Lab is a room.

An animal can be honest or dishonest. Understand the honest property as describing an animal.

When play begins (this is the stupid and should be unnecessary rule):
repeat with A running through animals:
let T be a text;
now T is the printed name of A;
if A is honest:
now the printed name of A is “honest [T]”;
otherwise:
now the printed name of A is “dishonest [T]”.

The unicorn is an animal. It is honest.
The yak is an animal. It is dishonest.

A block is a kind of thing. It has a number called weight. The description of a block is “It looks to be about [weight of the item described in words] pound[s].”
After examining a block (called B):
now the printed name of B is “[weight of B in words] - pound block of [B]”.

The yttrium is a block. The weight is one.

The uniform is wearable. The description is “It’s a United States Army uniform.”
After examining the uniform for the first time:
now the printed name of the uniform is “United States Army uniform”.

A unicorn, a uniform, an hourglass, a yak and yttrium are in the lab.

[Matt’s code - additions noted]

[create a global variable to be called later in i6:]
Current vowel sound is a number that varies. Current vowel sound variable translates into i6 as “vowel_sound”.

Before printing the name of a thing (called x):
let T be a text;
now T is the printed name of x;
now Current vowel sound is T evaluated.
[say Current vowel sound.]

To decide what number is (s - a text) evaluated:
if s starts with a vowel sound:
decide on 1;
decide on 0.

To decide whether (string - a text) starts with a vowel sound (this is vowel sound checking):
let the first word be punctuated word number 1 in string;
if the first word is a word listed in the Table of Words That Start With Vowel Sounds, yes;
if the first word is a word listed in the Table of Words That Don’t Start With Vowel Sounds, no;
if character number 1 in the first word is a vowel, yes;
no.

To decide whether (letter - a text) is a vowel:
if letter exactly matches the regular expression “a|e|i|o|u|A|E|I|O|U”, yes;
no.

Table of Words That Start With Vowel Sounds
word
“hour”
“hourglass”
“honest”
“yttrium”

Table of Words That Don’t Start With Vowel Sounds
word
“uniform”
“unicorn”
“united”
“United”
“one”

[end]

Include (-
Global vowel_sound = 0;
-) after “Definitions.i6t”.

Include (-

Constant LanguageAnimateGender = male;
Constant LanguageInanimateGender = neuter;
Constant LanguageContractionForms = 2; ! English has two:
! 0 = starting with a consonant
! 1 = starting with a vowel
[ LanguageContraction text;

!!! This is the old routine:
!if (text->0 == ’a’ or ’e’ or ’i’ or ’o’ or ’u’
!or ’A’ or ’E’ or ’I’ or ’O’ or ’U’) return 1;
!return 0;
!!! Out w/ old – in w/ new:
if (vowel_sound == 1) return 1;
return 0;
];
Array LanguageArticles →
! Contraction form 0: Contraction form 1:
! Cdef Def Indef Cdef Def Indef
"The " "the " "a " "The " "the " "an " ! Articles 0
"The " "the " "some " "The " "the " "some "; ! Articles 1
! a i
! s p s p
! m f n m f n m f n m f n
Array LanguageGNAsToArticles → 0 0 0 1 1 1 0 0 0 1 1 1;
-) instead of “Articles” in “Language.i6t”.[/code][/spoiler]

Here’s the result:

I had to replace the whole “Articles” section of Language.i6t, but only thing changed is LC (which is actually simpler since it merely uses the value your code calculates). I think this method is minimally invasive and therefore probably safer and more robust. Also, it means that authors using your extension won’t have to worry about the i6 code because they can control everything from I7. It still needs work to generalize things with either / or properties, but it’s a start. :slight_smile:

(P.S. I know one wouldn’t refer to yttrium with a lone indefinite article; I just left it that way to demonstrate that code works as expected.)

EDIT: Oh yeah, I forgot to mention that the output is the same regardless of which machine you compile to – yay!

OK, that’s very nice to get started with. I guess the stupid and should be unnecessary rule is, um, necessary because you have to look at the printed name property to figure out which vowel you need; and that means that you need to fold “honest” into the printed name in order to get it looked at. Presumably that’s why “honest” didn’t work before, because it was (I tried a quick fix and immediately sent the code into an infinite loop.)

The problem for me is that it’d get awkward if there are lots of things that could show up before the printed name, especially if they weren’t unpredictable and involved random substitutions. Like, if you had something where the code prefaced the name of the thing with “[one of]orange[or]red[at random]”, I’d worry that it might preface it with “orange” when it was time to calculate current vowel sound, and “red” when it came time to print, and we’d get “an red.” And the coding could get awkward.

Looking through the Standard Rules it’s definitely possible to call an I7 rulebook from I6; that’s how the Does the player mean rules get called. And I need to turn the procedure for checking whether text starts with a vowel sound into a rulebook anyway. The question is how I pass the parameter to the rulebook… or can I just draw it out from StorageForShortName or wherever it is? This may go back to another issue I’ve had, which is how you turn an I6 array (?) into an I7 text.

Well, I blundered through a whole bunch of things by trial and error, and I finally got something to apparently compile… but the story pane is blank and gray. That seems like it shouldn’t happen.

Here’s what I have right now, spoilered for ugliness:

[spoiler][code]The Lab is a room.

An animal can be honest or dishonest. Understand the honest property as describing an animal.

Before printing the name of an animal (called beast): say "[if beast is dishonest]dis[end if]honest ".

The unicorn is an animal. It is honest.
The yak is an animal. It is dishonest.

A block is a kind of thing. It has a number called weight. The description of a block is “It looks to be about [weight of the item described in words] pound[s].”
After examining a block (called B):
now the printed name of B is “[weight of B in words] - pound block of [B]”.

The yttrium is a block. The weight is one.

The uniform is wearable. The description is “It’s a United States Army uniform.”
After examining the uniform for the first time:
now the printed name of the uniform is “United States Army uniform”.

A unicorn, a uniform, an hourglass, a yak and yttrium are in the lab.

[Matt’s code - additions noted]

[create a global variable to be called later in i6:]
[Current vowel sound is a number that varies. Current vowel sound variable translates into i6 as “vowel_sound”.

Before printing the name of a thing (called x):
let T be a text;
now T is the printed name of x;
now Current vowel sound is T evaluated.
[say Current vowel sound.]

To decide what number is (s - a text) evaluated:
if s starts with a vowel sound:
decide on 1;
decide on 0. ]

To decide whether (string - a text) starts with a vowel sound (this is vowel sound checking):
let the first word be punctuated word number 1 in string;
if the first word is a word listed in the Table of Words That Start With Vowel Sounds, yes;
if the first word is a word listed in the Table of Words That Don’t Start With Vowel Sounds, no;
if character number 1 in the first word is a vowel, yes;
no.

To decide whether (letter - a text) is a vowel:
if letter exactly matches the regular expression “a|e|i|o|u|A|E|I|O|U”, yes;
no.

The initial sound rules are a rulebook. The initial sound rules have outcomes vowel and consonant.

An initial sound rule (this is the basic initial sound test rule):
if “[short name storage]” starts with a vowel sound:
vowel;
otherwise:
consonant.

To say the/-- short name storage:
(- PrintShortNameStorage(); -).

Include (-

[ PrintShortNameStorage len i;
len = StorageForShortName–>0;
for ( i = 0 : i < len : i++ )
{
glk_put_char_uni(StorageForShortName–>(i + 1));
}
];

-)
.

Table of Words That Start With Vowel Sounds
word
“hour”
“hourglass”
“honest”
“yttrium”

Table of Words That Don’t Start With Vowel Sounds
word
“uniform”
“unicorn”
“united”
“United”
“one”

[end]

Include (-
Global vowel_sound = 0;
-) after “Definitions.i6t”.

Include (-

Constant LanguageAnimateGender = male;
Constant LanguageInanimateGender = neuter;
Constant LanguageContractionForms = 2; ! English has two:
! 0 = starting with a consonant
! 1 = starting with a vowel
[ LanguageContraction text result rv;

!!! This is the old routine:
!if (text->0 == ’a’ or ’e’ or ’i’ or ’o’ or ’u’
!or ’A’ or ’E’ or ’I’ or ’O’ or ’U’) return 1;
!return 0;
!!! Out w/ old – in w/ new:
rv = FollowRulebook( (+ initial sound rules +) );
if ((rv) && RulebookSucceeded()) {
result = ResultOfRule();
if (result == (+ vowel outcome +)) return 1;
return 0;}
return 0;
];
Array LanguageArticles -->
! Contraction form 0: Contraction form 1:
! Cdef Def Indef Cdef Def Indef
"The " "the " "a " "The " "the " "an " ! Articles 0
"The " "the " "some " "The " "the " "some "; ! Articles 1
! a i
! s p s p
! m f n m f n m f n m f n
Array LanguageGNAsToArticles --> 0 0 0 1 1 1 0 0 0 1 1 1;
-) instead of “Articles” in “Language.i6t”.[/code][/spoiler]

What I did was to borrow the code that Text Capture uses to load the captured_text buffer into the “[captured text]” text substitution and used that to get something that will load StorageForShortName into the “[short name storage]” text substitution. Then I looked at the code the standard rules uses to call the Does the Player Mean rulebooks from inside I6 and did some similar stuff to call the initial sound rules, adding temporary variables to LanguageContraction until it compiled… which probably wasn’t safe. But I was just expecting some abject failures, not a gray pane.

Anyway, it’s pretty clear that I need to figure out how to get Inform 6 and 7 to talk to each other. I think one thing that may be going wrong here is that “text” is an input to LanguageContraction but my I7 code is just trying to look at StorageForShortName. What would be ideal would be to pass an I6 array to I7 as a text variable, but I suspect that’s not possible… I haven’t had much luck getting people to explain I6-I7 communication to me in this way, though.

The best I’ve managed for passing strings from I6 to I7 was to make a global I6 buffer, have the I6 code write into it, and make an I6 function which just printed the contents of that buffer. Then an I7 say-phrase would call that I6 function, so by calling that as a text substitution, the I6 buffer would be printed out into another I6 buffer which would be converted into an I7 string.

As you might guess from that description, I would NOT recommend doing this for a serious extension.

Well that’s basically what I was trying to do except much more clumsily–I copied the code from Text Capture that lets you get the captured_text buffer into “[captured text]” (for Glulx):

[ PrintCapture len i; len = captured_text-->0; for ( i = 0 : i < len : i++ ) { glk_put_char_uni(captured_text-->(i + 1)); } ];

except I pointed it at StorageForShortName instead. Probably a better way to do it, instead of trying to access StorageForShortName directly, would be to use BlkValueCopy to copy that into a global I6 buffer, and point my text substitution at that? I’m sort of guessing about BlkValueCopy because I noticed that when I defined a text-based rule the call to it got translated into I6 as

FollowRulebook(363, BlkValueCopy(I7SFRAME, TX_L_53), true);

So that may be what you have to do in a serious extension. Seems pretty suboptimal, though; passing strings between I6 and I7 should be simpler than this.

EDIT: Have improved things from a blank story pane to a memory access out of range error. Will, perhaps unwisely, keep tinkering.

OK, I have a concrete I6 question, I think. In the following code:

[code]Include (-

Array I6Buffer buffer 250;

[ PrintI6Buffer len i;
len = I6Buffer–>0;
for ( i = 0 : i < len : i++ )
{
glk_put_char_uni(I6Buffer–>(i + 1));
}
];

-).

Include (-

Constant LanguageAnimateGender = male;
Constant LanguageInanimateGender = neuter;
Constant LanguageContractionForms = 2; ! English has two:
! 0 = starting with a consonant
! 1 = starting with a vowel
[ LanguageContraction text result rv;

!!! This is the old routine:
!if (text->0 == ’a’ or ’e’ or ’i’ or ’o’ or ’u’
!or ’A’ or ’E’ or ’I’ or ’O’ or ’U’) return 1;
!return 0;
!!! Out w/ old – in w/ new:
SOMETHING GOES HERE TO TRANSFER “TEXT” INTO I6BUFFER
rv = FollowRulebook( (+ initial sound rules +) );
if ((rv) && RulebookSucceeded()) {
result = ResultOfRule();
if (result == (+ vowel outcome +)) return 1;
return 0;}
return 0;
];
Array LanguageArticles -->
! Contraction form 0: Contraction form 1:
! Cdef Def Indef Cdef Def Indef
"The " "the " "a " "The " "the " "an " ! Articles 0
"The " "the " "some " "The " "the " "some "; ! Articles 1
! a i
! s p s p
! m f n m f n m f n m f n
Array LanguageGNAsToArticles --> 0 0 0 1 1 1 0 0 0 1 1 1;
-) instead of “Articles” in “Language.i6t”.
[/code]

What should I put to copy “text” into I6Buffer? I thought it should be BlkValueCopy but that doesn’t seem to be working.

Actually, there is an extension that makes it possible to detect whether the printing the name activity is run to choose the article or for actual printing.

So you would write:

For printing the name of the hourglass: if the print-stage is article-choosing: say "ourglass"; [So the article will be "an"] otherwise: say "hourglass".
I don’t know if it’s relevant to what you’re doing, but I thought it could be useful.

Update: dfremont helped me solve the copying problem. I have something mostly working now.

The remaining problem is that it messes up on variable text substitutions, because the text substitution gets evaluated once when loading the results of the printing the name activity into StorageForShortName, and again when actually printing the name, which means that the string that Inform thinks it’s printing when evaluating the article might not be the same as the string it actually prints. This isn’t just a problem with my code but a general Inform bug.

Natrium, thank you so much for reminding me of that extension! I do want to try for a more general solution; if you know exactly what might get printed after the article, it’s not that hard to get the article right. I think you can set the indefinite article of the hourglass directly to “an,” or you could do things like what I did above:

The indefinite article of a cipher is usually "a[if the item described is open-woffle]n[end if]".

where we know that what gets printed first in the cipher’s name is the cryptotype, which will either be one-time, open-woffle, or Caesar. (We’re trying to make sure you get “a one-time cipher” and “an open-woffle cipher.”) But the idea behind that extension looks to be extremely useful for the things I’m looking at to take care of the substitution problem. I can’t figure out a way to do it directly for the I7 bug, but there’s an indirect way of doing it:

[code]Include Print Stage Detection by Taryn Michelle.

The cached preface is some text that varies.

Before printing the name of the traffic signal when the print-stage is article-choosing:
now the cached preface is the substituted form of "[one of]green[or]amber[or]red[cycling] ".

Before printing the name of the traffic signal:
say the cached preface.

Crossroads is a room. A traffic signal is in the Crossroads.

Test me with “l/l/l/l/l”.[/code]

I will have to think about whether I can use something like this to solve the general problem for my article-choosing code. I won’t be able to use Print Stage Direction directly, because my code also rewrites Articles.i6t, but I can incorporate its modifications of Articles.i6t into my modifications, and then maybe I can cache whatever gets written into “[I6 Buffer]” and spit it back out at the name-printing stage. Thanks again.

Hmm. Well, it’s working, but I’m not sure why it’s working the way it is.

With Print Stage Detection basically folded in:

[spoiler][code]The Lab is a room. North Lab is north of Lab. Gary is a man in North Lab. South Lab is south of Lab. “The south Lab.”

An animal can be honest or dishonest. Understand the honest property as describing an animal.

Before printing the name of an animal (called beast): say "[if beast is dishonest]dis[end if]honest ".

The unicorn is an animal. It is honest.
The yak is an animal. It is dishonest.

A block is a kind of thing. It has a number called weight. The description of a block is “It looks to be about [weight of the item described in words] pound[s].”
After examining a block (called B):
now the printed name of B is “[weight of B in words]-pound block of [B]”.

The yttrium is a block. The weight is one.

The uniform is wearable. The description is “It’s a United States Army uniform.”
After examining the uniform for the first time:
now the printed name of the uniform is “United States Army uniform”.

A unicorn, a uniform, an hourglass, a yak and yttrium are in the lab. An Æsop anthology is in the lab. An oenology text is in the lab. The printed name of the oenology text is “œnology text”.

Color is a kind of value. The colors are blue, black, brown, yellow, orange, red, violet, indigo, gray, and white.

A mood urn is in Lab. Before printing the name of the mood urn: say "[random color] ".
A mood ring is in the mood urn. Before printing the name of the mood ring: say "[random color] ".

[Matt’s code - additions noted]

To decide whether (string - a text) starts with a vowel sound (this is vowel sound checking):
let the first word be punctuated word number 1 in string;
if the first word is a word listed in the Table of Words That Start With Vowel Sounds, yes;
if the first word is a word listed in the Table of Words That Don’t Start With Vowel Sounds, no;
if character number 1 in the first word is a vowel, yes;
no.

To decide whether (letter - a text) is a vowel:
if letter exactly matches the regular expression “a|e|i|o|u|A|E|I|O|U”, yes;
no.

The initial sound rules are a rulebook. The initial sound rules have outcomes vowel and consonant.

To skip upcoming rulebook break: (- say__pc = say__pc | PARA_NORULEBOOKBREAKS; -).

First initial sound rule (this is the eliminate spurious line breaks rule): skip upcoming rulebook break.

Last before listing nondescript items when the number of marked for listing things is not 0 (this is the eliminate another spurious line break rule): skip upcoming rulebook break.

An initial sound rule (this is the basic initial sound test rule):
let temp be the substituted form of “[I6 buffer]”;
[say “(DBG: [temp])”;]
if “[temp]” starts with a vowel sound:
vowel;
otherwise:
consonant.

To say the/-- I6 buffer:
(- PrintI6Buffer(); -).

Include (-

Array I6Buffer buffer 250;

[ PrintI6Buffer len i;
len = I6Buffer->0;
for ( i = 0 : i < len : i++ )
{
glk_put_char(I6Buffer->(i + 1));
}
];

-)
.

Table of Words That Start With Vowel Sounds
word
“hour”
“hourglass”
“honest”
“yttrium”
“Æsop”
“œnology”

Table of Words That Don’t Start With Vowel Sounds
word
“uniform”
“unicorn”
“united”
“United”
“one”

[end]

[Include (-
Global vowel_sound = 0;
-) after “Definitions.i6t”.]

Include (-
Global short_name_case;

[ PrefaceByArticle obj acode pluralise capitalise i artform findout artval buflen;
if ( article_choosing ) { ! prevent reentrant calls from also attempting to choose an article, thereby disrupting article-choosing for the original object. (This is okay – they will get their turn to print as requested later on, during the name-printing stage for the original object.)
print (PSN__) obj; return;
}
if (obj provides articles) {
artval=(obj.&articles)–>(acode+short_name_case*LanguageCases);
if (capitalise)
print (Cap) artval, " ";
else
print (string) artval, " ";
if (pluralise) return;
print (PSN__) obj; return;
}

i = GetGNAOfObject(obj);
if (pluralise) {
    if (i < 3 || (i >= 6 && i < 9)) i = i + 3;
}
i = LanguageGNAsToArticles-->i;

artform = LanguageArticles
    + 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases);

#Iftrue (LanguageContractionForms == 2);
if (artform-->acode ~= artform-->(acode+3)) findout = true;
#Endif; ! LanguageContractionForms
#Iftrue (LanguageContractionForms == 3);
if (artform-->acode ~= artform-->(acode+3)) findout = true;
if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
#Endif; ! LanguageContractionForms
#Iftrue (LanguageContractionForms == 4);
if (artform-->acode ~= artform-->(acode+3)) findout = true;
if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
#Endif; ! LanguageContractionForms
#Iftrue (LanguageContractionForms > 4);
findout = true;
#Endif; ! LanguageContractionForms

#Ifdef TARGET_ZCODE;
if (standard_interpreter ~= 0 && findout) {
    StorageForShortName-->0 = 160;
    @output_stream 3 StorageForShortName;
  article_choosing = true;
    if (pluralise) print (number) pluralise; else print (PSN__) obj;
  article_choosing = false;
    @output_stream -3;
    acode = acode + 3*LanguageContraction(StorageForShortName + 2);
}
#Ifnot; ! TARGET_GLULX
if (findout) {
  article_choosing = true;
    if (pluralise)
        buflen = Glulx_PrintAnyToArray(StorageForShortName, 160, EnglishNumber, pluralise);
    else
        buflen = Glulx_PrintAnyToArray(StorageForShortName, 160, PSN__, obj);
  article_choosing = false;
    acode = acode + 3*LanguageContraction(StorageForShortName, buflen);
}
#Endif; ! TARGET_

Cap (artform-->acode, ~~capitalise); ! print article
if (pluralise) return;
print (PSN__) obj;

];
-) instead of “Object Names II” in “Printing.i6t”.

Include (- Global article_choosing = false; ! flag to signal when printing to array for article determination -) after “Definitions.i6t”.

Include (-

Constant LanguageAnimateGender = male;
Constant LanguageInanimateGender = neuter;
Constant LanguageContractionForms = 2; ! English has two:
! 0 = starting with a consonant
! 1 = starting with a vowel
[ LanguageContraction text len result rv i;

!!! This is the old routine:
!if (text->0 == ’a’ or ’e’ or ’i’ or ’o’ or ’u’
!or ’A’ or ’E’ or ’I’ or ’O’ or ’U’) return 1;
!return 0;
!!! Out w/ old – in w/ new:
I6Buffer->0 = len;
for (i=0:i<len+1:i++) I6Buffer->(i+1) = text->i;
rv = FollowRulebook( (+ initial sound rules +) );
if ((rv) && RulebookSucceeded()) {
result = ResultOfRule();
if (result == (+ vowel outcome +)) return 1;
return 0;}
return 0;
];
Array LanguageArticles -->
! Contraction form 0: Contraction form 1:
! Cdef Def Indef Cdef Def Indef
"The " "the " "a " "The " "the " "an " ! Articles 0
"The " "the " "some " "The " "the " "some "; ! Articles 1
! a i
! s p s p
! m f n m f n m f n m f n
Array LanguageGNAsToArticles --> 0 0 0 1 1 1 0 0 0 1 1 1;
-) instead of “Articles” in “Language.i6t”.

Section - Adding pass-detection to the printing the name activity (from Print Stage Detection by Taryn Michelle)

To decide whether name-printing is choosing articles: (- ( article_choosing ) -).

A printing-stage is a kind of value. The printing-stages are article-choosing and name-printing.
To decide what printing-stage is the print-stage:
if name-printing is choosing articles:
decide on article-choosing;
decide on name-printing.

Section - Using Print Stage Detection

A thing can be variably-named. The mood ring is variably-named. The mood urn is variably-named.

The cached name is some text that varies.

Last after printing the name of a variably-named thing when the print-stage is article-choosing:
now the cached name is “[I6 Buffer]”;
[say “DBG: caching happened”.]

First before printing the name of a variably-named thing when the print-stage is name-printing:
rule succeeds.

First for printing the name of a variably-named thing when the print-stage is name-printing:
say the cached name.

First after printing the name of a variably-named thing when the print-stage is name-printing:
rule succeeds.
[/code][/spoiler]

Sorry the code is probably unreadable, it’s pretty much a palimpsest at this point.

The thing is that I’m not sure why I have to do this:

Last after printing the name of a variably-named thing when the print-stage is article-choosing: now the cached name is "[I6 Buffer]";

rather than use the substituted form of “[I6 Buffer]”. Using the unsubstituted form seems weird and risky, but if I use the substituted form it prints things like “…a œnology text and a ?nology text (in which is an gray mood urn) here” when it should be printing “a œnology text and a gray mood urn (in which is an orange mood ring) here.”

Anyway, something that’s working at all is cool–thanks for pointing me back to Print Stage Detection and saving me a lot of work!