I7: Defining vs Determining Values

I’ve been having some trouble with creating compilable code that includes variables that are preset for some kinds of things but determined by formula for other kinds of things. The matter is a bit complex and I think an example rather than an attempt at explanation will best demonstrate what I’m talking about.

Let’s say we have a game world in which every possible thing will have a numeric value called weight, and that this value may change over time for some objects under some circumstances. We wish to stipulate the weight of some (probably) static things when defining them, while writing rules that will derive or adjust the weight of other things according to current conditions. Setting aside all other particulars of the following example not directly related to this question (such as the various superior ways to determine the gross and net weights of an item, or whether the whole scenario is unnecessarily complex and contrived) let’s say we wrote:

[code]A person has a number called vitality. The vitality of a person is usually 5.

A thing has a number called weight. The weight of a thing is usually 1. The weight of a person is usually 150.

The description of a thing is usually “Fascinating. [The noun] [unless the noun is the player]weighs[otherwise]weigh[end if] [weight of the noun] pounds.”.

The Batting Cage is a room. The description of the Batting Cage is “Surrounded by a thick canvas backdrop, the team has gathered here for batting practice.”.

Persuasion rule: persuasion succeeds.

A man called Jeff is in the Batting Cage. The player is Jeff. The vitality of Jeff is 6.
A man called Sam is in the Batting Cage. The vitality of Sam is 8.
A woman called Carol is in the Batting Cage.

A bowling ball is in the Batting Cage. The weight of the bowling ball is 14.
A baseball bat is in the Batting Cage. The weight of the baseball bat is 2.

A ring is in the Batting Cage. The description of the ring is “A practice weight that fits onto a baseball bat. It is generally believed that swinging a bat with the weight attached will increase the power of a batter’s swing. This ring weighs 2 pounds.”. The weight of the ring is 2.

Instead of an actor putting something on the baseball bat:
unless the noun is the ring:
say “Let’s stay focused here.”;
otherwise:
if the second noun incorporates the ring:
say “The ring is already on the bat.”;
otherwise:
now the ring is part of the baseball bat;
increase the weight of the baseball bat by 2;
say “[if the actor is the player]You place[otherwise][The actor] places[end if] the ring on the bat.”.

Instead of taking the ring:
if the noun is incorporated by the baseball bat:
move the noun to the actor;
decrease the weight of the baseball bat by 2;
say “[if the actor is the player]You remove[otherwise][The actor] removes[end if] the practice ring from the bat.”;
otherwise:
continue the action.

The block swinging rule is not listed in the check swinging rulebook.

Check an actor swinging:
unless the noun is the baseball bat:
say “Let’s stay focused here.” instead.

After an actor swinging the baseball bat:
if the noun incorporates the ring:
increase the vitality of the actor by 1;
if the vitality of the actor is greater than 10:
change the vitality of the actor to 10;
if the actor is the player:
say “You feel your muscles bulging after your workout.”;
continue the action;
otherwise:
say “[The actor]'s muscles are bulging after [if the actor is male]his[otherwise]her[end if] workout.”;
continue the action;
otherwise:
continue the action.

Report an actor swinging something:
if the actor is the player:
say “You swing the bat as the others study your technique.”;
otherwise:
say “[The actor] swings the bat as the rest of you study [if the actor is male]his[otherwise]her[end if] technique.”.[/code]
Wonderful. The code compiles and everything works as well as we might expect for such a simple scenario. However, we included the statement “A person has a number called vitality” and oddly referred to this number during the process of swinging the bat. Let’s say we later decide to put this obscure value to use and specify that “vitality” will determine a person’s bodyweight, but we want the weight of other things to remain their predefined value. Therefore we modify the code in the following way:

[code][Delete:]
The weight of a person is usually 150.

[Add:]
To decide what number is weight of (abc - a person):
let wvalue be the vitality of abc;
change wvalue to wvalue * 30;
decide on wvalue.[/code]
Inform doesn’t like this change, and tells us:

(with a similar message for “decrease the weight of the baseball bat by 2”).

Fine. We take a more direct approach, changing the two Instead rules for modifying the bat with the ring as follows:

[code][Delete:]
increase the weight of the baseball bat by 2;

[from the Instead of an actor putting something on the baseball bat rule, and replace it with:]

now the weight of the baseball bat is 4;

