Precise meaning of "has been" and "had been" in combination with number of turns/times?

I’m trying to better understand the precise meaning of the phrases used in creating conditions on past actions in 6M62. I’m working with a simple scenario:

"Past Tense Test"

Place is a room.

A container called a jar is in Place.

A coin is a kind of thing. The player carries five coins.

Every turn:
    if a coin is in the jar, say "coin is.";
    if a coin is in the jar one time, say "coin is one time.";
    if a coin is in the jar two times, say "coin is two times.";
    if a coin is in the jar three times, say "coin is three times.";
    if a coin is in the jar for one turn, say "coin is one turn.";
    if a coin is in the jar for two turns, say "coin is two turns.";
    if a coin is in the jar for three turns, say "coin is three turns.";
    if a coin was in the jar, say "coin was.";
    if a coin was in the jar one time, say "coin was one time.";
    if a coin was in the jar two times, say "coin was two times.";
    if a coin was in the jar three times, say "coin was three times.";
    if a coin was in the jar for one turn, say "coin was one turn.";
    if a coin was in the jar for two turns, say "coin was two turns.";
    if a coin was in the jar for three turns, say "coin was three turns.";
    if a coin has been in the jar, say "coin has been.";
    if a coin has been in the jar one time, say "coin has been one time.";
    if a coin has been in the jar two times, say "coin has been two times.";
    if a coin has been in the jar three time, say "coin has been three times.";
    if a coin has been in the jar for one turn, say "coin has been one turn.";
    if a coin has been in the jar for two turns, say "coin has been two turns.";
    if a coin has been in the jar for three turns, say "coin has been three turns.";
    if a coin had been in the jar, say "coin had been.";
    if a coin had been in the jar one time, say "coin had been one time.";
    if a coin had been in the jar two times, say "coin had been two times.";
    if a coin had been in the jar three times, say "coin had been three times.";
    if a coin had been in the jar for one turn, say "coin had been one turn.";
    if a coin had been in the jar for two turns, say "coin had been two turns.";
    if a coin had been in the jar for three turns, say "coin had been three turns.";

Test me with "put coin in jar / z / z / z / take coin / z / put coin in jar / z / z".

The output that I get doesn’t make 100% sense to me:

Test Me Transcript
>[1] put coin in jar
You put the coin into the jar.

coin is.
coin is one time.
coin is one turn.
coin has been.
coin has been one time. [but not has been one turn?]

>[2] z
Time passes.

coin is.
coin is one time.
coin is two turns.
coin was.
coin was one time.
coin was one turn.  [did not evaluate true last turn, but does this turn and...]
coin was two turns. [first true evaluation of was shows two turns?]
coin has been.
coin has been one time.
coin has been one turn.
coin had been.
coin had been one time.
coin had been one turn. [same turn count as has been?]

>[3] z
Time passes.

coin is.
coin is one time.
coin is three turns. ["is for three turns" translates to exact count, but...]
coin was.
coin was one time.
coin was one turn.   [was/has been/had been translate to at least that count?]
coin was two turns.
coin was three turns.
coin has been.
coin has been one time.
coin has been one turn.
coin has been two turns.
coin had been.
coin had been one time.
coin had been one turn.
coin had been two turns.

>[4] z
Time passes.

coin is.
coin is one time.
coin was.
coin was one time.
coin was one turn.
coin was two turns.
coin was three turns.
coin has been.
coin has been one time.
coin has been one turn.
coin has been two turns.
coin has been three turns.
coin had been.
coin had been one time.
coin had been one turn.
coin had been two turns.
coin had been three turns.

>[5] take coin
Taken.

coin was.
coin was one time.
coin was one turn.
coin was two turns.
coin was three turns.
coin has been.
coin has been one time.
coin had been.
coin had been one time.
coin had been one turn.
coin had been two turns.
coin had been three turns.

>[6] z
Time passes.

coin has been.
coin has been one time.
coin had been.
coin had been one time.

>[7] put coin in jar
You put the coin into the jar.

coin is.
coin is two times.
coin is one turn.
coin has been.
coin has been one time.
coin has been two times.
coin had been.
coin had been one time. [not the same N as has been version]

>[8] z
Time passes.

coin is.
coin is two times.
coin is two turns.
coin was.
coin was one time.
coin was two times.
coin was one turn.
coin was two turns.
coin has been.
coin has been one time.
coin has been two times.
coin has been one turn.
coin had been.
coin had been one time.
coin had been two times.
coin had been one turn.

>[9] z
Time passes.

coin is.
coin is two times.
coin is three turns.
coin was.
coin was one time.
coin was two times.
coin was one turn.
coin was two turns.
coin was three turns.
coin has been.
coin has been one time.
coin has been two times.
coin has been one turn.
coin has been two turns.
coin had been.
coin had been one time.
coin had been two times.
coin had been one turn.
coin had been two turns.

