Conglomerating similar very long functions from similar games: code review-ish question

I’m looking to hunt down line breaks in Low-Key Learny Jokey Journey, and it occurs to me that it happens in the other similar games I have e.g. Very Vile Fairy File. There’s one big core function, called the verb-checker rule, which is present in all 5 games and slightly different across them.

For instance, one file has this:

this is the verb-checker rule:
(40 lines of code)
(40 lines of code)

Another has this:

this is the verb-checker rule:
(40 lines of code)
				if check-rule entry is vc-sad-sunk rule or check-rule entry is vc-bad-bunk rule:
					say "Whenever you're ready, just type [b][the player's command in upper case][r] to move on.";
					now think-cue entry is true;
					the rule succeeds;
(40 lines of code)

The gist is, they are long, 80-line functions that I don’t see any way to break up, and there are 2 or 3 game-specific if-statements I don’t believe I can remove.

So my question is: what is the best way to work around this? I want to fix the line break bugs once and only once, if possible. My current solution is to have, in most games,

to decide whether a rule (called r) is dont-score-yet: no;

Then for one game,

to decide whether a rule (called r) is dont-score-yet:
    if r is vc-sad-sunk rule or r is vc-bad-bunk rule:
        yes;
    no;

This isn’t super-elegant, but it works, and it’s easy to maintain, and it will get me to my goal of having only one 80-line function instead of 5 separate ones to change…

Is there a better way to do things, or have I stumbled about as best I could?

Thanks!

1 Like

It’s hard to say without knowing what’s in those 80 lines of code, but if it’s a bunch of if-statements, this seems like a good use case for a rulebook. Make each if-statement its own rule, and you can slot in an extra one for a specific game when you need it.

4 Likes

Hmm, yeah, it’s tough to explain, because I don’t want to foist a pile of code on people to sort through, but I do want to give the relevant details. I think your mention of rulebooks helps. I’ve used rules a lot in that big rule, but I wanted to write this out, not for a full code review but to say “Ok, next person, here might be how to do it.”

The big one is probably a line in the middle of the rule for one game (but no other) that says

				increment now-score of best-room entry;

Now we could just create a dummy now-score variable for all rooms in the global file I have, so the compiler doesn’t complain with the “increment” line. But I have a feeling there may be other wrinkles where this doesn’t quite work, and it doesn’t help me understand what’s going on.)

A basic proof-of-concept testing on sandbox projects seems to indicate the below works.

First, I put the following line in the global file all projects can access:

the subscore rules are a room based rulebook.

Then replace the first “increment” line with

				abide by the subscore rules for best-room entry;

Then leave the subscore rules blank except for the one game, which would have:

an subscore rule for a room (called myrm) (this is the track scores by room rule): increment now-score of myrm;

I guess a mental block I had was that rulebooks all had to have a working rule in them, but I saw pretty quickly that they don’t.

These games are all open-source, right? If you post a link to the appropriate file on Github we can see what the actual code looks like and provide better help.

Thanks, yes, they are open source – but I’m very worried there’s a lot of code to parse through.

So it only feels fair for me to do all the dirty work I can before you all see the relevant bits, so you can focus on the relevant/meaningful differences.

And also, your advice was a BIG help for a couple bottlenecks, so it’s quite possible there will be nothing to share. Realizing rulebooks could be empty helped push a few things ahead.

Plus I also know we’re all working for free here, so I don’t want to turn this into a “FIX MY MESSY CODE PLEASE” sort of thing" … and I created the chunks of similar code on my own initiative without planning. So if I can’t figure everything out for myself, I want to make it as likely as possible someone can walk in and say “Oh, you can just change THAT.”

In any case, if I am able to figure things out myself, I’d like to provide at least github commits to show what I did, so if anyone wants to do the same sort of thing in the future, they can. It feels nontrivial, and I’m probably not the first or last person to have to do this.

TLDR history of how this happened

The TLDR history is that I cut and pasted code from the first game (Very Vile Fairy File) to the 2nd (Quite Queer Night Near,) an EctoComp Petite Mort game, and then I added special cases to #1 and cut stuff from #2 so it would compile. And then the 3rd had some special cases that didn’t fit into #1 at first, so I cut and pasted and added stuff from #1, because I was putting out a post-comp release and didn’t want to have to deal with anything breaking.