[Delete:]
decrease the weight of the baseball bat by 2;

[from the Instead of taking the ring rule, and replace it with:]

now the weight of the baseball bat is 2;[/code]
Inform doesn’t care for this either, and tells us:

:cry: Apparently then we will not be allowed to directly modify the weight of things that have a predefined weight if the weight of other things in the game-world is derived from a formula rather than directly defined.

I originally included the statement “The weight of a thing is usually 1” as a catch-all for things whose weight I didn’t care to define. If I eliminate this statement and replace it with a more complex decision process, such as:

To decide what number is the weight of (abc - a thing): unless the weight of abc is 0: decide on the weight of abc; otherwise: decide on 1.
Nothing changes and I get the same compiler error as last time. However this latest addition is problematic in itself, since if I comment out the two “now the weight of the baseball bat is” lines and then examine the bowling ball (for example), the test game rightly crashes with the message “Fatal Error: Stack Overflow” since I attempted to define the weight of something by referring to its weight.

It’s not helpful to delete the latest rule addition and replace it with a multitude of statements such as:

[code]To decide what number is the weight of (abc - a thing):
decide on 1.

To decide what number is the weight of the bowling ball:
decide on 14.[/code]
etc, since I really do want to change the weight of the baseball bat under some conditions and it would seem that no valid “decide” rule could properly address the weight of the baseball bat.

In theory one could deal with the general problem by giving everything two weights, a hidden weight and a usable weight. One could then write rules such as:

[code]The hiddenweight of the bowling ball is 14.

To decide what number is the weight of (abc - a thing):
unless the hiddenweight of abc is 0:
decide on the hiddenweight of abc;
otherwise:
decide on 1.

To decide what number is weight of (abc - a person):
let wvalue be the vitality of abc;
change wvalue to wvalue * 30;
decide on wvalue.[/code]
But a) this is surely an inconvenient and highly contrived method, and b) in fact a solution to the general problem is needed for some rather complex code that runs to tens of thousands of words, and modifying hundreds of lines of code that deal with weight to add a highly contrived “hidden weight” value is a proposition that arouses little enthusiasm.

If anyone has experience writing I7 code in which some general values of things:

a) are determined by formula in some cases but directly defined in other cases, and

b) must be altered under certain conditions

I would be very interested in hearing your insights into this issue. Thanks in advance for any responses.

What you’re requesting is the ability to define a phrase that has the same syntax as a property reference (“the ___ of ___”) and have Inform figure out which one you mean at any given moment. But this is troublesome.

What happens if you have a variable X pointing to some object, and then you write “say the weight of X”? It looks like you’re referring to the weight property, but what if X is actually pointing to a person, whose weight is calculated by a phrase? Inform would have to check this at runtime and generate very different code for the two situations.

And then what happens if you write “change the weight of X to 10”? If X is a person, is this a legal but pointless request to change the weight property (whose value will never be used), or is this illegal because a person’s weight is calculated on the fly and can’t be changed directly? The former is a subtle bug waiting to happen; the latter means Inform would have to do another runtime check and throw an error in one of the two situations. From a programming standpoint, that error is undesirable, since there’s no straightforward way to detect it before it happens. (It’s easy to check whether X provides the weight property, but not whether X is an object whose weight is defined by a phrase, because you could have any number of phrases for different kinds or even individual objects.)

From a programmer’s standpoint, my suggestion is to pick an Inform feature to use for weight and stick with it for all objects: either a property or a phrase.

Using a property would cut down on the amount of code you have to change: just add an “every turn” rule that recalculates the weight of every person. If a person’s weight is rarely used and hard to calculate, a phrase would be more efficient, but that means changing more code.

From a philosopher’s standpoint, my suggestion is to rethink the concepts you’re using here. Weight (well, mass) is an inherent attribute of an object, not a derived calculation. It can be changed, say by slicing off a chunk of the object, but it stays the same until you explicitly change it. It should be a property. A derived calculation would be something like the weight of an object plus its contents – that’s a great time to use a phrase, since that quantity can’t be changed directly. And it’s a different concept from the object’s weight, so you can give it a different name, which solves the conflict. I know you said not to fixate on the details of this example, but perhaps the same point is true of whatever you’re really trying to do.

