Beginners question: Local vs Global

Hi everybody,

I’m new to I7, and I’m familiar with programming (scripts mainly, but I know what OOPL is). It took me a while to get a feeling for I7’s syntax (this tutorial helped a lot, and I7 for programmers).

Today I experimented a little and created just two rooms, but instead of using the normal navigation, the reader is supposed to push a button to be teleported to another room (“push red button”). That all works fine, especially that there is no teleportation if something else is pushed.

Anyway, I just realized that my introduced check for the “push” action in room 1 also works in room 2. Maybe that’s a good thing? So I assume checks are global in I7? I found that values are local to rules, but didn’t find much more about local vs global in I7 - so everything is more or less global?

Sorry, if these are dumb questions - my first impression was that I’d have to create “a world” room by room, but if there are things that apply globally - how would I organise such global things best so that I can find them again later?

Thanks!

Here are the essential lines of my I7 script:

A teleport button is a kind of thing. A teleport button can be working or broken. A teleport button is usually working.

A red button is a teleport button in the studio. 

Check pushing:
	if the noun is not a teleport button:
		say "nothing happens..."; 
		stop the action;
	if the noun is a working teleport button:
		say "After a flash of light, you find yourself in...";
	if the noun is a broken teleport button:
		say "Some crackled noises tell you that the button isn't working properly.";
		stop the action.

Carry out pushing:
	if the noun is a red button:
		now the actor is in the Alien's Chamber;
		stop the action;
	if the noun is a blue button:
		now the actor is in the studio;
		stop the action.
2 Likes

Welcome!
You can confine a check to a specific thing:

Check pushing the red button:
	now the actor is in the Alien's Chamber;
	stop the action

or make it global as you have done.

My programming for my first game is such an organizational disaster that it’s breathtaking, but every time I tried to reorganize it, I made it worse. I’m trying now to organize code that is global into a [rules] area at the beginning, and I’m hoping that will serve me better. But if anyone has ideas on good organization strategies, I’d love to hear them as well.

5 Likes

Thanks @AmandaB - yes, how to organize the code in I7 was part of my question, thank you for answering that honestly :blush:.

I think I saw in some other tutorials the usage of sections and parts, but it is still a mystery to me where to put what and if there are any consequences with regards to visibility of ‘concepts’ (rules, checks, objects).

1 Like

There isn’t anything close to consensus on how to organize your code beyond “some way that’s understandable to you”. Some past threads are I7 Code Organization and Efficiency, Splitting the story into multiple files . How to Structure I7 Code is one answer.

When you just say “check pushing” or “carry out pushing”, those rules will be triggered any time the player tries to push anything, anywhere, any time. There’s a subtle default restriction lurking there: they apply to the player only. If you have NPCs taking actions, those rules wouldn’t automatically apply. You would have to write “check an actor pushing”, “carry out an actor pushing”, etc.

There’s a default “Nothing obvious happens” for pushing things; if you’re happy with that instead of “nothing happens…” your code could be:

A teleport button is a kind of thing.
A teleport button can be working or broken.
A teleport button is usually working.
A teleport button is usually fixed in place.

The Studio is a room.
The red button is a teleport button in the Studio.
The purple button is a broken teleport button in the Studio.
The chartreuse button is a thing in the Studio.

The Alien's Chamber is a room.
The blue button is a teleport button in the Alien's Chamber.

Check pushing a broken teleport button:
  instead say "Some crackled noises tell you that the button isn't working properly.";

Carry out pushing a working teleport button:
  if the noun is the red button, move the player to the Alien's Chamber, without printing a room description;
  if the noun is the blue button, move the player to the Studio, without printing a room description;

Report pushing a working teleport button:
  say "After a flash of light, you find yourself in...";
  try looking;
  stop the action;

There are a zillion ways to do things, and some of the choices above are arbitrary. Instead of looking at red vs. blue button, it could have been if the location is the Studio, move the player to the Alien's Chamber. Others have specific reasons.

