Most common 'mistakes' and 'crutches' for those starting out with inform 7?

As a new and learning I7 author I have already felt like I have learned so much in just a short period of time, and more importantly, for the most part I am able to solve an issue without needing to ask for help, although whenever I have asked for help here I have always been greeted with excellent responses.

I’m really interested to hear from anyone, regardless of experience level some of your mistakes or crutches which you were glad to have corrected or found better methods to solve those problems.

As always I look forward to hearing from the community here.

Cheers!

7 Likes

Ah, a reminder to get back to my unfinished gotchas article…

Here’s one biggie.

When you specify an action acts on a thing, you can specify that it acts on a visible thing, a touchable thing, or a carried thing. You may also specify whether requiring light applies to the action.

In between Before rules and Instead rules are, in order, the basic visibility rule, the basic accessibility rule, and the carrying requirements rule. And the most obvious thing in the world would be to assume there’s a one-to-one correspondence here: that the basic visibility rule enforced the visible thing criterion, the basic accessibility rule the touchable thing criterion, and the carrying requirements rule the carried thing criterion. And you’d be two thirds right: it does work that way for touchable things and carried things.

The basic visibility rule enforces the “requiring light” criterion. Nothing enforces the “one visible thing” criterion, because there’s nothing to enforce. Because one visible thing means… any direction, room, region, or thing in the game (including any subkind thereof), anywhere, in or out of play.

You can often get away with it without everything going haywire, though, because it’s the grammar tokens in the Understand lines that determine whether a command will ever invoke that action. And if you’ve said

Fooing is an action applying to one visible thing.
Understand "foo [thing]" as fooing.

you’re actually fine in practice. Because that’ll enforce its use only with in-scope things. And since directions, regions, rooms, aren’t things, you’d have to have written understand lines specifying them as tokens for the action to possibly be invoked. But if you’d said:

Understand "foo [anything]" as fooing.

thinking that meant the same thing as "foo [thing]", then suddenly foo works on any thing in the game with no consideration of scope.

Compounding the ease of error here is that if you say:

Lesnerizing is an action applying to one thing.

it means one touchable thing. It’s natural to think that “visible thing” must be more restrictive than “thing” because you’ve qualified it. But it is (potentially) much more encompassing.

So: never use “visible thing” unless you understand all this and you’re doing it for a very specific reason.

and as a chaser, there’s pointers to useful stuff in the Inform 7 Documentation and Resources thread. (But it’s going to need a thorough rewrite for the new release…)

10 Likes

I think the main one is overuse of Instead rules, and corresponding neglect of Check, Carry Out, etc. rules (a related but more venial sin is cramming everything an action does into a single rule rather than using check, report, before, after, etc. when it’d be helpful to do so).

I think Every Turn rules also get overused, and can cause problems if the author doesn’t take care/know how to make sure they all fire in the proper order.

It’s easy to just define new actions without checking whether there’s an existing action in the standard rules that would be a good fit.

One I still run afoul of is underusing “to say XX” and definition statements to prevent lots of copying and pasting of code (and then introducing bugs when I tweak some but not all instances of the code).

This is less a mistake then an oversight, but many newer authors don’t know about or underappreciate the usefulness of the IDE Index and the RULES and ACTIONS testing commands. I feel like getting comfortable with them helps me fix like 95% of my bugs.

Hopefully it’s clear that I’m primarily talking about stuff I struggled with (or still struggle with)! But I don’t think these are too atypical.

12 Likes

I would say ignoring the Index is a common mistake. It is extremely informative. Even though it’s not necessarily the best thing to be looking at on day one, the sooner you start to explore it, the better. If nothing else, it’s an extremely handy map to the functionality that’s provided by the Standard Rules.

General attention to the way that rulebooks are processed is also important, especially in the context of the action processing rules that are basically the main loop of a running game. Knowing which rulebooks are there by default, their order of processing, what their default outcome is, and how your rules will affect the resolution of the action is very important to deciding which rules to write and why. (This is related to DeusIrae’s point about not overusing Instead rules.)