Thanks for the response. I was a little concerned that I wasn’t able to define the general issue coherently, but even had I failed to do so you have very accurately outlined the various aspects of the problem. From the error messages the compiler itself gave, it wasn’t discernible to me whether I was conceptually trying to do something not allowed or if I was just failing to find the proper syntax for doing something. I was hoping there would be some tricks to get around the problem, but what you’ve said is just as valuable since if I know nothing can be done I won’t spend any more time trying to ask Inform to do something it isn’t able to do.

My thinking was that I would be cautious and never write anything that, in terms of the example above, would change the weight of a person. I would be content for the compiled game to crash if I did in fact do so, since the error would be mine. However, I can see the reasonableness of Inform not allowing such a general approach for the reasons you mentioned.

If we set aside the example code I gave above, this idea bears relevance for what I was trying to do in several cases. One part of the unusual problems I often run into is that I’m trying to make a generic ruleset rather than any particular game with unique situations that can be directly addressed, and am trying to minimize massive every turn repeat-though rules. I have some complex liquid rules, and have an activity that is carried out every turn to recalculate the weight of every liquid based on its current volume. This particular code is important to me and can’t be done any other way, so anything that interferes with it must be set aside. I originally encountered the general issue described above when I later tried to implement some derived values for the weight of living things. For a long time I was just setting the weight of people (along with everything else except liquids) directly, e.g. “Joan weighs 125,” except in the case of things whose weight is based on their kind (in which case the weight is specified in the class definition). However, I also have given every person a physical body with a detailed implementation of body parts. I ran into the problem, then, when I attempted to say that the net weight of a person is the net weight of the person’s body–so that, for example, if a dinosaur takes a bite of George and George loses a leg, George would then weigh about 12 percent less than he did before. Even that itself isn’t an too much of an issue, though; the problem came when I further decided that since every person’s body already had a height and a general body shape (fat, thin, muscular, etc) I wanted to set the net weight of a person’s various body parts by a formula based on their individual height and general body shape. That didn’t sit well with Inform, for the reasons discussed in your post. It wasn’t a big deal to me, and since I couldn’t figure out the underlying problem I continued to specify an exact weight for each person rather than use a formula. By making an assumption that most players won’t be so obsessed with either trivia or vicious bloodlust that they’ll be upset enough by minor discrepancies to abandon the game, I can accept (since I am too lazy to assign a weight to every individual part of every individual body, even though no technical issues prevent me from doing so) that some players might be mildly disappointed to seek out a scale and discover that the disembodied leg of George, who weighs 185 pounds, weighs the same as the disembodied leg of Ned, who weighs 155 pounds. I recently ran into another angle on the problem, though, when I decided that some small items treated as weighing 0 pounds individually should begin to have an effect on the gross weight of containers (including people) and supporters when these small objects are aggregated in large quantities. Again I turned to a formula to resolve the issue, and was again frustrated, at which point it seemed best to try to resolve the matter definitively.

Thanks again for your response. Although it may not be what I hoped to hear, your answer is something I can indeed understand and accept.

I decided to solve the problem discussed above by utilizing the 2-values per characteristic (e.g. hiddenweight/weight) method I mentioned for a number of different characteristics present in my code.

Although most authors designing an Inform game probably wouldn’t have much interest in what follows beyond mere curiousity, for the benefit of anyone more generally interested in crpg-esque systems or detailed character generation I’ll record here a method I devised for determining by formula the weight of an average person. I did a modest amount of research into finding some sort of standardized way to determine a rule-based way to decide the weight of a human character and couldn’t turn up The Answer, so based on snippets of info here and there as well as some value judgements based on my own sensibilities I came up with the following. Since we’ll be weighing characters, we’ll assume we’ve already implemented some sort of weight system with various appropriate rules; the code below assumes a weight system based on units of one ounce. We also give each character a height (in inches), an age (in years), and a general body-type (with six basic styles from which to choose).

Generally speaking, the following guidelines are used in this height/weight system:

The base value for a man’s bodyweight is set at 106 pounds (1696 ounces) plus 7 pounds (112 ounces) for every inch of height over 5 feet (60 inches). For the first 60 inches of height, a man weighs 28.27 ounces per inch of height, which I’ve fudged to 29 ounces per inch.