The intent of Check rules is to verify that an action can happen, so I don’t want to do anything with them but output an appropriate message and stop the action if some condition doesn’t apply. I would avoid having the successful “After a flash of light, you find yourself in…” message there. After all, it hasn’t happened yet, and it’s not impossible you could write some Check Pushing rule in the future that forbade it.

A general principle is that Carry Out rules produce output when the whole point of the command is output, e.g., looking, examining, inventory, and that output should be avoided in Carry Out rules otherwise. Here I was tempted to ignore that principle and put the “After a flash of light” message here… but I also knew I needed to do something to suppress the default “Nothing obvious happens” message, and an After or Report rule was the most obvious thing. And since I was going to have one anyway, it had might as well take care of the output. So in the Carry Out rule, I went to the bother of suppressing the room description that would normally happen when moving the player.

I could have used an After rule instead of a Report rule, and I wouldn’t have needed “stop the action”. But the intent of Report rules is that they’re where the output happens, so I followed that.

In general, I’d suggest putting thought into whether there are easy ways to make your rule preambles more restrictive. A really easy class of error is allowing nonsensical things to happen because your preambles are too broad – for instance, there’s a hidden key that’ll be revealed when the player searches the flower pot, so you start with the key out of play and write: Carry out searching the flower pot: move the key to the flower pot. And when you try it, it works and you move on. But if the player drops the key in the attic and searches the flower pot again… pouf, teleporting key. Carry out searching the flower pot when the key is off-stage would be better (unless you know the key might be destroyed in play and you were accomplishing that by making it off-stage again…)

Since you were also asking generally about locality vs. globality…

Actions, Activities, and Rulebooks can have variables that are local to that action, activity, or rulebook (see Writing in Inform 12.10, 18.6, 19.10). This is as close to namespaces as I7 gets.

There are six constructs allowing code blocks: Rules, To-decide phrases, To-say phrases, Definitions, and things beginning At or When. Variables created with “let” inside of one of those are local to that code block. (And it’s to that whole block even if the “let” is inside a repeat or if.) Variables defined outside of those things are all global (other than action, activity, or rulebook variables.)

In general things are global… so think ahead when naming things that they’re distinctive enough to not be likely to create collisions later.

7 Likes

Sections (and the other Headings) can help you make the code more understandable to yourself and help you jump to the right place, but they don’t have teeth in terms of isolating parts of your program from each other. They’re significant in regard to extensions because they represent the level of granularity at which you can replace extensions’ code. Otherwise, they’re decorative.

Rules are global. To have local effects, you narrow down their preambles. Or selectively nullify them. (Instead of “stop the action” above I could have said The report pushing rule does nothing when pushing a working teleport button outside of any rule.)

Regions and Scenes facilitate localizing effects. (The documentation promotes thinking about Scenes as being about time-delimited things, but they’re a general tool that lets you say “when these conditions are met, the game is in State X.” “When we’re in state X and these other conditions are met, then we’re no longer in State X.” And then you alter whatever rules appropriately when State X applies.)

3 Likes

Thank you @Zed - I‘ll need some time to digest all that information, and will definitively check the link to the other thread about organizing your I7 code.

My feeling is that I7 is a combination of OO principles (some I know, but I don‘t have any practical experience with yet), and a rule-set (something I don‘t have any experience with yet). Maybe it‘s not needed to see what‘s going on in I7 behind the scenes, but as you said, there are lots of options how to do things, and often when I went through examples, I asked myself why it‘s done that way, and not another - or rather: What‘s the advantage in doing it that way? Thank you for providing some alternatives for my code, and elaborating on the „why“ you‘d do it that way.

There’s a default “Nothing obvious happens” for pushing things;

Interesting. That still came up for my code, and I assumed it was coming from the default objects or actions that are already part of I7. I assume that I‘d have to overwrite the reporting of my pushing action if I wanted to keep my code?

2 Likes

Yeah, that’s often not clear with programming languages in general, and I7 is distinct enough that instincts from other languages aren’t straightforwardly portable.

A thing I think the docs could emphasize more: there’s no substitute for just plain reading the Standard Rules if you’re to understand I7’s default behaviors. And especially no substitute for consulting it when you’re writing rules to customize the behavior of existing commands. Here’s the Standard Rules on pushing.

