[ Freelance Traveller Home Page | Search Freelance Traveller | Site Index ]

*Freelance Traveller

The Electronic Fan-Supported Traveller® Resource

The Traveller APL Workspace

This article originally appeared in the January 2015 issue.

The author recently “rediscovered” some old programming languages—the one of interest for this article is APL—and decided to refamiliarize himself with them. To that end, he decided to start building a Traveller workspace that could, with the addition of data by the user, be used as a Traveller play aid suite. The completion of the suite will take several articles, but at the end of each article, the workspace will be usable for some subset of the tasks involved in setting up or running Traveller.

Tools and Environment

APL is surprisingly easy to find out there; there are still commercial vendors, and several implementations, both up-to-date (in terms of the standards) and historical, can be found for free in various places. This workspace is being built with the (free) NARS2000 experimental APL interpreter, available at http://www.nars2000.org. The NARS interpreter runs natively in Windows; on Linux or Mac OSX with the WINE translation layer, available from http://www.winehq.org; and on Mac OSX under the commercial products Crossover or Parallels. The APL being used is compliant with the Extended APL standard (ISO 13751), and there will be an article on this workspace (once it’s complete) on adapting it to so-called ‘traditional APLs’ predating the adoption of the ISO standard.

The Code

APL’s general development paradigm bears resemblances to that of Functional Programming. Rather than being a monolithic chunk of code, APL routines (universally called functions) are small code assemblages that have a single purpose, ideally with no references to anything external to the function except the arguments and returned result.

As the meaning of APL symbols is not generally immediately obvious, the author is willing to be contacted by email for further discussion and analysis of the code, at a level beyond what is needed for this article.

Function: D

      ∇Z←COUNT D DM
[1] Z←⊂(COUNT,DM)

This function exists to allow the use of expressions such as 2 D + 3 or 3 D - 5 to specify the set of dice to roll. It does not actually roll the dice; it merely returns the dice structure used throughout the workspace. The left argument (COUNT) indicates how many six-sided dice are to be rolled when this structure is used; the right argument (DM) indicates the amount that should be added to the roll (subtracted, if negative). A two-element vector consisting of first COUNT then DM is assembled and “enclosed” to be treated as a scalar for storage as part of a variable.

Function: ROLL

[2] DM←1↑⊃DICE
[3] Z←+/(0=⎕IO),DM,?COUNT⍴6

ROLL actually rolls the dice; the argument DICE is a structure as returned by the function D. The function “breaks open” the structure into a number of dice (COUNT) and a modifier (DM). The dice are then rolled, and the modifier added. The final value is returned to the caller.

These two functions are sufficient to act as a general Traveller dice roller; one can load the workspace, and type statements like ROLL 2 D - 7 to get a random number (in the range -5 to 5, for this example).



CONSTRAIN takes as its right argument a value (assumed produced by ROLL), and as its left argument a two-element numeric vector representing the limits that the value must be between. If the value is between the limits, that value is returned; if it is outside the limit, the closest limit value is returned: (5,12) CONSTRAIN ROLL 2 D + 3 will return 5 if the 2D+3 roll returns 4-, or 12 if it returns 13+, otherwise it returns the actual value rolled.

Function: MAKERACE

[1] ⍎NAME,' ← 6 ⍴ 2 D 0'

MAKERACE takes a single argument consisting of a character string representing the name of a race (e.g., ‘human’, ‘vargr', 'dushau', 'hani', etc.) and creates (or overwrites) a variable in the workspace by that name whose value is a vector of six dice structures as generated by 2 D 0. Note that it does not return a value; rather, it modifies the workspace environment. This variable can—and should—be modified later to reflect the actual required rolls.

Function: C

      ∇Z←C ATTRIB
[1] Z←ATTRIB-(0=⎕IO)

C is a bit of a cheat—its sole function is to produce subscripts used with either a race-name variable or a game-character variable (e.g., JOE[C 6] or VARGR[C 1]) to ensure that the correct attribute—in Traveller5 style—is selected, regardless of whether the APL environment happens to be set to start index counting at 0 or 1. You can name the attributes for a given race, classic style, later on by entering a “direct (lambda) function”, e.g., INT←{C 4}, and then select the attribute by writing e.g., JOE[INT].

Function: GAMECHAR

[2] TMPCH←(⊃RACE)[;2]++/?(⊃RACE)[;1]⍴6
[3] Z←EHEX[TMPCH+(1=⎕IO)]

GAMECHAR creates a character’s UPP. It takes a variable created by MAKERACE as its single argument, and uses the six dice structures in it to “roll up” a character, returning the UPP in the standard “Extended Hexadecimal” form.

A sample session with this workspace might look something like this:

      URSA[C 1]←3 D + 2
      URSA[C 2]←2 D - 2
      URSA[C 3]←2 D + 2

First, we create a race called URSA. Then, we adjust the way the stats for an URSA are rolled—STR is 3D+2 (range 5-20), DEX is 2D-2 (range 0-10), and END is 2D+2 (range 4-14). The rest are the standard 2D rolls.

Now that we have a definition for URSA, we use that definition to roll up a character, Griz DiJim. GAMECHAR actually rolls the dice, and tells us that Griz is an URSA with stats F5B864.

Future articles from this author in this series will add functions to do things like generate and resolve careers, or roll tasks. You can download this workspace for NARS2000, or from the link on the January 2015 issue’s download page.