Breaking down "definition" into sub-rules/instances

I have some code which is good enough, but it’s a bit bulky. In particular, some of my definitions are unwieldy, and it’s given me problems debugging

to decide what region is mrlp: decide on map region of location of player.

definition: a direction (called dir) is viable:
	if mrlp is intro:
		if player is in intro-room and first-enemy is in intro-room and noun is east, no;
		if player is in second-room and second-enemy is in second-room and noun is east, no;
		if player has the stolen secret plans and noun is west, yes;
	else if mrlp is middlegame:
		if player in complex entrance and security guard is in complex entrance and noun is north, no;
	else if mrlp is endgame:
		if player is in anteroom and chief henchman is in anteroom and noun is inside, no;
	unless the room dir from location of player is nothing, yes;
	decide no;

This is stripped down and bowdlerized. And it’s okay as far as it goes, but I find every time I have a big rule like this, there’s always a bug somewhere in it, and a bit of organization also opens up some test cases I’d otherwise have missed.

So I’m wondering if Inform allows syntax like the below, for a bit more clarity and organization.

a region has a sub-definition called specific-viable

specific-viable of intro is intro-viable rule.

this is the intro-viable rule:
	if player is in intro-room and first-enemy is in intro-room and noun is east, no;
	if player is in second-room and second-enemy is in second-room and noun is east, no;
	if player has the stolen secret plans and noun is west, yes;
	maybe;

definition: a direction (called dir) is viable: [the extra indentation may be annoying, but I need this organized somehow and I can't make a sub-rule]
	(check if specific-viable of mrlp takes care of dir. If so, abide by that)
	unless the room dir from location of player is nothing, yes;
	decide no;

Another way to restate what I’m asking is if there’s “abide by”-style behavior for definitions, where you could have yes, no and maybe.

If not, the extra indentation isn’t much of a problem. But it felt like the sort of thing worth asking about, as any solution might have a really positive knock-on effect down the road.

Thanks!

How about just creating and leveraging a special-purpose rulebook?

Definition: a direction (called dir) is viable:
    follow the direction viability rules for dir;
    if rule succeeded, decide yes;
    otherwise decide no.

Direction viability is a direction based rulebook.

Direction viability for north:
    rule fails.

Last direction viability:
    rule succeeds.

Check going when the noun is not viable: [preamble condition gives precedence over Standard Rules]
    say "That direction is not viable." instead.

yielding (in a test room with no exits):

>N
That direction is not viable.

>E
You can't go that way.
2 Likes

To clarify on this point: A definition in I7 creates an “adjective” in I6, which means a routine taking an object as a parameter whose return value is treated as either true (meaning that the adjective applies) or false (meaning that it doesn’t), so ultimately a yes-or-no answer is required.

The compiler has some expectations about precision of wording in this case, so even though rule succeeds and decide yes seem to have the same meaning on a functional basis, it’s necessary for the definition to do the work of translating the rulebook’s result. If I recall correctly, when a rulebook ends without result neither if rulebook succeeded... nor if rulebook fails... will evaluate true, so it is possible to detect a “maybe” result from the rulebook. However, you will still need to respond to that circumstance with either decide yes or decide no within the definition. (The example treats ending with no result as equivalent to failing.)

I assume the map region of rooms gets changed during play? Scenes seem a somewhat more obvious way to organize these different outcomes based on a global state, but I don’t think I understand all of what you’re up to.

And, not knowing what you’re up to, and you having specified you’ve stripped the example down, none of this may apply, but just a few check going rules seemingly take care of this…


Check going east when the location is the intro-room and the intro-room encloses first-enemy and the mrlp is intro: instead [...]
Check going east when the location is second-room and second-room encloses second-enemy and the mrlp is intro: instead [...]
Check going north when the location is the complex entrance and complex entrance encloses the security guard and the mrlp is middlegame: instead [...]
Check going inside when the location is anteroom and anteroom encloses the chief henchman and the mrlp is endgame: instead [...]

or, for a more generic solution

An opponent is a kind of person.
An opponent has a direction called the blocker.
An opponent has a room called the territory.
first-enemy is an opponent. The blocker of first-enemy is east. The territory of first-enemy is intro-room.
second-enemy is an opponent. The blocker of second-enemy is east. The territory of second-enemy is second-room.
security guard is an opponent. The blocker of security guard is north. The territory of security guard is complex entrance.
chief henchman is an opponent. The blocker of chief henchman is inside. The territory of chief henchman is anteroom.

Check going when the location encloses an opponent (called the guard):
  if the noun is the blocker of the guard and the territory of the guard is the location, instead [...]

That left out the region part, but that’d be easy to add.

In one sense this is the same answer as Otis’: use a rulebook. But it seems to me like the check going rulebook would suffice instead of a new rulebook.

1 Like

Yes…unfortunately I couldn’t throw all the details out there, but the world is a hub. So scenes would be redundant. The thing is, my stuff is generally more “text adventure” than “interactive fiction.”

In this case it’s more about code readability and being able to catch bugs than anything – some of my rules were over a page long, and while that’s okay, I found that breaking the rules into regions helped me isolate cases and test things.

Your “check going when the location encloses an opponent” will be worthwhile in the future, I’m almost certain of it, so that’s good.

Enclosed is some code to show how I divided up the “check smelling” behavior into regions. Given there are 8 regions in the game, the original “check smelling” rule got long & when that happens, it’s 1) a great way for bugs to seep in and 2) initimidating to add special cases/behaviors/replies that might be fun to such a big chunk of code. While you don’t need to smell anything to complete the game, I wanted it in place because it could provide clues and jokes and atmosphere, and I didn’t want it to be more trouble than it was worth.