Any given Instead or After rule defaults to stopping the action unless you explicitly include “continue the action”, but the other action rulebooks (like Report) are the reverse: they keep going through all the matching rules unless one of them explicitly stops the action. Sometimes it’s a feature for multiple Report rules to be triggered, but otherwise you have to think about suppressing all but one.

3 Likes

I get the impression that you’re really supposed to use the Index for this, but the the docs could emphasize that more if that is the case.

2 Likes

Actually, I’m sure you’re right. I have mostly just read the Standard Rules.

1 Like

Your HTMLization of the Standard Rules is invaluable.

1 Like

Looking at code I wrote 8 years ago, I see some stuff that’s pretty tangled. I can see what I did and why I did it, but with what I know now, it does seem impractical. But it got the job done. I’ve refactored some of it, and it’s helped find minor bugs.

I think Zed did a really good job of covering how there’s more than one way to do things, and my feeling is you don’t need to worry too much about The Best.

I hope describing my outlines is helpful and doesn’t hijack the thread. You all may find something different from them. I think over the years I’ve gotten better at 1. identifying code that could/should be tightened up or 2. writing code that works, then seeing how to modify it later so it runs better. But this starting blueprint is effective for me.

Lumping like code together definitely helps.

volume initial definitions [story name, header, release number, etc.]

volume definitions

definition: a direction (called d) is viable:
    if the room d of location of player is nothing, no;
    yes;

volume when play begins

[when play begins rules here]

volume hinting [or other big important tricky verbs]

volume when play ends

volume special properties

book rooms [for example]

a room has text called cant-go-text.

check going nowhere (this is the custom nowhere text rule):
    unless cant-go-text of location of player is empty, say "[cant-go-text of location of player]" instead;
    say "You can't go [noun], but you can go [list of viable directions]." instead;

volume the map

book room 1

chapter room 1 item 1

chapter room 1 item 2

volume standard verb tweaks [this includes definition resets such as understand "eat" as something new]

volume custom verbs

volume for beta testers [make sure to not-for-release this for release]

One thing that really helped me organize stuff was breaking my story.ni into .i7x header files I could include in the opening volume. Some weren’t very big at all, but they were just one less place I had to hunt for tricky code.

Possible uses are for variable definitions e.g. “definition:” or “to decide whether:”, but I find even putting the test commands off into a test file reduces the clutter. (Come to think of it, that’ be a good place to put beta-tester stuff.) The downside is, it’s harder to access from the Inform IDE.

I use a lot of tables in I7, too, for better or (likely) worse, and having a side file full of tables helps me. This includes stuff like random text that can’t quite go into [one of] because a file would get too long.

If you want to untangle your source code, source control helps a lot. You can try stuff and undo it without fear.

2 Likes

It’s not my thread, but I found this very interesting, as it’s really different from what’s intuitive to me. Since what’s intuitive to me isn’t serving me well, I’m looking at yours carefully and seeing how grouping things differently might serve me better.

2 Likes

I’ve done my best, but I realize a lot of organization rules are subjective and don’t work for some people.

Over the years I think I’ve picked up a lot of stuff I just stumbled on. Sometimes you can just look at source code and say “Wow! You can just do/organize things that way?” So yeah, borrow and reject stuff as need be.

As for stuff I cribbed from? The two big examples I’d give are Aaron Reed’s Sand-Dancer, which shows how code evolves in the big picture, and Ben Collins-Sussman and Jack Welch’s Rover’s Day Out–both authors are good about writing good code and sharing it, and I’ve learned a lot just looking at it.

But then again pretty much any game that makes you say “Neat, how’d they do that” and provides source is worthwhile. I learned a lot of Inform 6 from David Welbourn’s 69105 Keys, for instance.

3 Likes

Whoops, I just saw that there’s a significant exception to this. As mentioned at the bottom of Old timey “Use with” Inform7 command syntax, when the compiler needs to disambiguate names within the source, it’ll give priority to candidates from the same section (and then chapter, part, book volume), though Zarf also notes why you might want to avoid relying on this behavior.