Learning to closely read the Problem messages produced by the compiler is another habit worth developing early. They can seem cryptic at first but almost always pinpoint the problem. If terms don’t make sense they can be searched for in the documentation.

Learning to appreciate and use the organizational headings (Volume, Book, Part, Chapter, Section) is probably worthwhile, too.

7 Likes

The main mistakes/crutches I have lodged in my code–I am starting out–have all been mentioned.

I spent maybe two months working on my first two rooms–to be fair, there were verbs and tables, too–and a big chunk of that was reworking all of the “instead” and “before” stuff into the “check, carry out, report” idiom. It was worthwhile, of course. The code was much easier to read and troubleshoot, and this approach allowed me to better leverage values and kinds.

Speaking of reading: organizing the code has been mentioned, and that has helped a lot, too.

Really, though, and I’m not trying to butter anyone up, the biggest mistake I’ve made is relying too heavily on the handbook and documentation without searching here. It’s no fault of those sources, which are both fantastic, but all manuals make assumptions about the reader’s intent. Since forumgoers express intent in their posts, it’s much easier to tell if this or that tactic is right for your game.

Also: “undescribed” seems like a good idea but it likely isn’t.

5 Likes

Personally, not learning how rules and rulebooks work early on was one of the biggest mistakes I made with this language, and I think it happened because I originally learnt from the manual alone. Writing With Inform waits for 18 whole chapters to talk about the actual mechanics of rules and rulebooks. In the first paragraph of Chapter 19, it explains that rules and rulebooks make up much of the high-level machinery of Inform (true) and in the same sentence declares that most of the time we don’t really need to know how this machinery operates (baffling and somewhat condescending)! Sure, not everyone’s going to be creating super complicated rulebooks for every aspect of their game, which is sensible. Sometimes a new adjective is all you need. But rules and rulebooks are the whole idea behind action processing, they give activities a purpose, they make source easier to manage and easier to read, they’re a principal of the language. I know enough about them now to know I still don’t use them to their full potential!
I admit that some of them can be confusing, that there’s a lot to remember about them, and that any discussion of them is likely to end up being quite dense. But I still believe that an early introduction to them would provide a more stable foundation on which someone could build a better understanding of everything they can accomplish with them.

DeusIrae mentioned the overuse of instead rules (an excellent point), which is something many have had experience with. I’m no exception. Instead rules are so easy to overuse because the naturalness of their phrasing somewhat obscures what they actually do - the role they play in action processing. I really only stopped overuseing them when I understood that role through the framework of rules. The chapter on rules honestly blew my mind the first time I read it.

So… In less pretentious language, rules: Learn to love 'em. :slight_smile:

6 Likes

Oops, I should have looked before posting, @otistdog and @kamineko basically covered the importance of this understanding without needing an essay to explain it. :stuck_out_tongue:

5 Likes

Before defining a new action, the easiest way to check is to play in the IDE and turn on ACTIONS (by typing “ACTIONS”) and then try the verb you want and any potential synonyms for it to see if the parser already has an interpretation you can use or need to untangle from understand rules first.

The common one is KNOCK ON DOOR. “Knock” is pre-defined as a synonym for ATTACK. If you try knocking on a door, you’ll get the standard refusal for violence (unless you’ve already re-defined ATTACK…but) With Actions on, it will say “Attacking the door - failed.” so you know what action it’s interpreting and can just use that action:

Instead of attacking the door:
    say "You knock politely. Nobody's home."

This has the added advantage of catching ATTACK DOOR, HIT DOOR, KILL DOOR, etc.

RULES also is handy, especially if you name your rules:

Instead of attacking the door (this is the polite visiting rule):
    say "You knock politely. Nobody's home."