So breaking up the rules helped, though if there’s an even better way to organize, it’d be wonderful to know!

check smelling code
a region has a rule called smell-rule. smell-rule of oyster is smell-oyster rule. smell-rule of others is smell-others rule. smell-rule of demo dome is smell-demo-dome rule. smell-rule of towers is smell-towers rule.

to decide which region is mrlp: decide on map region of location of player.

check smelling (this is the you can smell some stuff some places rule):
	abide by the smell-rule of mrlp; [other stuff below]

this is the smell-oyster rule:
	if player is in Posh Hops Shop, say "You catch a whiff: [']k, some mokes['] smoke." instead; [oyster]
	if player is in Disease Seaside, say "Say, spare sea spray." instead;
	if player is in Fighter Freight, say "You catch a whiff of Apple Ale. Yes, you remember the red label quite clearly." instead;
	if player is in Sclerous Closures and sardine is in Sclerous Closures, say "Sea rind." instead;
	if player is in Lean Lane, say "Antiseptic spice taint." instead;
	if player is in Rascal Craals, say "This situation only stinks in general, not literally." instead;
	if noun is weaselly walleyes, say "The weaselly walleyes smell of Skum-Musk." instead;
	if player is in Tenfold Teflond Den Loft, say "Old heat. Loathed hot lead." instead;

this is the smell-towers rule:
	if noun is ingrates, say "Nitre gas. Or niter gas. You forget how it's spelled, and trying to remember makes you see red either way." instead;
	if noun is Nerd Aid, say "The Nerd-Aid smells suspiciously like the very red Rind-Ade drink." instead;
	if leak lake is touchable, say "The overwhelming smell of kale is not offensive, but you'd like to get by Leak Lake, all the same." instead;

this is the smell-others rule:
	if noun is slime, say "The slime actually smells...nice, like dishwashing detergent." instead;
	say "Smells, savory, vary so." instead;

this is the smell-demo-dome rule:
	say "Thankfully, nothing unusual like, say, a Med-O-Dome." instead;

With basic actions, then, it’s just a matter of plugging rules into regions, and I have similar rules for listening, sleeping, swearing, checking which object to hint, or checking the “go to [room]” command (e.g. if a specific location is no longer visitable. I have a flag called still-visitable for a room, which does the job most of the time, but often I want to give a specific reject telling the player why they can’t go back, or they can’t jump around just now.)

This worked well until directions were introduced into the mix. Then I had to consider directions and regions in order to break the code down into something more manageable.

This is pretty much what I went with. I included a simple-stupid version of my code below. It wouldn’t make sense in an actual game, but I hope it shows the logic. I tested just going north and south between rm1 and rm2.

r1 is a region. rm1 is a room in r1. r3 is east of rm1. r5 is west of rm1.
r2 is a region. rm2 is a room in r2. rm2 is south of rm1. r4 is east of rm2. r6 is west of rm2.

every turn: say "[list of viable directions].";

to decide which region is mrlp: decide on map region of location of player;

a direction viability rule for a direction (called dir) when mrlp is r1:
	if dir is southwest, the rule succeeds;
	if dir is west, the rule fails;

a direction viability rule for a direction (called dir) when mrlp is r2:
	if dir is northeast, the rule succeeds;
	if dir is east, the rule fails;

Definition: a direction (called dir) is viable:
	follow the direction viability rules for dir;
	if the rule succeeded, yes;
	if the rule failed, no;
	if the room dir of location of player is nothing, no;
	yes;

Direction viability is a direction based rulebook.

As I mentioned above to Zed, the code as-is would work just as well inside one big definition, but this makes it a lot easier for me to add cases without wading through a huge block of code.

And looking at the above code, I see I could write viability rules for each room. That might be more convenient in some cases, though since regions are small enough and few rooms will have exceptions for viability, I’d have some grounding under which to decide, okay, here’s how the rule is used in other rooms in the region.

I could see if the latest if statement I added might somehow clash with the logic in any similar rooms nearby, without being overwhelmed by a page of code.

Unfortunately, relationships among more than 2 things are always tough. Tables might be an approach, i.e., have a Table of Smells with columns for region, room, thing, and text. Then carry out smelling something could iterate through the table to find the appropriate text. I see you have some things that are more complicated, but if the number of exceptions to the pattern is small, they could be handled with their own carry out smelling rule.

1 Like

Yes, definitely, I use tables for other bigger things–for instance, hint [thing].

“A thing has text called hint-text” is possible, but tables have the added benefit of looking like a nice tidy database.

As you noted, though, the table would need separate room and thing columns for smell. So it’s tough to decide what’s best. At some point it makes sense just to crank out if statements and not worry about a too-elegant solution. But it’s good to know what’s there.