A-code texts explained

Contents:

 


General notes on style

  • The use of upper/lower case in the examples is purely my programming convention; except within text definitions, A-code is case insensitive.
  • The use of dots to separate words in A-code entity names is also conventional; any other convention could be used, e.g. using dashes or underscores instead of dots, or (not recommended!) uppercasing the first letter of each word.
  • The use of commas as A-code statement parameter separators is purely optional, for increased readability; A-code counts commas to be white spaces.
  • All examples use the up-to-date A-code notation (A-code 11), which differs in some respects from that in the original version of the language by Dave Platt.

A-code text basics

This part of the document deals with the basics of A-code text declaration and use.


  1. A-code text declarations

    The current version of A-code does not permit "in-line" texts, though version 12 will do. At present, all texts must be declared as separate entities. The basic A-code text declaration has the form

       TEXT <text_name>
          <some_lines_of_text>
    
    Like all major A-code directives, the keyword TEXT must be at the very beginning of a line. The text lines following it must all have at least one leading blank. The declaration is terminated by a line with a non-blank first character (i.e. another major directive or a comment), or by end-of-file.

    While generally line breaks are ignored in the text definition (see the next section for more details), any texts declared using the TEXT directive are deemed to have a trailing end-of-line. A text fragment, without the trailing EOL can be declared using the FRAGMENT variant of a text declaration:

       FRAGMENT <text_name>
          <some_lines_of_text>
    

    Here is a simple, purely artificial example:

       FRAGMENT LINE.START
          This line is sp
       TEXT LINE.END
          lit into two parts.
    
    When displayed by the A-code primitive SAY
       say line.start
       say line.end
    
    The result is a single line "This line is split into two parts." followed by a line break.

    Both TEXT and FRAGMENT directives can be used to declare "anonymous" texts by omitting the text name. This practice goes back to Dave Platt's original version and is now deprecated -- text switches can be used instead. See Appendix C on handling anonymous texts.

    Back to the top of the document


  2. Basics of text definitions

    A-code text definitions (the lines of text following the TEXT or FRAGMENT declaration line), are processed as follows:

    Back to the top of the document


  3. Centered text

    A text definition line beginning with the plus character + is treated as a line to be separately centered on the display. The plus sign is stripped off, and the line is prefixed with a line break (unless it happened to be preceded with one) and a line break is appended to it. The result is displayed centered.

    A-code also understands "block centering". A centered block is a set of successive text definition lines, each of which is prefixed with the equals character =. The equals signs are stripped off, and the whole block is displayed offset to the right in such a way that its longest line appears centered on the display.

    As with individual line centering, the block is prefixed by a line break if one is required (i.e. there wasn't one already), and suffixed with a line-break. Ends of line within the centered block are also honoured, contrary to the more general A-code convention.

    Here is an artificial example showing both kinds of centering:

       TEXT CENTERING.EXAMPLE
          This text shows
         +a centered line
         +and another centered line,
          as well as
         =a whole block
         =of lines, all of which
         =are centered as a single unit.
         Which is sometimes useful.
    
    If displayed (e.g.) in a fixed font on an 78-character wide display by
       say centering.example
    
    this would result in
       --------------------------------------------------------------------------------
       |  This  text shows                                                            |
       |                               a centered line                                |
       |                          and another centered line,                          |
       |  as well as                                                                  |
       |                         a whole block                                        |
       |                         of lines, all of which                               |
       |                         are centered as a single unit.                       |
       |  Which is sometimes useful.                                                  |
    
    


A-code text-morphing features

This part of the document describes the "text-morphing" features of A-code texts.


  1. Text switches defined

    A text switch is effectively an indexed array of strings embedded in a text message. More than one text switch can be embedded in a single text, and on the other hand, a whole text may consist of a single text switch.

    Formally, a text switch is a sequence of arbitrary text strings separated by forward slashes, with the whole sequence being enclosed in square brackets. E.g.

       [None/One/Two/Many]
    
    is an example of a simple text switch of four elements.

    The idea is that when the whole message is displayed to the player, just one element from every given switch is selected for display, according to some rule. The selection is made by using some "qualifier" value as the index of each text switch array encountered in the message. Text switch elements are indexed from zero upwards, so that in the above example the element None has the index value of zero, while the element Many has the index value of three.

    While ends of lines are treated in A-code text definitions as white spaces (just like, for example in HTML), an end of line immediately following a text switch separator character / is simply ignored, which conveniently allows text switches to be spread across several lines. E.g. the above shown switch could have been equivalently written as

       [None/
        One/
        Two/
        Many]
    
    because A-code ignores any line-leading (and line-trailing) spaces in text definitions.

    Back to the top of the document


  2. Simple text switches, explicitly qualified

    This text morphing feature was introduced by Dave Platt to handle messages such as the report of dwarf attacks on the player. Here's the definition of a text called knives.thrown and containing two text switches:

       TEXT KNIVES.THROWN
           [/One/Two/Three/Four/Five/Six/Seven/Many] nasty sharp kni[/fe is/ves are] thrown at you!
    
    This is displayed by an A-code text-handling language primitive SAY:
       say <text_name>, [<explicit_qualifier>]
    
    or in this specific case
       say knives.thrown, thrown
    

    The second parameter ("thrown") is in this case the name of a variable holding the number of knives thrown at the player, though from the point of view of the language syntax, it could be a constant, or any A-code entity possessing a value (e.g. an object or a location). In the absence of an implicit qualifier (to be explained later), this value is used to index any text switches embedded in the text supplied as the 1st parameter, where switch elements are counted from zero. So, if one knife is thrown, the displayed message will read:

       One nasty sharp knife is thrown at you!
    
    But suppose more than one knife is thrown, say 5 of them. The first switch is unproblematic -- its index values go from 0 to 8 -- but the second switch only has elements 0, 1 and 2. This is handled by the primary rule of text switches: use the index value nearest to the qualifier value. So the message becomes:
       Five nasty sharp knives are thrown at you!
    
    While in this example, the explicit qualifier was supplied as a variable, any A-code entity which has a value associated with it (e.g. an object, or a location, or a plain numeric constant) is also acceptable as an explicit qualifier.

    Back to the top of the document


  3. Repeated text switch elements

    In practice, text switch elements can be long, multi-line strings, and sometimes it is necessary to have some of the elements repeated. For example, if a player has a purse which can contain some coins, its contents could be described by the following text:

       TEXT PURSE.CONTAINS
          There [are/is/are] 
          [no/one/two/three/several/several/several/several/several/several/many]
          coin[s//s] in the purse.
    
    Such repetition is clearly wasteful, as well as tiresome, and the maintenance of switches with repeat elements is unnecessarily complex, because any changes to the repeated elements need to be applied to each of them.

    To get around this, A-code interprets a switch element consisting of the single character = to mean a repeat of the immediately preceding element. This in turn may have the same special format, in which case its preceding element is considered. For obvious reasons, the first (zero index) element of a switch may not be a repeat element.

    Using this convention, the above PURSE.CONTAINS text could be defined as

       TEXT PURSE.CONTAINS
          There [are/is/are] [no/one/two/three/several/=/=/=/=/=/many]
          coin[s//s] in the purse.
    
    When qualified by an actual number of coins, this would specify the number as several for 4 to 9 coins and as many for any more than that.

    Back to the top of the document


  4. Word and value holders

    A-code texts can also contain word and value place-holders, which get replaced dynamically at run-time by words or values specified by the explicit qualifier. Place-holders of either kind can occur both outside and inside text switches.

    The dollar sign $ is used as a value place-holder. If an unescaped dollar sign is encountered in a text to be displayed, and if the text is used with an explicit qualifier, the dollar is replaced with the numerical value of the qualifier. For example, the PURSE.CONTAINS text defined in the last section, could be re-defined as

       TEXT PURSE.CONTAINS
          There [are/is/are] [no/$] coin[s//s] in the purse.
    
    In this case, the A-code statement
       say purse.contains, 13
    
    would result in the display of
       There are 13 coins in the purse.
    
    Of course, as before, any A-code value-bearing entity (variable, location, object...) could be used as a qualifier, instead of a constant value.

    A word place-holder is signalled by an unescaped hash sign #, and is conceptually similar, except that a word, rather than a value, indicated by the explicit qualifier is inserted in place of the hash sign. Just what can be used as an explicit qualifier in this case, is a bit more complicated.

    All declared vocabulary terms obviously have one (or more, if there are synonyms) actual word associated with them. At the author's discretion, most objects and possibly some locations will also have an associated vocabulary word or words. In all these cases there will be a "primary" word -- the first one declared in the list of synonyms (if there are any synonyms). It is this primary word that gets inserted in place of a word place-holder.

    All of this is easier to understand on some practical illustrations. Take for example the object chair1 with the associated nouns "chair" and "seat". Should there be a text defined as

       TEXT NO.KILL.THINGS
          The # is not something mortal, so cannot be killed!
    
    then the statement
       say no.kill.things, chair1
    
    would produce
       The chair is not something mortal, so cannot be killed!
    
    What makes this much more useful than may appear at the first glance, is the fact that A-code variables may store either values or pointers (references, to you Algol fans!) to arbitrary A-code entities. Hence if the game code executes somewhere the statement
       lda target, chair1         # Point variable TARGET at object CHAIR1
    
    where "target" is a variable name, then a subsequent statement
       say no.kill.things, target
    
    will produce exactly the same display text as if the object "chair1" or just the simple noun "chair" were used as the explicit qualifier.

    Word place-holders really come into their own because the mandatory A-code variables ARG1 and ARG2 hold respectively the verb and the noun of the player's last command. So assuming that the player said "KILL CHAIR", then

       say no.kill.things, arg2
    
    would once again tell the player that chairs are not for killing.

    However, there is a further subtlety here, when it comes to using player command words pointed at by the ARG1 and ARG2 variables, because in this case what is echoed as a part of the response is not necessarily the primary word associated with the referenced object, but the word actually used by the player (allowing for expansion of abbreviations, typo correction and vocabulary folding -- see a separate document on the full 3D structure of the A-code vocabulary). So if the player typed "KILL STO" (note the abbreviation of STOOL to STO, assumed to be unambiguous in this example), the displayed response would be

       The stool is not something mortal, so cannot be killed!
    

    Word place-holders can also appear both within and without text switches, but we'll cover that somewhat later on, under the heading of switch and holder interplay.

    Back to the top of the document


  5. Simple implicit qualifiers

    Not all texts need explicit qualification. For example, any texts associated with objects or locations (i.e. various forms of object and location descriptions) are deemed to be implicitly qualified by the current state of the object or location -- i.e. by its current internal value.

    Take for example the object BATTERIES defined as

    OBJECT BATTERIES, =BATTERY
       [/Fresh/Worn-out] batteries
      %[There is a pair of brand-new batteries in the goods tray./
       There are fresh batteries here./
       Some worn-out batteries have been discarded nearby.]
      &The two batteries are just the right size and shape for the lamp.
       Both are marked as "[BRAND-NEW/FRESH/WORN-OUT]" in
       chunky [blue/green/red] letters.
    
    which has the usual triplet of the inventory, ordinary and detailed descriptions. Each of these contains one or more text switches, which will get automatically qualified by the state of the object. So for instance, if the batteries are spent, the statement
       describe batteries
    
    will display
       The two batteries are just the right size and shape for the lamp. Both are marked
       as "WORN-OUT" in chunky red letters.
    
    Similarly the INVENTORY command and the general LOOK command will display their appropriate descriptions qualifying the embedded text switches by the object's state.

    It is important to note that implicit qualifiers always override any explicit qualifiers -- a point which will have a great significance in the next section. For now it is sufficient to observe that

       describe batteries, 0
    
    would be completely pointless.

    Back to the top of the document


  6. Dynamic implicit qualifiers

    As already noted elsewhere, A-code texts also carry an internal state (or value), initialised by default (like all A-code values) to zero. Note, however, that to avoid overriding explicit qualifiers in simple text switches, internal text states are only used as implicit qualifiers if a "method" for their manipulation is given as a part of the text definition. Formally, a full text definition looks like this:

       TEXT [<method>] <text_name>
          <line_of_text> 
          [....]
    
    where <method> is one of increment, cycle, random and assigned. All of these have different effects.
    • If the method specified is increment, then every time the text is displayed, its internal state is incremented by one, until it reaches the number of elements of the switch with most elements embedded in the text. Here is an example from Adv770:
      TEXT increment ONCE.IS.ENOUGH
      
          [And thank goodness for that! /]I [/really /*really* /*REALLY* ]don't
          know why you decided to go and get lost in that dark forest. Let's
          say [once/twice/thrice/enough] is enough and not do it again, huh?
      
      This will automatically produce messages of increasing exasperation, as the value of the text tick up after every use, finally sticking at the value of 4.
    • The cycle method also causes the internal text state to increment by one, but the index value used by each individual switch within the text is the state value modulo the switch size, and the state gets reset to zero once it reaches the least common multiple of all switch sizes within that text.

      This is best demonstrated on a purely artificial example:

         TEXT cycle DIGITS
            [1/2] [1/2/3] [1/2/3/4]
      
      If used repeatedly, the state of this text will increment by one from zero to 11 and then return to zero again and repeat the same cycle. In the process it will produce successive displays "1 1 1", "2 2 2", "1 3 3", "2 1 4", "1 2 1", "2 3 2", "1 1 3", "2 2 4", "1 3 1", "2 1 2", "1 2 3", "2 3 4", "1 1 1" etc...

      As you can see above, each of the switches cycles independently of the rest, yet only one text state value is driving the whole process. This is very useful in assembling automatically at run-time a wide variety of responses.

    • The random method does what one would expect. After the text is displayed using its current state value, this value is reset at random to a value within the index range of the largest embedded text switch. The new value chosen is guaranteed to be different from the current one, so for switches with two components, this method acts exactly as the cycle one.

    • The assigned method is really a pseudo-method in that it does not actually do anything, other than enabling the text value as an implicit qualifier. Without it, the internal text state is ignored by switch processing, and an explicit qualifier has to be supplied instead.

    It should be noted that none of the above described methods, nor the "null" non-method (i.e. when no method is specified) preclude the state value to be assigned into a text or numerically manipulated and examined like any ordinary variable. So for example, taking the above defined DIGITS text, the statements

       say digits        # Internal state starts from 0!
       add digits, 10    # It got incremented to 1, so will now be 11
       say digits        # It's now 11 so will be reset back to zero
       say digits
    
    would result in "1 1 1", "2 3 4", "1 1 1"

    Back to the top of the document


  7. Switch and holder interplay

    Now that we have been through the details of implicit qualifiers, the interplay between text switches and place-holders can be stated very simply as two rules:

    1. Only explicit qualifiers are used when substituting for place-holders of either kind.
    2. For processing text switches, implicit qualifiers always override explicit qualifiers.

    This enables responses such as exemplified by the Adv770 text ITS.JUST.A:

    TEXT cycle ITS.JUST.A
        It's just a #. [Nothing very remarkable about it/Not remarkable in
        any way/Nothing special to it/The apt description is "unremarkable"].
    
    The typical use for this text is
       say its.just.a, arg2
    
    which will replace the word place-holder in the text with the noun from the player's last command, while cycling in its successive uses through its embedded text switch.

    It may at first appear strange that implicit qualifiers are ignored in place-holder substitution, while explicit qualifiers are overridden by implicit ones for switch processing. However, this arrangement does exactly what is often wanted, because it makes it much more sensible to have place-holders within text switches. Again, an example from Adv770 is probably a good illustration:

    TEXT cycle NOCOMPRENDE.VERB
        [My ignorance shames me, but I do not know what action might be
        signified by "#"./Alas, my vocabulary is too limited to encompass "#".
        Try some other verb?/Very remiss of me to be sure, but I've never
        learned to "#"./To my shame, I have no idea what you mean by
        "#"./"#"? Sorry, I don't what it means./I am afraid "#" is not a
        verb I've ever learned./Ahem... "#" is not in my dictionary.
        Would you care to re-phrase?/Regrettably, that is not something I
        know how to do.]
    
    If the game fails to make sense of the player's verb, all it has to do now is
       say nocomprende.verb, arg1
    
    thereby producing a wide variety of responses.

    As an aside, in practice I find that the cycle method is much more useful for this purpose than the random one. Contrary to our intuitive expectation, randomness tends to be non-uniform (i.e. "clumpy") and hence requires a large number of options, if obvious repetitions are to be avoided.

    Back to the top of the document


  8. Text tying

    As already noted, A-code variables can be in fact pointers to other A-code entities. The same is true for internal values of A-code texts. A text can be "tied" to another value-bearing entity, thereby removing the need for the game code to explicitly ensure the text value stays in synch with the state of that other entity.

    In effect, tying texts to other entities is also a text "method" in that it activates the text's implicit qualifier (which in this case happens to be the value of the entity to which the text is tied). Because this additional text method is not purely internal to the text, there may be reasons for the tying to be performed dynamically within the game code. Hence tying is performed by means of an executable language statement, rather than in text declaration.

    Once again, an example may be of help. Adv770 has a quartz seal, which can have one of two states. If the player tries to examine the seal when it is not in his inventory, the game tells him the seal is too small and needs to be picked up. The actual wording of this admonition depends on the state of the seal, hence the game initialisation code contains the statement

       tie pick.up.seal, seal
    
    This effectively ties the value of the text PICK.UP.SEAL to the value of the object SEAL. From now on, if the state of the object changes, the message displayed by
       say pick.up.seal
    
    will automatically change to match, because PICK.UP.SEAL contains a two-component text switch.

    Yes, in this particular case one could use SEAL as an explicit qualifier:

       say pick.up.seal, seal
    
    The seal example merely illustrates the technique. It is too simple to show the justification of that technique. The actual motivation for introducing text tying in A-code was provided by my efforts to resolve a highly complex problem of constructing a location description which depended on several independent factors. It is far too complex to be presented as an illustrative example.

    Back to the top of the document


  9. Text nesting

    Sometimes a number of separate messages (e.g. location descriptions) consist of a part which is common to them all, and another part which is specific to a given message (or group of messages). This can present maintenance problems (e.g. fixing a typo in *all* identical parts) and is also wasteful. A-code offers an alternative approach. The common message part can be defined as a separate text fragment (i.e. a text without a trailing end-of-line character), which can be "nested" within individual messages.

    An A-code message text may include the symbolic name of another text enclosed in unescaped braces (i.e. curly brackets): {}. When the message is displayed, this construct gets replaced with the text indicated by the text name within the braces. The nesting mechanism is recursive, so the nested text may have further texts nested within it.

    Here's an example from Adv770, showing the declarations of the first six of the Adv550's ice tunnels. It uses double-level nesting.

    FRAGMENT INTRICATE.TUNNELS
       You are in an intricate network of ice tunnels.
    #
    FRAGMENT ICE.DEAD.END
       {INTRICATE.TUNNELS} The only exit is
    #
    FRAGMENT ICE.TUNNELS
       {INTRICATE.TUNNELS} Exits lead
    #
    PLACE ICE.CAVE.1
       {ICE.TUNNELS} north and west.
    #
    PLACE ICE.CAVE.1A
       {ICE.DEAD.END} south.
    #
    PLACE ICE.CAVE.2
       {ICE.TUNNELS} north, east and west.
    #
    PLACE ICE.CAVE.2A
       {ICE.TUNNELS} north and south.
    #
    PLACE ICE.CAVE.3
       {ICE.TUNNELS} north and east.
    #
    PLACE ICE.CAVE.3A
       {ICE.DEAD.END} south.
    

    Nested texts may have their own implicit qualifiers, of course. If the top level text is used with an explicit qualifier, this is passed on as an explicit qualifier to all nested texts, to any depth.

    As a special case, A-code also permits nesting of the ARG1 and ARG2 variables, which are treated as the words actually typed by the player (possibly expanded from an abbreviated state, and typo-corrected). This is used, for example, as follows:

       TEXT YOU.DO.IT
          You {ARG1} the {ARG2}.
    
    If the player typed (e.g.) "G LAMP", and succeeded in picking up the lamp,
       say you.do.it
    
    would display "You get the lamp."

    Back to the top of the document



Back to the A-code page
Back to the main Mipmip page
Mike Arnautov, Thursday, 02-Feb-2012 17:38:06 MST