That way when RULES is on and you get a despairingly huge list of rules, it will say “The polite visiting rule applies” instead of “The Instead of attacking rule applies” which is much easier to figure out as many rules you wouldn’t think of often are checked and will or will not apply to any action.

7 Likes

The biggest mistakes I made were with punctuation.

If you have an if-statement right after a sentence, it looks bad. For instance:

You see Bob here.[if Bob is angry] He looks angry.[end if]

will end up printing the two sentences on different lines.

So the best thing to to do is:

You see Bob here[if Bob is angry]. He looks angry[end if].

This will print everything on one line.

This comes up way more often than you’d think, and is non-intuitive.

11 Likes

I still struggle mightily with this. Punctuation and brackets make weird line breaks, and even though I mostly grasp the rules, I have trouble applying them while writing, which means I have to comb back through to find every spacing problem caused by the weirdness. I really, really wish I had gotten this down early on.

4 Likes

My reading of the “don’t need to understand how this machinery works” line is just that one doesn’t have to sweat internalizing the details of 19.16. The Laws for Sorting Rulebooks, that in practice knowing “more specific rules go before more general rules” is all you need.

This continues to be a challenge of mine. I still default to thinking in other programming paradigms than rule-based. Even moreso, I’m sure I still haven’t grasped how much potential assertions with every has. ( WI 4.16 Assemblies and body parts, WI 4.17 Names made in assemblies )

3 Likes

Sometimes when the line break things vex me, I just give up and fall back on:

To say ?: say unicode 63.
To say !: say unicode 33.
To say dot: say unicode 46.

Then one can say any of "[?]", "[!]", or "[dot]", and get “?”, “!”, “.” without any line break implications.

11 Likes

Hey, that’s really cool!

3 Likes

Agreed, that section is the dense bit I was talking about, a little unclearly. I’m not overly bothered by the phrasing, the chapter as a whole is fine. I mean that I struggled with a lot of concepts before I read that chapter, and I didn’t read it for some time because I hadn’t yet learned to skip around a bit. I think people who program in several languages will know the feeling of simultaneously recognizing and being unable to imagine the scope of the things possible with a language we’re comfortable in. I didn’t really get that feeling about Inform until I started to grasp rules, and even now I don’t use them where I definitely could. Rule-based programming is cool, but it was unintuitive to me until it was explicitly described.

I know I haven’t lol

3 Likes

Some great contributions here. I always find it really helpful to see where others have stumbled and try and learn from that.

My biggest challenge at the moment in creating my combat system is dealing with many variables all having to harmoniously coexist without breaking something.

My standard action variables are approaching a point where soon it will have a dozen if statements, a dozen let statements, numerous now statements etc. So far I have successfully been able to make this work but as I try to add more adventurous features it is definitely proving to be quite challenging.

I am also tryjng to write it as if it could eventually be adapted to an extension, not because I think it would make a good extension for other people but moreso that most of the code is written in a way where it doesn’t rely on rules that are specific and instead very general and broad. While at first this was a challenge to get my head around, it has made adding new weapons, armour, items and so on a breeze. Simply specify the kind and add the stats and it just works.

I am yet to delve deeply into tables, lists and list creation and truth states.

Numbers and variables are in themselves a bit of a challenge and when hundreds of lines of code are almost all describing an action and variables, it can be quite tricky to keep everything from breaking.

I’m very excited to eventually have my ‘personal extension’ if you will, complete so I can begin applying it to either a rogue like format or (ideally) a crafted narrative which uses the extension in an engaging way.

Every night I have been setting a goal for a feature or two to implement and almost always I get them done, so while progress at times feels a bit slow, it’s progress nonetheless.

1 Like

It took me some time to grok that you can write rules (mostly) wherever in the source text you want and I7 sorts them out at compile-time, with the only caveat that more-specific rules take precedence over less-specific rules, and you can specify vaguely where in the rule stack they go with “first” and “last”.