And by that time I’d pasted code from #3 to #4 and #5 because each of those had their own wrinkles. I just needed to get stuff to compile before a deadline!

1 Like

You could set a global before following the verb-checker rule that is then checked within the rule to determine whether it should run a given bit.

Looking at the whole source, I was wondering whether the innards of that repeat loop might better be its own rulebook… but it looks like the issue is Inform’s inability to pass around a table row. You could make a kind of object called pseudo-row with properties corresponding to each column, and then create a pseudo-row based rulebook. Loading it up would be 15 tedious lines of code, but then you’d have the flexibility of rulebook preambles to control what runs.

1 Like

Or you could use a bit of I6 to hack around that limitation. I think this might be a case where that’s worth doing.

2 Likes

Is there a way to do it without the ugliness of choosing a row (to satisfy the compiler, which balks at a rule or phrase using the foo entry without a choose row) and then setting the row id?

Table of Spam
code
"Z"
"Y"
"A"

To decide which K is the (name of kind of value K) in question: (- parameter_object -).

when play begins:
  repeat with i running from 1 to the number of filled rows in the table of spam begin;
    follow the rb rules for i;
  end repeat;

to set the row id to (n - a number): (- ct_1 = {n}; -)

rb is a number based rulebook.

rb:
choose row 1 in the table of spam;
set the row id to the number in question;
say code entry;

This works but you’d need those two lines of code at the start of each individual rule.

I was thinking an alternative for “choose row N in T” that uses a global variable (or perhaps a rulebook variable), which is set at the beginning of the rulebook.

I’m still not seeing how one gets around the compiler itself balking at the use of entry without a choose row (unless you’re replacing entry with something else, too).

Ah, yeah, you’d have to replace that too, and that’s a huge pain because afaict it’s defined by the compiler rather than any extension.

1 Like

I think I may be missing something here: what does this I6 hack do that this standard I7 doesn’t?

rb is a number based rulebook.

rb a number (called N):
choose row N in the table of spam;
say code entry;

Mmmm… nothing. Something like it would have value if you were working with rows found by lookups by anything other than row number, to avoid repeated linear searches of the table, and I’m afraid I tunnel-visioned on that case.

Do you mean by passing the current row from one rule to another? Like with:

To decide which number is the current row id: (- (ct_1) -).

Follow the rb rules for the current row id.

Ha, easy-peasy lemon-squeezy. We can have a [column] entry equivalent phrase and forget about choosing a row altogether.

To decide what K is the (tc - value of kind K valued table column) column entry for/of (row-num - a number) in/from (tbl - table name):
(- TableLookUpEntry({tbl}, {tc}, {row-num}) -).

Table of Spam
code    num
"Z" 1
"Y" 5
"A" 25

To decide which K is the (name of kind of value K) in question: (- parameter_object -).

when play begins:
  repeat with i running from 1 to the number of filled rows in the table of spam begin;
    follow the rb rules for i;
  end repeat;

rb is a number based rulebook.

rb: say the code column entry for (the number in question) in the table of spam;
say the num column entry for (the number in question) in the table of spam;

rb: say line break;

when play begins:
  repeat with i running from 1 to the number of filled rows in the table of spam begin;
    follow the rb rules for i;
  end repeat;
2 Likes

The only thing you can’t do with this is set the values. But you can accomplish that with a new “change the ___ entry for ___ in ___ to ___” phrase.

Still not sure what extra lifting the ‘x in question’ is doing compared to …(called x) …? Perhaps because I still haven’t grasped the exact problem all this is intended to solve- if the row number is known and can therefore be passed to the rb rulebook as a number?

Not much… it lets you not say (called x) over and over in each rule. One can do that if one prefers…

Since parameter_object is a global, you could also hardcode it into the entry-selection phrase to make life easier on the end user.

Easy enough… (changed the syntax to include row)

To decide what K is (tc - value of kind K valued table column) column for/of row (row-num - a number) in/from (tbl - table name):
(- TableLookUpEntry({tbl}, {tc}, {row-num}) -).

To set (tc - value of kind K valued table column) column for/of row (row-num - a number) in/from (tbl - table name) to (v - K):
(- TableLookUpEntry({tbl}, {tc}, {row-num}, 1, {v}) -).