INFORM VARYING STRINGS  v1.0   /   Sean Barrett

Document history:
  v0.1 99-11-07
  v0.2 99-12-29
  v1.0 01-09-13

CONTENTS:
  Contents
  Brief Learn-By-Cut-and-Paste Examples
  General description
  Usage constraints
  Functional description and syntax
  Limitations
  Sample uses
  Some real-world examples

BRIEF LEARN-BY-CUT-AND-PASTE EXAMPLES

  If you want to read a detailed explanation, skip this section
  and start reading from "General Description".

  For those who prefer to just type and experiment and don't want
  to read a long detailed document and just want to learn from examples,
  the following fragments of code are somewhat self-explanatory.

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

    "Bob says, ~{!Uh huh|Yeah|Sure|Ok|Cool}.~";  ! doesn't repeat last

    print_ret "{What an ugly statue. You can't believe anything could be so
      ugly.|You take another look, just to be sure. It sure is
      ugly.|Despite what you might have thought, the statue remains
      ugly.}";

    "Bob clicks the one-digit tally counter and reads off the result
     to you. ~{&One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Zero}.~";

    print "^{!Two large cows|Several rubber ducks|Hoardes of
       angry cartoon characters} {!march|stomp|stride|charge}
       {%down {!the hill|the hillside|from the trees|through
       the trees}|up {!from the river|the hill|the street}} and
       then, after {!pausing to confer with each other|engaging
       in a food fight with {!rotten fruit|whipped cream|giant
       hunks of beef}|licking the streetlight posts|heckling
       the mime|staring vacuously at the moon},
       {!disappear|vanish|slip away} {!over the hill|{%through
       the trees|amongst the trees}|into the river}.^";

  See also "REAL-WORLD EXAMPLES" for more examples, and the section
  "SIMPLE SAMPLES" for a demonstration of what exactly some simple
  examples output.

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.  Each string that prints is chosen from several options;
  the choice can be 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 region of a given
  string can independently contain one of these effects, and such
  substrings can actually be nested.

USAGE CONSTRAINTS

  Varying strings can only be used in strings that are immediately
  printed, that is, strings of the following kinds:

      [;
         "You can't go that way.";
      ];
  and
      [;
         print_ret "You can't go that way.";
      ];
  and
      [;
         print "^Bob looks around nervously.^";
      ];

  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. 

FUNCTIONAL DESCRIPTION AND SYNTAX

 INDICATING VARYING SUBSTRINGS

   A varying substring is delimited (marked off) by wrapping it
   within the characters '{' and '}'. 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.}"; ]

   Note that in the second example above, there is still an implicit
   newline after the string, which is not part of the 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 variation 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.)

 VARIATION METHOD SPECIFICATION

   The choice of which variation method (random, constrained random,
   sequence, or cyclic sequence) to use for selecting alternatives is
   determined by a special 'variation 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 fair bit more memory than their
   brethren dumb strings; but only if it actually varies (i.e. contains
   a '{'). For reasonably-sized strings, the extra strings you write
   probably take up more space than the actual overhead of the code
   for handling variations.

 Performance Overhead

   Selection from the n variants uses a chain of 'if's, which has
   similar performance to Inform's switch() statement.

SAMPLE USES

  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.^|||||||||||||}";
     ];

REAL-WORLD EXAMPLES

  The following examples are all taken from my rather silly game
  "Fit For a Queen" which was written for Emily Short's "Walkthrough Comp",
  and unlike the other examples in this document, have actually been
  compiled and tested.

  The description of the "room" you start in:

      description [;
         print "{You are playing a computerized ~text adventure~, or work of
              ~Interative Fiction~, on a computer. You're currently pretty
              stumped by this game, ~A Storm Brought About By A Moth's
              Flapping Wing~.^^You type 'LOOK' yet again.^^|}";
         if (game has general)
            "You're finally unstuck, and ready to move on!";
         "{You bring up the current room's description. It tells you what
           the room looks like, what exits there are, and about the various
           objects (both manipulable and decorative) to be found there|You
           study the room description{| again| again, but {|still }do not
           find anything to help you think of a solution}}.";
      ],

   The result of turning the smooth duck if it's closed:

      give self general;
      "{You attempt to turn the giant duck freshener statue,
       and it rotates smoothly around one corner, revealing
       a steep passage descending down into the earth.|You
       rotate the duck freshener to reveal the secret passage.}";


   This random atmospheric makes sure that one of the options ("drops
   his tray...") will only ever appear once, by providing a nested
   alternative ("stops to study...") if it's chosen again:

      if (random(70) < 20)
         "^Phillipe {!walks lackadaisically to a table.|glares at you
          as he walks past.|{drops his tray, creating a horrible mess.|stops
          to study his fingernails.}|gets in a staring contest with
          one of the robots.|pretends he doesn't notice a robot signalling
          him.}";

THE END