The base value for a woman’s bodyweight is set at 105 pounds (1680 ounces) plus 5 pounds (80 ounces) for every inch of height over 5 feet (60 inches). For the first 60 inches of height, a woman weighs 28 ounces per inch of height.

The above two ideas were based on some research at various websites peddling various products, so I won’t provide any cites; nonetheless the basic idea seemed generally sound to me, although I played around with the exact poundages a bit before deciding on the 7lbs/inch for men and 5lbs/inch number for women.

I’ve factored age into the equation as follows, after experimenting with different values (i.e. the numbers here are somewhat more arbitrary than the rest of the formulas):
–for a person under the age of 25: the average weight is decreased by approximately one-half of one percent for each year of age under the age of 25;
–for a person between the ages of 26 and 60: the average weight is increased by approximately one-half of one percent for each year of age over the age of 25;
–for a person over the age of 60, the weight is increased by the amount of weight the individual would have presumably gained between the ages of 26 and 60, only to then be decreased by approximately one percent for each year of age over the age of 60.

Last but by no means least is an attempt to account for the frame/build/body-shape of the indivudual. There are 6 possible body-shapes:
–if the individual is frail/extremely thin, the weight is reduced by 25 percent;
–if the individual is thin/very lean, the weight is reduced by 15 percent;
–if the individual is an average healthy person with an average build, no adjustment is made;
–if the individual is unusually well-muscled but lean (i.e. your average superhero/athlete/etc), the weight is increased by 15 percent;
–if the individual has a somewhat soft/overweight build, weight is increased by 25 percent;
–if the individual is unusually obese, weight is increased by 35 percent.

If a particular character did not fit these guidelines in some way, we could just write a special rule to decide that character’s weight in any way desired.

The following example translates the above into Inform 7-ese:

[code][to be safe, one should set the project to compile to glulx if the weight of any item is expected to approach 2000 pounds]

The testarea is a room.

[height is measured in inches]

A person has a number called height. The height of a person is usually 66. The height of a man is usually 68. The height of a woman is usually 64.

[age is measured in years]

A person has a number called age. The age of a person is usually 25.

[reflects the frame/build of the person]

Bodyshape is a kind of value. The bodyshapes are frail, lean, normal, muscular, flabby, and obese. A person has a bodyshape. A person is usually normal.

[weight is measured in ounces; 16 ounces=1 pound]

A thing has a number called hiddenweight. The hiddenweight of a thing is usually 1.

A thing has a number called weight.

To decide what number is the weight of (stuff - a thing):
decide on hiddenweight of stuff.

[a final branch, ‘if avgjoe/avgjane is afflicted,’ is part of the test game and has nothing to do with the general calculation]

To decide what number is the weight of (avgjoe - a man):
let bwvalue1 be height of avgjoe - 60;
if bwvalue1 is greater than 0:
change bwvalue1 to bwvalue1 * 112;
increase bwvalue1 by 1696;
if bwvalue1 is less than 1:
change bwvalue1 to 0;
repeat with htupd running from 1 to height of avgjoe:
increase bwvalue1 by 29;
let bwvalue2 be bwvalue1 / 200;
if the age of avgjoe is less than 25:
decrease bwvalue1 by (bwvalue2 * 24);
repeat with ageupd running from 1 to age of avgjoe:
increase bwvalue1 by bwvalue2;
if the age of avgjoe is greater than 25 and the age of avgjoe is less than 61:
repeat with ageupd running from 26 to age of avgjoe:
increase bwvalue1 by bwvalue2;
if the age of avgjoe is greater than 60:
increase bwvalue1 by (bwvalue2 * 35);
repeat with ageupd running from 61 to age of avgjoe:
decrease bwvalue1 by (bwvalue2 * 2);
change bwvalue2 to bwvalue1 / 100;
if avgjoe is frail:
decrease bwvalue1 by (bwvalue2 * 25);
if avgjoe is lean:
decrease bwvalue1 by (bwvalue2 * 15);
if avgjoe is muscular:
increase bwvalue1 by (bwvalue2 * 15);
if avgjoe is flabby:
increase bwvalue1 by (bwvalue2 * 25);
if avgjoe is obese:
increase bwvalue1 by (bwvalue2 * 35);
if avgjoe is afflicted:
change the hiddenweight of avgjoe to (bwvalue1 / 3);
decide on hiddenweight of avgjoe;
decide on bwvalue1.

