10. An Animation Program

We are now in a position to write a LIFE animation program, lifeanimate, using
character-based rather than GUI facilities.
The structure of the program is similar to
that of the file conversion program in section 7. It
will use the abstract data
type Lifestate defined in section 9.

MODULE lifeanimate;
IMPORT lifestate,lifeio;

The algebraic data type Command defines the possible interactive
commands: move the viewing window to the centre of gravity of the
picture (GoToCentre), erase the picture (Erase), print a
help message (Help), quit the program (Quit), run the
animation for a number of steps (Run), centre the viewing window
on a specified point (GoTo), invert the state of a specified cell
(ChgPnt), load a pattern from a file (Load), store a
pattern to a file (Store) or write an Encapsulated PostScript
picture of a pattern (PostScript). The dummy command
Illegal is used to represent illegal user responses, and contains
a string which represents an error message.

The following macros define the size of the viewing window (38 x 17), and the
strings used to print live and dead cells on a character-based terminal.
Figure 4 shows an example interaction with the program,
including the printout of viewing windows.

MACRO
W -> 38;
H -> 17;
Star -> "* ";
Blank -> " ";

CLEAN allows the user to provide rewrite rules on the constructors of an
algebraic data type. We use this facility to rewrite terms of the
Command type to Illegal in the case of errors. This is a
useful general technique: it simplifies the interactive input/output by
distributing some of the error-checking.

Two useful functions for input handling are SkipLine, which skips
to the end of the input line, and SkipToSpace, which skips to the
next white space, returning a boolean flag if end-of-line was reached:

The Readcommand function reads a command from an input file.
Care must be taken to ensure single-threading. For example,
access in a guard to a
variable such as ok which is the result of a read operation
forces that read operation to occur, in which case the value of the file
before the read operation becomes inaccessible. The unique type
mechanism ensures single-threading, but it is inadvisable to rely on
this mechanism too heavily. Distributing some of the error-checking, as
discussed above, assists in producing single-threaded code.

Up to this point, the first input character ch has been read,
which makes f' the current file value. Examining the variable
eol forces a SkipToSpace operation, making f'' the
current file variable. From this, the file variable h is
obtained by reading a filename s':

For the default rule, the current file value must be taken as g'
since one or two numbers may already have been read as a result of
examining ok or ok' in failed guards. The local
definitions for Readcommand are:

The GetFileName function strips away white space from around a filename in
an input string. The function GetFileNameAux is called when the
beginning of the filename is found: it searches for the end of the
filename and extracts it from the string:

The Statusline function gives a list of strings to be printed as part of
the description of the current Life pattern. It shows the coordinates and
properties of the pattern and the viewing window. The ability to manipulate
strings in this way simplifies input/output.

The ShowRect function prints the viewing window, by using a recursive loop
through relative coordinates (x,y) ranging from (0,0) to (w-1,h-1). The
loop is started at (-1,0), with the negative coordinate
used to signal the start of a line. When
a coordinate (w,i) is reached, this signals the end of a line, and the scan
continues with (-1,i+1). When the coordinates of a point in the given list are
reached, a star is printed at that position. An unexpected failure in the
ordering of the points list (which has been sorted at this point) causes
abortion, using Concat to assemble an error message string.

The Loop operation performs the major user interaction. It uses
Readcommand to read a user command, and calls LoopAux to perform
the appropriate action, by pattern-matching. The parameters required are the
current Lifestate s, the standard input-output file f, and the file
system fs. The result is a modified file system, so the LoopAux
function must take responsibility for closing the standard input/output file.

The LoopAux function pattern-matches on the various possible commands. It
handles the Quit command by closing the standard
input/output and terminating. The Help and Illegal commands result
in the output of the messages defined above:

The Run command prints a message if the existing pattern is already stable
or dead, otherwise it executes the first step and calls DoRun. This function
loops until the pattern becomes stable, or the desired number of steps is
reached. It always prints the current viewing window before performing
subsequent steps: