Let's Play/Read: Inform 7 manuals (Done for now)

Once the fix is in, there should be a Hyperbolic Jam, where all entries must make use of these functions.

8 Likes

This would be the most awesome jam in the history of awesome jams!

6 Likes

Coming to this late, but if you’re going to do a game with ordering people around (as you said you were inspired to) the first thing I encountered was the annoyance of getting utterly cryptic error messages (along the lines of “You hear no reply.”) when I’d just made typos. This is the entire motivation behind the Compliant Characters extension so I’d recommend it (though I haven’t finished writing it by going through every single standard library action yet, it handles “take”.).

It’s easy to go very wrong with either. This is what I suggest: I think “after reading a command” is best used for preprocessing (gentle manipulations) before feeding into the main parser. While “for reading a command” is best used in the form “for reading a command when ”.

I’ve attempted to avoid them even in my most invasive extensions; several times I designed something using them and then ripped it out in favor of something cleaner. I was looking through my code, and I found four ways I’ve used them which seem unavoidable:
– “after reading a command” for removing hard tabs from the command (Tab Removal extension). Preprocessing. This is quite safe.
– “for reading a command when is happening” to specify an alternative or restricted parser just for that scene (Michael Callaghan’s Questions does more of this). Also pretty safe.
– the parser sugar in my unfinished “Compliant Characters” extension which converts “tell John to juggle” into “John, juggle”, and sends it back through the parser using “for reading a command when the special reparse flag is true”. A lot of finicky bits (I only just figured out how to avoid advancing the turn count).
– “after reading a command” doing regexp replacement in an unfinished game which has multiple playable characters: this replaces “my” with “John’s” in a command such as “wear my hat”, depending on who the player is playing right now, and replaces “your” with “Jane’s” in a command such as “Jane, drop your hat” before further processing. That way the main parser sees “wear John’s hat” and “Jane, drop Jane’s hat”, and if those have been set up correctly, it handles everything correctly regardless of who you’re currently playing. This is cleaner than any other way I’ve seen of doing this. If I get the bugs worked out I’ll probably break it out into an extension.

I just keep writing unfinished games, working out how to do some little corner of the game exactly like I want it, modularizing it and turning it into an extension, rather than ever finishing writing a game; ah well, I guess that’s what I enjoy doing. All my extension documentation examples are offcuts from unfinished games which may never be published.

1 Like

I had read most of this, but I gotta say, the map-maker section was entirely new to me (I had skipped over most of the graphics-related stuff).

So I spent some time trying to get it to work, and sure enough, it’s not working in exactly the same ways @mathbrush found that it wasn’t working. It doesn’t respect most of the directives in the manual. If I try to specify a room size, the room outline just disappears. It doesn’t respect an overall font directive, though it will respect room-name-font of Duckpond set to "Spartan"

I spent a little while (OK, 6 hours) trying to debug this, and it seems that the parameters are getting lost. “Render EPS map.w” is supposed to be reading these parameters from a data structure, but it’s either picking up the default values, or it’s not picking up any values at all. I can’t really figure out why.

Hmmm. Given that the EPS file can’t be read by editors like Inkscape anyway, and isn’t being produced properly…

…at this point rather than trying to fix it, I think it might make more sense to write modules to:
– export the map in the JSON format used by trizbort.io, which is extremely straightforward
– or export it to the XML format used by Trizbort, which is almost as straightforward
– or export it to SVG, which is almost as complicated as EPS but much better supported by modern software – this, however, would require figuring out how to actually get the options passed into the renderer correctly, which is the problem affecting the current EPS implementation

There seems to be significant potential in the map-maker, but it’s obviously been subject to bit-rot.

3 Likes

Check out @DraconisParser Overrides for a clever structure to handle some things one might otherwise resort to “after reading a command” for.

2 Likes

Very cool! That should help a lot for several classes of use-case. Doesn’t really do the trickery I’ve been doing…

My uses for “after reading a command” so far are:

Tab Removal.i7x extension (I should talk about that one with you again, I really think this should be fixed in core Inform)

And this bit of trickery:

Politeness is a kind of value.  The politenesses are impolite and polite.
The command politeness is a politeness which varies.

Before reading a command:
  now command politeness is impolite;

After reading a command (this is the replace possessive words with names rule):
  Let cmdline be text;
  Let cmdline be the player's command;
  replace the regular expression "(?i)\bmy\b" in cmdline with "[printed name of player]'s";
  repeat with potential actor running through visible people:
    let T be the substituted form of "[printed name of potential actor]";
    if cmdline exactly matches the regular expression "(?i)\s*([T])\s*,.*":
      replace the regular expression "(?i)\byour\b" in cmdline with "[T]'s";
  if cmdline matches the regular expression "(?i)\bplease\b":
    now command politeness is polite;
  replace the regular expression "(?i)\bplease\b" in cmdline with "";
  if the command debugging option is active:
    say "Command: [cmdline][line break]";
  change the text of the player's command to cmdline;
  [Note that the parser will reparse after this, and it's not case sensitive]

This was written for a game with two possible player characters – the game asks you to select which character to be at the beginning, and the other one becomes an NPC. It’s a suprisingly clean implementation of “my” and “your”, given how problematic relative references can be if the player could be one of several characters.

My use for “For reading a command”:

in Compliant Characters.i7x – take a command parsed using the normal parser (“tell [someone] [topic]” or similar), use it to generate a new command (from the topic), and send it back through the parser using a “special reparse flag”. IMHO, this one’s nifty.

Chapter 1
Chapter 2
Chapter 3 Part 1, Part 2
Chapter 4 Part 1, Part 2
Chapter 5 Part 1, Part 2
Chapter 6
Chapter 7 Part 1, Part 2
Chapter 8
Chapter 9
Chapter 10
Chapter 11 Part 1, Part 2
Chapter 12 Part 1, Part 2, Part 3
Chapter 13 Part 1, Part 2
Chapter 14
Chapter 15 Part 1, Part 2
Chapter 16 Part 1, Part 2
Chapter 17 Part 1, Part 2
Chapter 18 Part 1, Part 2, Part 3, Part 4
Chapter 19 Part 1, Part 2
Chapter 20
Chapter 21
Chapter 22
Chapter 23
Chapter 24
Chapter 25 Part 1, Part 2
Chapter 26
Chapter 27 Part 1, Part 2

11 Likes

I had thought it was the case that if only one kind of thing had some given either-or property, then one could say just

X is [that property]

and the compiler would infer it was the appropriate kind of thing. But that’s wrong. What’s true is that

X is [any either/or property or condition (enumerated kind of value property) of objects or things]

is enough to let the compiler know “X names an object whose kind I should infer” and, in the absence of specifics indicating something isn’t a thing, it always defaults to thing. (This also happens if you say X is an object. X ends up a thing.)

Salon is a visited. doesn’t work, even though that is specific enough that the compiler could infer Salon is a room.

This works:

Platform is an enterable.
Mic stand is on platform.

to make platform an enterable supporter… but Mic stand is on platform alone would make platform a supporter (not enterable, per the default). Platform is an enterable, alone, results in a compiler error.

Phrases, meanwhile, can be written specifying any either/or property or condition for anything; you’ll just get a runtime error if you pass it an object that doesn’t provide that property. This compiles:

Lab is a room.

a thing can be larry, moe, or curly (this is its stooge property).

to poke (m - a moe) in the eye: say "wise guy!"

stooge1 is a moe. [ sufficient to make stooge1 a thing ]

the verb to nyah-nyah-nyah means the stooge property.

stooge2 nyah-nyah-nyahs curly. [ sufficient to make stooge2 a thing ]

when play begins: poke stooge1 in the eye;
poke stooge2 in the eye.

and outputs:

wise guy!

*** Run-time problem P47 (at paragraph 23 in the source text): Phrase applied to an incompatible kind of value.

Because an either/or property or condition is, of itself, a valid description of values (see WI chapter 6: Descriptions, besides to poke (m - a moe) in the eye, you can do:

A room can be spawnpoint.
Cradle of civilization is a spawnpoint room. [ You *do* need to say `room` here, unless something else is telling the compiler it's a room ]

when play begins: now the player is in a random spawnpoint.
3 Likes

I’m a little late to the party but still want to contribute so here I go…

I tend to translate Inform into traditional programming ideas so I can better understand what is happening. The way I now think about this concept is “compile time code” vs. “runtime code”.

By “compile time code”, I mean the things you define in your world data model before you build your game – your kinds and objects and the relations between those objects – and then you compile your code and this gives you the state of the world at the start of your game. These kinds of statements are called “assertions” in Inform.

By “runtime code” I mean anything that happens to the state of your world data after the game begins. The parser code that translates the player commands into matching actions. Tweaks to the activities (i.e. all the underlying pieces of the game loop). The rules for your actions and their “preambles” that identify under what circumstances those rules are triggered. And the code that does that actual conditional checking and data state changes and reporting back to the player (e.g. the say command), those are the “phrases”.

I don’t know if this makes things more or less clear for others, but this is the general model I keep in my head.

2 Likes

I will tell you what I particularly like about these phrase examples: they follow the baseline structure of how action rules and rulebooks work. The conditional if phrase is like a Check rule. The now phrase is like a Carry Out rule. The say phrase is like a Report rule.

These examples provide a really good idiom for designing and writing customized Before and Instead rules (and maybe even parts of After rules). How do the customized rules Check the player request? How do they Carry Out any changes in the state of the game world? How do they Report this change back to the player. It’s not always necessary to implement every phase into every Before, Instead and After rule, but I try to at least consider each one.

As a side note on all of this, it follows another computer interface model I keep in my head when thinking through IF games: the REPL model.

The player parser is the Reader that translates human text into something that the game can understand (an action with certain arguments like the actor, the noun, and the second noun).

Assuming the reading of the player text doesn’t cause a parser error, the rules Eval the command in two phases. First, it uses a Check to make sure nothing ridiculous is happening, like a player trying to eat a building. Then, it often changes the state of the data model in the Carry Out rule.

Next, the game needs to Print some sort of feedback to the player so they are at least somewhat aware that the state of the game world has changed, and this is done with the Report rule.

Finally, the game Loops back to the parser prompt and the whole thing starts all over again.

Headings are quite useful but what I am really looking forward to is this feature.

The ability to include multiple files disappeared after Inform 6 and I am looking forward to welcoming it back in Inform 7.

1 Like

I’ve seen different names for these object properties. I think these are called attributes in Inform 6. I think of them as flags or boolean properties. Inform 7 programmer’s Manual refers to them as boolean adjectives.

Inform 7 adds a feature where you can name both the set and unset versions of these either/or properties, for example locked and unlocked. If you can’t think of a good “opposite name”, you can just prepend a not in front of it, for example: the opposite of portable is both not portable and fixed in place.

Side note: in other programming languages, programmers typically slap an “is” on the front and give them names like isOpen and, in Lisp languages, programmers typically put a question mark on the end and name them like this: open?. The value,true or false, is answering the question posed by the name.

1 Like

Agreed. There is a real art to creating room descriptions and the subsequent scenery descriptions they beget. It can also be tricky naming things and understand-ing everything so they don’t conflict with portable objects, and each other.

As a completist, I sometimes find myself going down some deep examine rabbit holes as I keep introducing new nouns into each level of the descriptions I write. I have to watch myself.

I also try to make my portable object have completely different names from the scenery to avoid ambiguity problems but if I have a room with a river, for example, and the player grabs some water in a bucket, “water” is going to create some ambiguity trouble.

I also try to keep track of all of the nouns I introduce as I am writing description so my understand assertions cover them, but I have found it easier just to cut the fat and get rid of as many nouns as I can if they don’t add anything to the story.

Update: Okay, apparently, posters are limited to five replies in a row in a given post unless someone replies to a reply? I’m getting an error message about something like that, had no idea there was a limit. Anyway, I don’t feel like figuring it all out and I don’t really want to consolidate all of my postings, so I will stop here. Thanks for the posting, though, very useful stuff!

People had the manual in their heads as a whole when the topic was new (well, I did) and now they don’t (well, I don’t!), making it more work to interact. Nevertheless, I’m going to use my awesome forum power of not being you, to reply to you, to allow you to post more if you should so desire. Probably with continued expectation of a low reply rate.

-Wade

2 Likes

Yes, in general it’s better to make one reply than many in a row. You can quote multiple posts/people in one post, so you don’t need to reply once per post you’re responding to.

2 Likes

I still have confusion around degrees of certainty. I use usually as a good way to initialize a value that varies (i.e. variable):

txt is some text that varies.
txt is usually "foo".

However, I naïvely believed that always could set up a text (i.e. string) constant and learned otherwise after attempting to compile:

txt is some text that varies.
txt is always "foo".

Problem. You wrote 'txt is always "foo"' : but a variable can only be given its value straightforwardly or qualified by 'usually', not with 'always', 'seldom' or 'never'.

Which, of course, makes perfect sense because the text either varies or is always constant. Can’t be both. The compiler error also saved me the trouble of testing this with never – a strange concept, a string that will never contain a unique sequence of characters – and I have no idea what seldom would even mean in this context.

Eventually, I did find the way to declare and implement a constant, which does utilize always:

txt is always "foo".

And, yes, the purpose of using always in this way is both to prevent the programmer, via a compiler error, from overwriting a value they shouldn’t overwrite, and also to have a single place to change the constant values littered throughout your code. The same reasons that programmers use constants in other languages.

Except…that’s not how always always works. If a value is owned by another thing (I think?) it doesn’t act the same way. I found out about this difference after I was experimenting with degrees of certainty and I filed this bug: Log in with Atlassian account.

always refers to what the value is across owners, not across time. This code will not throw a compiler error and will happily change the always value:

The lab is a room.

The lab has a number called the bar. The bar of the lab is always 42.

Fooing is an action applying to nothing.
Understand "foo" as fooing.
Instead of fooing:
	showme the bar of the lab;
	now the bar of the lab is 1;
	showme the bar of the lab.

Test me with "foo".

So, I guess you could say that my degree of certainty about degrees of certainty is somewhat uncertain. But, hopefully, my mistakes will help others and I did find two ways (stated above) in which degrees of certainty can be very useful.

3 Likes

You have diagnosed this correctly. “Always” for properties has a different meaning than “always” for global variables. (See chapter 4.3 vs chapter 4.13.)

This is confusing, but I can’t remember anybody noticing it before, so maybe it’s not that confusing? Worth a note in 4.3, although I guess changing properties isn’t introduced until chapter 8 so it might be confusing either way.

My recollection is that the idea of global constants wasn’t invented until well after the properties meaning of “always” was settled. We failed to go back and think about the implications for the earlier concept.

3 Likes

Also it’s worth saying that the property usage (4.3) is meant for kinds, not individual objects. It says that you can’t define an instance of that kind with a different starting value.

So writing “The bar of the lab is always 42,” where the lab is an individual object (room), has no effect at all.

3 Likes

Yeah, it all comes down to combining a number and a unit into a single type. I think I am hyper-aware of this distinction because of my background in healthcare software development, where we do unit conversion acrobatics all of the time. It is a very common data type in that context.

But have I ever come up with a scenario where I would use this in IF? Not yet. I also haven’t ever used the Metric Units or Approximate Metric Units extensions. Would be a great piece of functionality for educating kids on unit conversion though, which is, by far, the most useful skill I picked up in my high school chemistry class :slight_smile: