INFORM VARYING STRINGS
Sean Barrett

(This document is formatted for monospaced fonts, 80 characters display)

CONTENTS:
  Contents
  General description
  Usage constraints
  Functional description and syntax
  Limitations
  Sample uses
  Don't do this

GENERAL DESCRIPTION

  "Varying strings" provide a mechanism by which printed strings
  can be made to vary with no programming effort on the part of
  the author.  A given string can be chosen in one of several
  ways: at random, with constrained randomness, in sequential
  order repeating the last, or in sequential order wrapping
  around to the beginning.  The technique can be applied to a
  substring of a printed string, rather than the entire string;
  more than one substring of a given string can contain one of
  these effects, and such substrings can actually be nested.

USAGE CONSTRAINTS

  Due to issues with the way the interpreter works and the scope
  of changes that would be necessary, varying strings can only be
  used in strings that are immediately printed, that is, strings
  of the following two kinds:

      [;
         print "^He doesn't seem to cotton to you.^";
      ];
  and
      [;
         "You can't go that way.";
      ];

  A string which is stored somewhere and later printed cannot be
  a varying string; for example

      ...
      description
         "This is an object whose description can't use varying strings.",
      ...

  versus

      ...
      description
      [;
         "This is an object whose description can use varying strings.";
      ],
      ...

  Note that this is the same as the general limitation on putting
  non-strings inside print statements, e.g. print "foo ", x, " bar";
  is legal, but { y = "foo ", x, " bar"; } is not--the sequence is
  printable, but it is not a string.  Varying strings are not really
  strings; they are simply complex print statements, but they LOOK
  (syntactically) like strings.  (This print-only limitation is their
  only real deviation from normal strings--beyond their "varyingness".)