I think it’s last-in, first out, so if you write in this order:

First check jumping: say "Foo!" instead.
First check jumping: say "Bar!" instead.

…if I remember correctly, the compiler reads the “Foo” rule which is written to the top of the check jumping rulebook, then the “Bar” rule is similarly written to the top before that. The Bar rule would run first and fail the rulebook, so you’d only get the Bar rule to fire.

3 Likes

After I thought I had a decent knowledge of I7, my biggest comeuppance was some basics about texts.

In 6G60, “text” and “indexed text” were separate types one had to convert between. Texts were strings that included adaptive text; indexed texts were plain text strings. In 6L02, they become one thing from a language user point of view while still having separate representations under the hood. In 6M62 and v10, one can still say “indexed text” but it means the same thing as “text”.

Texts containing adaptive texts are Schroedinger’s Boxes: their values are undefined until you open the box and the wave function collapses into some particular string. And that happens every time you open the box. So if you have:

pack-animal is a text that varies.
pack-animal is "[one of]mule[or]llama[cycling]".

when play begins:
  say "'[pack-animal]' is [number of characters in pack-animal] characters long.";

you get "mule" is 5 characters long.

To pin it down, you need the substituted form of <text>. The docs also refer to this as “expanding”, though that’s not the name of the phrase. I’ll use that term in the following.

If you try to compare two texts-with-adaptive-text with “is”, the result is almost always false even if their expanded forms are the same. If you have if t is empty when t is a text-with-adaptive-text, the result is always false, even when the expanded text is empty. Comparing two plain texts with “is” works. Comparing a text-with-adaptive-text to a plain text works (the text-with-adaptive-text gets expanded). Thus the easiest way to test whether a text-with-adaptive-text is empty is if t is "".

So: don’t compare strings variables with “is”. If one side or the other is a text literal (“literal” is programmer-ese for not a variable, but some particular explicit value in your code, a quoted string in this case), e.g., if t is "cat" you’re okay. Otherwise, stick to if t exactly matches the text w.

To tell whether something is a plain text or a text-with-adaptive-text, you can use the substituted or unsubstituted adjectives.

To say phrases can have any code, including code that has nothing to do with output. So there’s a sometimes clever trick where you sneak side-effects into saying things. But keep in mind that these side effects occur every time the text is expanded, not just when it’s output. So if you have:

g is initially 0.

To say gg: increment g; say g; say line break;

when play begins:
say gg;
if "[gg]" is "1", say "gg = 1.";
let q be the substituted form of "[gg]";
say gg;

the output is:

1
4
5 Likes

The rather arcane reason for this (as I suspect you know) is that a ‘text with adaptive text’ is conceptually a function returning a text and Inform considers that two different functions are not the same thing, the results returned by said functions being irrelevant to this non-equivalence.

As a simple conceptual example, 2 squared is not the same thing as 2+2, even though both return 4,or x multiplied by x is not the same as x squared, even though both return x squared.

In Inform this idea is taken one step further, in that Function A is never considered the same thing as Function B, even if both Function A and Function B contain identical code to return 2 squared. This is unsurprising, as it would be difficult for Inform to discern equivalent functionality in function code, and it doesn’t try to.

4 Likes

Similarly, following on from the concept of ‘text with substitution’ as ‘function returning text’, ‘if some-text-with-substitution is empty …’ is asking whether the given function is non-existent, hence it is always false.

3 Likes

There’s an additional ‘gotcha’ here in that texts can be expanded unexpectedly by the internal workings of Inform so that Inform can examine the current result of that expansion for its own purposes. This will unexpectedly run any ‘non-outputting code’ included in your ‘To say …’ phrase. You can trap this unwanted behaviour in your ‘To say …’ code by presaging ‘non-outputting code’ by the almost-undocumented conditional ‘unless expanding text for comparison purposes …’ See here and here.

4 Likes