To decide what number is the weight of (avgjane - a woman):
let bwvalue1 be height of avgjane - 60;
if bwvalue1 is greater than 0:
change bwvalue1 to bwvalue1 * 80;
increase bwvalue1 by 1680;
if bwvalue1 is less than 1:
change bwvalue1 to 0;
repeat with htupd running from 1 to height of avgjane:
increase bwvalue1 by 28;
let bwvalue2 be bwvalue1 / 200;
if the age of avgjane is less than 25:
decrease bwvalue1 by (bwvalue2 * 24);
repeat with ageupd running from 1 to age of avgjane:
increase bwvalue1 by bwvalue2;
if the age of avgjane is greater than 25 and the age of avgjane is less than 61:
repeat with ageupd running from 26 to age of avgjane:
increase bwvalue1 by bwvalue2;
if the age of avgjane is greater than 60:
increase bwvalue1 by (bwvalue2 * 35);
repeat with ageupd running from 61 to age of avgjane:
decrease bwvalue1 by (bwvalue2 * 2);
change bwvalue2 to bwvalue1 / 100;
if avgjane is frail:
decrease bwvalue1 by (bwvalue2 * 25);
if avgjane is lean:
decrease bwvalue1 by (bwvalue2 * 15);
if avgjane is muscular:
increase bwvalue1 by (bwvalue2 * 15);
if avgjane is flabby:
increase bwvalue1 by (bwvalue2 * 25);
if avgjane is obese:
increase bwvalue1 by (bwvalue2 * 35);
if avgjane is afflicted:
change the hiddenweight of avgjane to (bwvalue1 / 3);
decide on hiddenweight of avgjane;
decide on bwvalue1.

[note this doesn’t calculate gross weight, i.e. weight plus weight of any carried items, but presumably our general weight system would be more refined]

To say scaleweight for (weigher - supporter):
let weighed-items be 0;
if anything is on weigher:
repeat with weighee running through things on weigher:
increase weighed-items by weight of weighee;
change weighed-items to weighed-items divided by 16;
say “[weighed-items]”.

To say (elev - a number) in elevated terms:
if elev is less than 1:
say “a fraction of an inch”;
otherwise if elev is greater than 0 and elev is less than 12:
say “[elev] inches”;
otherwise if elev is greater than 11:
let subelev be the remainder after dividing elev by 12;
let baseelev be elev divided by 12;
say “[baseelev][’][subelev]’”.

A scale is an enterable supporter in the testarea. The description of the scale is “A common bathroom scale. The LED display reads ‘[scaleweight for the item described] lbs’ in large red digits.”.

Persuasion rule: persuasion succeeds.

Cyrus is a man in the testarea. The age of Cyrus is 42. Cyrus is obese. The height of Cyrus is 75. The player is Cyrus. The description of Cyrus is “At [height of cyrus in elevated terms], you’re a mountain of a man.”.

Joe is a man in the testarea. The description of Joe is “Joe is average in every way. He’s [height of joe in elevated terms].”.
Jane is a woman in the testarea. The description of Jane is “Jane is average in every way. She’s [height of jane in elevated terms].”.

Tommy is a man in the testarea. The age of Tommy is 16. The height of Tommy is 67. Tommy is muscular. The description of Tommy is “It’s Tommy, Jane’s teenaged-son. He’s on the varsity football squad, and stands around [height of tommy in elevated terms].”.
Sally is a woman in the testarea. The age of Sally is 77. Sally is frail. The height of Sally is 62. The description of Sally is “It’s Sally, Joe’s grandmother. Joe suspects she’s ill as her [height of sally in elevated terms] frame is skin-and-bones, but she refuses to see a doctor.”.
Brenda is a woman in the testarea. The age of Brenda is 47. The height of Brenda is 65. Brenda is flabby. The description of Brenda is “It’s Brenda, Jane’s mother. She’s [height of brenda in elevated terms].”.

Baby Dan is a man in the testarea. The age of Baby Dan is 1. The height of Baby Dan is 17. The description of Baby Dan is “It’s Joe’s infant son, Dan. He’s only a year old and can already walk around on his own–amazing! Tiny Dan is but [height of dan in elevated terms] tall.”.