My questions:

  1. Why in response #1 does the condition has been in the jar one time evaluate true but has been in the jar for one turn does not?
  2. Why in response #2 do both of the conditions coin was in the jar for one turn and coin was in the jar for two turns evaluate true, but the previous turn neither did?
  3. Why do both the has been and had been versions of for <N> turns conditions with the same value of N evaluate true on the same turn? (See by comparison the for <N> times treatment in response #7.)
  4. Why do present tense conditions using is for for <N> turns conditions translate as exactly equaling N while past tense conditions using was/has been/had been translate as being greater than or equal to N?

I’ve read WWI 9.13 The past and perfect tenses, WWI 9.14 How many times? and WWI 9.15 How many turns?, but that hasn’t resolved these questions. Although WWI 9.13 is careful to note parenthetically that multiple actions in a turn can complicate things, in this example there is only one action per turn.

3 Likes

It’s quantum wossname. The act of observing something changes it.

I have now banged my head against the code enough to know what it’s doing, but I still don’t know why it’s doing some of those things. There’s useful documentation in Chronology. But I had to read the I7 compiler’s I6 output to get a bunch of this.

For each of the historical states you tell I7 to track, it maintains a past chronological record and a present chronological record. What’s stored in them is:

true/false – the state of whatever you’re tracking
times – the number of times this condition has gone false → true
consecutive – the number of consecutive turns this has been true

ChronologyPoint sets the values of the past chronological records from the current values of the present chronological record. It’s run in two places, BeginAction, and the Update Chronological Records rule.

The only place present chronological records’ values change is in TestSinglePastState which, despite the name, is used to ask questions of either the past chronological record or the present chronological record.

What the Update chronological records rule does is to run TestSinglePastState to inquire about the current value of all the states; it also updates the present chronological records. And then the Update chronological records rule calls ChronologyPoint and the past records are synced from the present.

was/had been questions look at the past chronological record to give their answer.
is/has been questions look at the present chronological record.

When you ask a question about the present chronological record, TestSinglePastState notes the existing truth state in a value called old. And then the logic goes:

If the real value *right now* is true:
  if old was false, trips gets incremented
  if and only if TestSinglePastState was invoked by the Update Chronological Records rule:
    increment consecutive
else (i.e., the real value right now is false):
  consecutive is set to 0 (whether or not we were called by Update Chronological Records).

The present chronological record is then updated with these values.

questions simply about the truth state get the truth state. (but is-questions in your code that aren’t asking about n times or n turns get compiled into a direct test, not to a TestSinglePastState call.)
questions about number of times is answered with trips.
questions about number of turns is answered with 1 + consecutive if it’s an is-question or a was-question but with consecutive if it’s a has-been question or a had-been question.

is-questions about number of times or turns is an exact comparison; for questions about the past, it uses >=.

Why in response #1 does the condition has been in the jar one time evaluate true but has been in the jar for one turn does not?

A has-been question looks at the present chronological record. TestSinglePastState has just incremented its trips value from 0 to 1, which is compared to 1 since you asked about one time, so it’s true.

When you ask about has been one turn, you get the present chronological record’s consecutive value, but we haven’t hit the Update chronological records rule yet, so it’s still 0. And-has-been returns the consecutive value itself, not 1 + consecutive.

Why in response #2 do both of the conditions coin was in the jar for one turn and coin was in the jar for two turns evaluate true, but the previous turn neither did?

In the meantime, we went through Update Chronological Records. Everything’s current value was tested and this time consecutive was incremented for things that are true right now. And then ChronologyPoint updated past records from the present. This is the first time this has happened, so it’s the first time was or had been questions could possibly return true about anything. (And this happened again at the start of the new action, but nothing had changed.)

We’re asking was-questions, so we’re looking at the past chronological record, and consecutive is 1. But was-questions about turns return 1 + consecutive, so it’s 2, thus >= 1 and >= 2.

Why do both the has been and had been versions of for turns conditions with the same value of N evaluate true on the same turn? (See by comparison the for times treatment in response #7.)

Because has-been interrogates the present chronological record and had-been interrogates the past chronological record, they will be in sync from the time ChronologyPoint is run until there’s a TestSinglePastState that changes the present chronological record to something different. Since ChronologyPoint is run at the beginning of BeginAction and at the end of the Update Chronological Records rule, that’s a window in which they can differ. So when an action has just changed things, has-been/had-been get different results. But when you waited with z, they got synced again.

Why do present tense conditions using is for for turns conditions translate as exactly equaling N while past tense conditions using was/has been/had been translate as being greater than or equal to N?

Good question.

6 Likes

Thank you, Zed, for this excellent walkthrough for a difficult puzzle!. I, too, spent a lot of time looking at TestSinglePastState() and the contents of Chronology.i6t, but there were some subtleties I was missing.

The details related to question #2 still have me scratching my head – not about your explanation of events but about what the result is supposed to really mean. I seem to intuitively expect had been-style interpretation (i.e. avoiding the +1 to consecutive) to apply here. Perhaps it’s just that Inform has better grammar than I do? (If anyone can explain how to map the WWI guidance to this result, I would love to hear it.) It also seems as though a ... was ... for 1 turn condition can’t have any unique meaning at present, i.e. there can be no situation in which the comparable ... for 2 turns does not also apply.

2 Likes

I don’t see one either. I’ve wondered if this is a logic error and it should be 1 + consecutive for is/has-been and consecutive for was/had-been. (I’m pretty sure it couldn’t be changed without modifying the compiler, though. TestSinglePastState isn’t from the template layer or kits; it’s written directly by the compiler.) I think this would address all the issues in your first 3 questions. It might well introduce other issues, though.

2 Likes

I think the details of question #1 demonstrate the system working as designed, due to the difference in meaning between times and turns. WWI 9.14 How many times? says of has been two times that the “result is true if the condition holds now and has held for only one previous spell in the past.” By extension has been for one time means “result is true if the condition holds now and has held for no previous spell in the past.” As you cite in your walkthrough, the check itself updates the present chronological record in this case. So this is actually perfectly sensible; when I listed it I had apparently lost track of which combinations of parameters for TestSingleState() were triggering which code and was thinking that it should have been a consecutives + 1 result for the has been for one turn condition.

The details of question #3’s operation also seem OK, if less intuitive. On the one hand (as you point out) since the consecutives count can’t change mid-turn, they evaluate to the same number most turns. But response #5 (>TAKE COIN) shows that only had been for <N> turns versions evaluated true that turn, so there is a functional difference in meaning between that and the has been version (caused as you note by the has been check zeroing the present chronological record). I had been intuitively expecting symmetrical behavior at the start and the end of the overlap of two ranges, i.e. for the had been versions in response #2 (>Z [just after placing coin in jar the first time]) to not evaluate true – but that was probably just some confusion leaking in due to thinking about how has been would interact with for <N> times (not turns).

As far as question #2 goes: I’m curious about how the difference between was and had been should be parsed for for <N> turns conditions as Informese; my intuitive sense is that they are largely interchangeable as English, though I recognize that there are differences. Although TestSinglePastState() can’t be modified directly at the template layer due to the fact that it’s generated by the compiler, it’s possible to install a shim routine in 6M62 to erase the difference between was for <N> turns and had been for <N> turns to make #2 work like I had expected intuitively:

Section - Shim for TestSinglePastState
[makes `was for <N> turns` function like `had been for <N> turns]

Include (-

Replace TestSinglePastState OrigTestSinglePastState;

-) before "Test Single Past State" in "Chronology.i6t".

Include (-

[ TestSinglePastState	past_flag pt turn_end wanted;
    if (past_flag == 1 && wanted == 2)
	    return OrigTestSinglePastState(past_flag, pt, turn_end, wanted)-1; ! reverse hardcoded +1 for "was for N turns" conditions
    else
	    return OrigTestSinglePastState(past_flag, pt, turn_end, wanted);
];

-) after "Test Single Past State" in "Chronology.i6t".

I’m not sure how to pull off the equivalent trick in 10.1 due to the differences in code replacement methodology.

2 Likes

Adverb phrases of occurrence touches on your final question about exact comparison vs. >=…

And the following the constants are used to record how to measure the threshold value — “for more than three turns” would be GT_REPM, and so on. The default, NO_REPM, means that nothing is specified by way of comparison — “four times” — and the meaning of that may depend on context; Inform treats a NO_REPM occurrence quite carefully — see Chronology

but then in Chronology, you see that “quite carefully” just means exact comparison. So that’s no closer to answering why, but seems to indicate it was very deliberate.

You modify the compiler. :frowning_face: v10 doesn’t allow Replace in an I6 inclusion:

My low-level reader of source code reported a mistake - “this Inform 6
directive is not supported in kits or ‘(-’ inclusions: ‘Replace
TestSinglePastState OrigTestSinglePastState;’”. Low-level material written
in Inform 6 syntax occurs either in kits or in matter written inside
‘Include (- … -)’ in source text, either in the main source or in an
extension used by it.

plus I just determined that it’ll let you say…

Include (-
[ TestSinglePastState;
print "no-op^";
 ];
-) replacing "TestSinglePastState".

and you’ll end up with two TestSinglePastStates and the I6 compilation will fail.

1 Like