FUNCTIONAL DESCRIPTION AND SYNTAX

 INDICATING VARYING SUBSTRINGS

   A varying substring is delimited (marked off) by wrapping it
   within the characters '{' and '}'.  (I am open to discussion
   of a better choice of characters and for an escaping mechanism
   to allow games to still display the chosen characters.)  This
   simply indicates to the Inform compiler which parts of the
   string undergo varying-string processing.

   For example:

       [; "This {varying string} contains two {delimited} substrings."; ]
       [; "{The entire string here is a single varying substring.}"; ]

   The following are malformed varying strings, and will produce syntax errors:

       [; "an invalid }string} thing"; ]
       [; "{another invalid string"; ]

 INDICATING ALTERNATIVES

   Each varying substring consists of one or more "alternative" strings
   which might be printed.  These are delimited from each other using
   the character '|'.  '|' appears *between* the alternatives, and
   '{' and '}' appear *around* the alternatives.

       [; "{One|Two|Three|Four}"; ]
       [; "This substring has {two|2} varying {substrings|thingies}."; ]

   It is legal to have an "empty" alternative (or more than one):

       [; "{One|Two||Four}"; ]                     ! four alternatives
       [; "You hear {loud |strange |}noises."; ]   ! three alternatives
       [; "{||||}"; ]                              ! five alternatives

 PRINTING BEHAVIOR

   When printed, a varying substring displays exactly one of its
   alternatives.  A varying substring with only one alternative
   will always print that substring, and will behave no differently
   than a non-varying-string--that is, it will print exactly as if
   the '{' and '}' characters were not present.  (See, for example,
   the first two examples in "indicating varying substrings".)

   There are four possible selection methods which determine which
   alternative is printed.  The mechanism for specifying a method
   is discussed in the next section.

     RANDOM                     (example, N = 3:  23233313122)
       one of the N alternatives is chosen at random

     CONSTRAINED RANDOM         (example, N = 3:  21312321323)
       one of the N alternatives is chosen at random, except that it
       will never print the same alternative twice in a row; that is,
       it actually chooses from the (N-1) alternatives discounting the
       one chosen the last time this substring was printed.

     SEQUENCE                   (example, N = 3:  12333333333)
       each of the N alternatives is printed successively; first the
       first, then the second, then the third.  On print attempts
       after the Nth, the Nth is printed.

     CYCLIC SEQUENCE            (example, N = 3:  12312312312)
       each of the N alternatives is printed successively; the first,
       then the second, then the third.  On the N+1'th print of the
       substring, the first alternative is printed again; on the N+2'th,
       the second alternative, etc.
           
   Note that while the pure "RANDOM" approach can be straightforwardly
   implemented in regular Inform, the other three approaches are
   clumsy, as they require additional state--e.g. a variable or attribute.
   The point of varying strings is to make these other techniques trivial
   to code, so that authors will feel free to make use of them all the
   time.  (There is a price, of course--memory, including some dynamic
   memory.)

 SELECTION METHOD SPECIFICATION

   The choice of which selection method (random, constrained random,
   sequence, or cyclic sequence) to use for selecting alternatives is
   determined by a special 'selection code' character which appears
   at the beginning of the varying substring, immediately after the '{'
   character.

          METHOD       CHARACTER     EXAMPLE
     sequence            [none]    "This is the {first|second|last} variant.";
     random                %       "The coin comes up {%heads|tails}.";
     constrained random    !       "I didn't say ~{!foo|bar|baz}~ last time.";
     cyclic sequence       &       "I've counted an {&odd|even} number.";

   (Note to C programmers: the set of characters used were chosen to
    map some familiar C idioms as mnemonic devices: '|' for 'or', of
    course, but also:  (rand() % n) for random selection, ((x+1)&7)
    for wrapping power-of-two sequences, and '!' for 'not' as in "not
    the same as last time".)

 SIMPLE SAMPLES

     sequence             "This is the {first|second|last} variant.";
        This is the first variant.
        This is the second variant.
        This is the last variant.
        This is the last variant.
        This is the last variant.
        This is the last variant.

     cyclic sequence      "I've counted an {&odd|even} number.";
        I've counted an odd number.
        I've counted an even number.
        I've counted an odd number.
        I've counted an even number.
        I've counted an odd number.
        I've counted an even number.

     random               "The coin comes up {%heads|tails}.";
        The coin comes up tails.
        The coin comes up heads.
        The coin comes up heads.
        The coin comes up tails.
        The coin comes up tails.
        The coin comes up tails.
        The coin comes up heads.

     constrained random   "I didn't say ~{!foo|bar|baz}~ last time.";
        I didn't say "bar" last time.
        I didn't say "foo" last time.
        I didn't say "bar" last time.
        I didn't say "baz" last time.
        I didn't say "foo" last time.
        I didn't say "bar" last time.

 NESTING

   Any alternative of a substring may itself contain varying substrings.
   If and only if that alternative is printed are its varying substrings
   processed.  Thus, if an alternative is not selected, then its substrings
   do not update their sequential sequence etc.

   A silly nesting example:
       "{&one|{%two|2}|three|four|five|{!six|6|half-dozen}}"

   This means:
       Cycle through sequentially the six substrings
               "one"
               "{%two|2}"
               "three"
               "four"
               "five"
               "{!six|6|half-dozen}"
       Each time the second string is selected, it picks randomly
       between the substrings "two" or "2".  Each time the sixth
       substring is selected, it picks randomly amongst the three
       strings "six", "6", and "half-dozen", but never picks the
       same one of those three as the previous time it printed this
       substring (which was six iterations before).

   Nested substrings can become hard to read due to the high density
   of symbols, and unlike traditional programming languages, it is
   not possible to introduce extra whitespace to make it easier to
   read, since whitespace in strings is meaningful.  You don't have
   to make use of nested strings, however, and they become easier
   to parse visually as you get used to them.

   Note that because substrings are only evaluated if they're selected,
   you can't rely on the sorts of counting behaviors demonstrated in
   the previous set of examples.  Consider

       "I've counted {%an {&odd|even} number of
        times|{once|twice|a bunch of times}}.";

   Suppose the random selection between the two main substrings
   is:  "121121221"; then this will display:

       I've counted an odd number of times.
       I've counted once.
       I've counted an even number of times.
       I've counted an odd number of times.
       I've counted twice.
       I've counted an even number of times.
       I've counted a bunch of times.
       I've counted a bunch of times.
       I've counted an odd number of times.