A bowling ball is in the testarea. The description of the bowling ball is “A shiny bowling ball in a brilliant shade of emerald green.”. The hiddenweight of the bowling ball is 240. [15 pounds]

A person can be afflicted. A person is usually not afflicted.

An odd berry is in the testarea. The odd berry is edible. The description of the odd berry is “Hmmm. This small purple berry gives you a bad vibe.”.

Before asking Baby Dan to try eating the odd berry:
say “That’s just wrong.” instead.

After an actor eating the odd berry:
if the actor is the player:
say “You eat the berry, and…[no line break]oh no! You’ve been afflicted with a strange wasting disease!”;
now the player is afflicted;
otherwise:
say “[The actor] eats the berry, and suddenly shrivels up to a mere shadow of [if the actor is male]him[otherwise]her[end if]self. [The actor] has been afflicted with a strange wasting disease!”;
now the actor is afflicted.

Test me with “get on scale / x scale / get off scale / get ball / put ball on scale / x scale / get ball / joe, get on scale / x scale / joe, get off scale / jane, get on scale / x scale / jane, get off scale / tommy, get on scale / x scale / tommy, get off scale / sally, get on scale / x scale / sally, get off scale / brenda, get on scale / x scale / brenda, get off scale / dan, get on scale / x scale / dan, get off scale / eat berry / get on scale / x scale”.[/code]
Certainly the assessment of the relationship between weight, height, age, and body-shape accomplished by the above code won’t be to everyone’s liking as it’s based in part on my own sense of things (and even given that I’ll likely continue fiddling with the numbers a bit), but perhaps it will be useful to anyone trying to come up with a way to codify such a value in any type of fairly detailed crpg. If anyone finds the above helpful, they should feel free to use or modify it in any way they like.

Interesting code, Endosphere. I look forward to seeing your system / game. :slight_smile:

One important thing to note however, is that calculated values and variables are separate constructs and should not be confused. For example, this line: A thing has a number called weight. … is both unnecessary and potentially dangerous. Here you are creating a property of things which is a number. Later, with your “to decide” phrases, you create a calculated value with the same name. Generally, you won’t notice this conflict, since the calculated value will take precedence in most cases. However, look at this transcript after running the “test me” script in the IDE (edited for clarity):

According to the debugging command, your weight is zero. This is because that routine is looking at the weight property you created with the above line of code (which defaults at zero and hasn’t been changed), not the calculated value you have named “weight of (avgjoe - a man).” Although it doesn’t affect your test code, the line should be deleted since there is a potential for conflict and giving everything in the world a property you’re not actually using is a waste of memory.

EDIT: Also, I just noticed that your phrase for the scale needs a tweak. Notice that the player works out to 102 lbs (his starting weight / 3) with no accounting for the bowling ball he’s carrying. That’s because of this line: repeat with weighee running through things on weigher: Remember that “on” means directly on. If you want to include indirect containment, do this: repeat with weighee running through things enclosed by weigher: … which adds in the 15 lbs of the bowling ball.

Thanks for the response, Skinny Mike. The example code demonstrating the weight formula in action is a bit muddled; it should probably have been posted a separate thread, if at all (my only reason for posting it was that I’ve been researching quite a bit and haven’t been able to find anything that does the job). Since all my weight-code-writing is in my mind closely related to the issue discussed several weeks ago in this post I tacked it on here, and then made a half-hearted effort to tie the example to the previous discussion; this was probably to the detriment of both the body-weight formula and the defining/determining variables discussion.

Although you could just mean there’s no apparent reason to state weight as a value in the example given (which is true, although there are several important reasons in my general code from which I adapted the example in a somewhat muddled way), I’m understanding you here as making a clarification of vaporware’s earlier point,

by providing the example:

Actually this expansion of values was my very deliberate intention. In my rambling response to vaporware, my main point was intended to be that I understand I want to do something Inform doesn’t allow, namely to

and even though

I’m undeterred and want to do it anyway. Thus due to a lack of options I’ve decided on the hack noted near the end of my first post that results, as you rightly noted, in “a potentially dangerous…potential for conflict” in the code. I’m comfortable, however, with the idea that in order to get what I want I’ll need to code cautiously to avoid any potential problems.

Thanks as well for pointing out a better way for the scale to work in the example; it was sort of silly for me to make a note rather than just change two words in the rule to make the example work better in terms of its own small game world.