2 Likes

@Zed : I tested this, and I still think you’re right. :upside_down_face:

But first things first. @justaguy was referring to “chapters” and “sections”. I saw “parts” in a tutorial, but others spoke of “volumes”, again others of “books”, so what’s the hierarchy of those bits?

I simply entered all these terms in the source and checked the contents tab:
parts-sections-volumnes

As you can see, sections and chapters are on the same level, so they must be synonyms. Same for books, parts, and volumes.

OK, now let’s try to compile the following:

"local-and-global" by Jens Haley

Part 1 - The beginning

Furniture is a kind of thing. Furniture can be big or small. Furniture is usually big.

Chapter 1 - First Chapter

R1 is a room.

A bed is furniture in R1. 

Chapter 2 - Second Chapter

R2 is a room.

A bed is furniture in R2.

It does not compile, because object bed cannot be in two rooms at the same time. Apparently it does not help that the bed object is defined in two different chapters.

Let’s be more specific by adding camp and divan:

"local-and-global" by Jens Haley

Part 1 - The beginning

Furniture is a kind of thing. Furniture can be big or small. Furniture is usually big.

Chapter 1 - First Chapter

R1 is a room.

A camp bed is furniture in R1. 

Chapter 2 - Second Chapter

R2 is a room.

A divan bed is furniture in R2.

This can be compiled, but why? Because the objects are in separate chapters, or because they are named differently? I remember that I7 is quite powerful when it comes to parsing, so spaces are allowed when introducing names. I don’t have to write divan_bed and camp_bed to distinguish the two.

To verify my theory, let’s do a third test: Everything stays as it is, but I’ll remove parts and chapters again:

"local-and-global" by Jens Haley

Furniture is a kind of thing. Furniture can be big or small. Furniture is usually big.

R1 is a room.

A camp bed is furniture in R1. 

R2 is a room.

A divan bed is furniture in R2.

This still compiles, so parts and chapters are irrelevant with regards to unique names. I7 regards both beds as unique, because they have different names: One is divan bed, one is camp bed.

q.e.d. :grin:

I weirdly anticipated your question! Today’s inform7tips tweet:

A mnemonic to remember the order of Inform 7’s headings (Volume-Book-Part-Chapter-Section): “Very Bad People Choose Sin.”

If you have:

Lab is a room.

Furniture is a kind of thing. Furniture can be big or small.

Chapter 1

The camp bed is furniture in the Lab.

Section 1

The divan bed is furniture in the Lab.

The bed is big.

when play begins:
  repeat with item running through furniture begin;
    say "[if item is big]big[else]small[end if] [item].";
end repeat;

then the camp bed is small and the divan bed is big. If you remove the chapter and section headers, then the camp bed is big and the divan bed is small.

1 Like

Just for reference, cf. Writing with Inform 2.5. Headings and 2.6. Why using headings is a good idea.

ok, now I get @justaguy 's point.

Still, bed isn’t really local to Chapter 2, it’s simply an unknown identifier. I7 then simply tries to circumvent an error situation by checking the same chapter for a more proper definition.

I agree that it’s better to use he full name to avoid such tricky situations.

Thanks for the links, Jens.
Generally, when I’m programming, I have a pretty good idea of that the language’s syntax is. I’ve never acquired a good idea of Inform 7’s syntax. Things either compile* or I have no idea why.
*Actually, that’s not true: sometimes they compile but do something unexpected.

1 Like

As a non-coder, one of the stumbling blocks I had learning Inform 7 was thinking I had to be meticulous about writing stuff in a correct order, when I7 actually sorts that all out at compile time and organizes rules into rulebooks to be used when necessary. There are a couple of minor things where the source text has to define something before referring to it, but trusting the compiler to arrange things was a bit of a leap at first.

Headings may seem like a chore, but it really helps when error message can refer to them to direct to the error and you can use the drop list to filter you code to a specific section. I wrote one whole game when the “jump arrows” by error messages would not work before an update, so headings and organization were critical in that case.

3 Likes