LIMITATIONS

 SERIES PRINTING

   The following is a valid Inform varying string usage, which contains
   two varying strings and an embedded function call.

     [;
         "I don't {%like|care for} ", (the) compiler_extension,
                                       " {%very much|a lot|}.";
     ];

   Given the appropriate object, this might print:

     I don't like varying strings very much.
     I don't care for varying strings very much.
     I don't care for varying strings a lot.
     I don't care for varying strings very much.
     I don't like varying strings very much.

   On the other hand, this is not a valid Inform varying string use:

     [;
         "{%I hate ", (the) compiler_extension, "very much.|
            I hate what you've done.}";
     ];

   Unfortunately, this is invalid, because varying strings require that
   each string, that is each block of text contained in double quotes,
   be a valid varying string.  The first string contains a '{' without
   a matching '}'.  I would like to remove this limitation, but it would
   require a more thorough understanding of the Inform compiler than
   I possess, and would probably be a lot more work.  The existing
   implementation lies just at the boundary between being a real
   implementation and being a hack--the scope of the implementation is
   a hack, but the implementation is reasonably clean.

 HIDDEN STATE

   The "state" of the alternative selection is hidden away from your
   code; the whole point of varying strings is to do all the state
   handling for you.  As a result, though, there is no way for any
   other code to depend on the state of what was printed.  Varying
   strings must be purely decorative.

   "The coin comes up {%heads|tails}." is an appropriate varying string
   for flipping a coin in a game in which flipping a coin has no effect.
   But if it should have any effect, you will want to explicitly compute
   a random number to flip the coin.  This is true even if the effect is
   just to change another purely decorative print, for example, if you
   want to be able to examine the coin and see which way it came up last
   time it was flipped; varying strings provide no way to link together
   their results to address this.

   This severe limitation is mandatory; without this limitation,
   varying strings would just be yet another programming language, with
   all the complexity and inconvenience therein.  You'd have to provide
   names for the states; you'd have to distinguish declarations from
   usages to avoid dumb bugs; you'd want more syntax to allow comparisons
   and ability to remap from one range to another range, etc.  If you
   want to do this sort of thing, that's what the Inform language proper
   is there for.

 Memory Usage

   Varying strings probably consume a lot more memory than their
   brethren dumb strings; however, a varying string only consumes
   more memory if it actually varies, that is, if it contains
   a '{' in it.  I don't know Inform or the z-machine well enough
   to know exactly how much more it uses, although if someone wanted
   to figure it out, there is no doubt a straightforward formula.
   In general, if you're concerned about space, don't use varying
   strings.

   It's possible that somebody more knowledgeable could rewrite
   varying strings to be more memory efficient, and that moreover
   such an implementation would be as memory efficient as manually
   coding the effect in Inform source would be.  I imagine that my
   current implementation is no more than 2x the memory *overhead*
   of the maximally efficient approach, and for strings of any
   reasonable size is probably relatively small regardless.

 Performance Overhead

   Selection from the n variants uses a chain of 'if's, which has
   similar performance to Inform's switch() statement.  A faster
   implementation would use array lookups, but then it wouldn't be
   possible to support nested varying strings.

SAMPLE APPLICATIONS

  Some subtle variations:

    An initial sequence that never repeats, followed by a repeating sequence:

      "{Ready|Get set|Go|{&One|Two|Three}}";

    An initial sequence that never repeats, followed by a random choice:

      "{Ready|Get set|Go|{!Foo|Bar|Baz}}";

  Varying strings were implemented because the author wanted it to
  be easy to give NPCs more varied responses.  The author wanted
  two particular selection methods: sequential and random; while
  implementing varying strings, the other two were reasonably easy
  to add anyway.

  Avoiding repetitive conversations:

     "{Q says, ~That 'watch' is an exquisitely compact timed
          explosive, 007. Turn the minute hand to the number
          of seconds you want, the hour hand to the number of
          minutes, shake it, push the alarm button, and it's
          activated. Push the button twice rapidly to deactivate
          it. Once it's activated, changing the time won't
          affect the actual delay 'til it goes off.~|Q says,
          ~Do pay attention, 007.  The minute hand is seconds,
          the hour hand is minutes; shake the watch and push the
          button to arm it; push the button twice to disarm
          it.~|{|An exasperated }Q explains {|yet }again
          how the watch is a timed explosive, that minutes and
          hours on the watch represent seconds and minutes until
          it goes off, that shaking it and pushing the button
          activates it, and that pushing the button twice in
          a row deactivates it.}";

  Random atmospherics (but a rather memory-wasteful approach):

     each_turn
     [;
        print "{!^A bird flies by high overhead, heading south.^|^A
                    dog barks somewhere in the subdivision to the
                    west.^|||||||||||||}";
     ];

DON'T DO THIS

  A word of advice: there are some situations in which it's best
  to stick with a single, consistent, unvarying response.  For example,
  if the player drops an object, your game should print "Dropped."
  or "Ok." or some specific message of that sort.  It should NOT randomly
  pick between three or four different variations, because this might
  mislead the player into thinking that perhaps something different
  is going on in each of the cases.

  In other words, consistent responses are an important meta-cue
  to the player about the degree of simulation vs. emulation in
  the system.  (Examples of simulation in typical IF: light/dark,
  objects inside objects, travelling from place to place, "have
  you been here before"-ness.)

  For an extreme example of what not to do, IMHO, look at the
  commercial RPG "Betrayal at Krondor".  At key moments of
  simulation--when the player's party is about to be attacked,
  or after a victory when the player loots corpses--a long bit
  of narrative text is spewed out that implies the scenario,
  using several descriptive sentences.

  While there is aesthetic value to these bits of narrative (at
  least the first or second time you read them), they create an
  unfortunate situation for the poor player.  Each time such an
  event occurs, the player reads a few words of text, and then
  thinks, "Oh, it's another encounter".  Moreover, for quite some
  number of repetitions the player will read (or at least skim)
  all the way through the text, uncertain about whether perhaps
  the text varies from previous occasions.

THE END
