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

This let’s play has really saved have my life with my current game. I was getting frustrated because I have a very complex combat system involving persuasion, minions and recording (inspired by the persuasion rules sections), and there is a big system of checks before someone can fire a gun (is it loaded? are they carrying it? is a battle going on? will they have to get out of cover first?)

I wanted to do a ‘before’ rule for shooting at a specific special healer enemy, but it bypassed all the check rules. But then I remembered! I can just type:

‘abide by the check shooting rulebook’.

And it works perfect! I don’t think I would have ever known about that if I hadn’t read through the whole thing.

9 Likes

After going through this manual I designed a lot of stuff in my game around it.

But I discovered something weird…

it seems like hyperbolic cosine and hyperbolic sine are backwards, so ‘hyperbolic sine of the number understood’ actually gives ‘hyperbolic cosine of the number understood’. The easiest way to see this is to plug in some number and its negative and see how the symmetries are messed up (should be sinh(-x)=-sinh(x) and cosh(x)=cosh(-x)". tanh is messed up too.

But the inverse hyperbolic functions aren’t switched. So inverse hyperbolic sine gives the right answer. Unfortunately, this means that sinh and arcsinh, as programmed, aren’t inverses of each other.

I discovered this because I made an in-game calculator that has buttons for all of these. The puzzle involves arctanh x, so it never ran into the bug, but a tester tried the other buttons and the answers just didn’t add up right. Pretty weird! This will almost certainly never be relevant again, but it’s pretty interesting.

6 Likes

I regret to be the boring guy, but have you reported this bug? This page links to the current bug report place at Jira.

Re: the hyperbolics, my dad (retired maths teacher) and his staff called the hyperbolic functions the drunk functions. Cosh. Shine. Thangent. As in, they sound like sterotypical drunk slurring. This was in the 70s-90s. I tried to google the combination up (hyperbolic functions, drunk/drunken) and caught at least one mention of this on the first page of search results in each case, but only one.

-Wade

4 Likes

This is the code for the sinh function in Inform, wherein someone with a better mathematical background than me can probably find the error.

[ REAL_NUMBER_TY_Sinh in tmp out;
    @exp in tmp;
    @fsub M_0 in in;
    @exp in out;
    @fadd tmp out out;
    @fmul out M_HALF out;
    return out;
];

Or in other words:

tmp = e^in
out = e^-in
return (tmp+out)/2

This looks right to me, which means the error is probably in the implementation rather than the planning?

3 Likes

I think which one adds e^x and e^-x and which one subtracts them is backwards. This fixes it (just swapping them). Fixes tanh, too, since it’s defined in terms of them.

Include (-
[ REAL_NUMBER_TY_Cosh in tmp out;
    @exp in tmp;
    @fsub M_0 in in;
    @exp in out;
    @fadd tmp out out;
    @fmul out M_HALF out;
    return out;
];
-) replacing "REAL_NUMBER_TY_Cosh".

Include (-
[ REAL_NUMBER_TY_Sinh in tmp out;
        @exp in tmp;
        @fsub M_0 in in;
        @exp in out;
        @fsub tmp out out;
        @fmul out M_HALF out;
        return out;
];
-) replacing "REAL_NUMBER_TY_Sinh".

Issue I7-2416.

4 Likes

Sinh has a minus, while Cosh has a plus in real life. It’s because sin(x) is (e^ix-e^-ix)/2i and cos(x) is equal to (e^ix+e^-ix)/2 according to the Euler identity, and the hyperbolic versions just drop the i.

I could submit the bug but it feels a bit weird even having the functions in inform at all. If no one else does after a while I will though.

5 Likes

Ahh! That’s the sort of thing I hoped someone would notice if I posted the code.

As for why they’re included in Inform at all, I imagine it’s just on the off-chance that someone makes a puzzle requiring calculating arctanh. :stuck_out_tongue:

4 Likes

I submitted it; I edited my post after the fact…

Graham replied:

If only the author of Inform had had some basic training in mathematics, all this could have been avoided…

9 Likes

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